epicyon/daemon.py

8147 lines
389 KiB
Python
Raw Normal View History

2020-04-02 21:35:06 +00:00
__filename__ = "daemon.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "1.1.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
2020-06-12 12:03:04 +00:00
import sys
2019-06-28 18:55:29 +00:00
import json
2019-07-01 14:30:48 +00:00
import time
2019-09-07 08:57:52 +00:00
import locale
2020-04-15 10:57:04 +00:00
import urllib.parse
2020-01-12 22:23:01 +00:00
from functools import partial
2020-06-20 19:37:44 +00:00
import pyqrcode
2019-07-27 20:30:58 +00:00
# for saving images
from hashlib import sha256
2019-12-04 12:57:12 +00:00
from hashlib import sha1
2019-06-28 18:55:29 +00:00
from session import createSession
2020-04-02 21:35:06 +00:00
from webfinger import parseHandle
2019-06-28 18:55:29 +00:00
from webfinger import webfingerMeta
2019-11-13 10:32:12 +00:00
from webfinger import webfingerNodeInfo
2019-06-28 18:55:29 +00:00
from webfinger import webfingerLookup
from webfinger import webfingerUpdate
2019-11-13 12:45:41 +00:00
from metadata import metaDataInstance
2020-04-02 21:35:06 +00:00
from metadata import metaDataNodeInfo
from pgp import getEmailAddress
from pgp import setEmailAddress
from pgp import getPGPpubKey
from pgp import setPGPpubKey
2019-12-17 14:57:16 +00:00
from xmpp import getXmppAddress
from xmpp import setXmppAddress
2020-02-26 14:35:17 +00:00
from ssb import getSSBAddress
from ssb import setSSBAddress
2020-03-22 14:42:26 +00:00
from tox import getToxAddress
from tox import setToxAddress
2019-12-17 15:25:34 +00:00
from matrix import getMatrixAddress
from matrix import setMatrixAddress
from donate import getDonationUrl
from donate import setDonationUrl
from person import randomizeActorImages
2020-01-19 20:42:03 +00:00
from person import personUpgradeActor
from person import activateAccount
from person import deactivateAccount
2019-08-08 13:38:33 +00:00
from person import registerAccount
2019-06-28 18:55:29 +00:00
from person import personLookup
2019-07-04 16:24:23 +00:00
from person import personBoxJson
2019-07-11 12:29:31 +00:00
from person import createSharedInbox
2019-08-13 09:24:55 +00:00
from person import isSuspended
2019-08-13 11:59:38 +00:00
from person import suspendAccount
from person import unsuspendAccount
2019-08-13 13:58:48 +00:00
from person import removeAccount
from person import canRemovePost
2019-11-06 11:39:41 +00:00
from person import personSnooze
from person import personUnsnooze
2019-12-01 13:45:30 +00:00
from posts import mutePost
2019-12-01 15:03:55 +00:00
from posts import unmutePost
2019-11-25 22:34:26 +00:00
from posts import createQuestionPost
2019-07-27 22:48:34 +00:00
from posts import createPublicPost
2020-02-24 13:32:19 +00:00
from posts import createBlogPost
2019-08-11 11:25:27 +00:00
from posts import createReportPost
2019-07-28 11:08:14 +00:00
from posts import createUnlistedPost
2019-07-27 22:48:34 +00:00
from posts import createFollowersOnlyPost
from posts import createDirectMessagePost
2019-08-02 18:37:23 +00:00
from posts import populateRepliesJson
2019-08-18 09:39:12 +00:00
from posts import addToField
2019-08-20 11:51:29 +00:00
from posts import expireCache
2020-05-22 11:48:13 +00:00
from inbox import clearQueueItems
2019-06-28 21:59:54 +00:00
from inbox import inboxPermittedMessage
2019-07-02 15:07:27 +00:00
from inbox import inboxMessageHasParams
2019-07-04 12:23:53 +00:00
from inbox import runInboxQueue
2019-09-02 21:52:43 +00:00
from inbox import runInboxQueueWatchdog
2019-07-04 14:36:29 +00:00
from inbox import savePostToInboxQueue
2019-08-02 18:04:31 +00:00
from inbox import populateReplies
2019-09-25 10:22:49 +00:00
from inbox import getPersonPubKey
2019-06-29 20:21:37 +00:00
from follow import getFollowingFeed
2019-07-29 16:13:48 +00:00
from follow import sendFollowRequest
2019-07-03 18:24:44 +00:00
from auth import authorize
2019-07-16 16:08:21 +00:00
from auth import createPassword
2019-07-24 22:38:42 +00:00
from auth import createBasicAuthHeader
from auth import authorizeBasic
2019-11-12 10:59:17 +00:00
from auth import storeBasicCredentials
2019-07-04 12:23:53 +00:00
from threads import threadWithTrace
2019-10-16 18:19:18 +00:00
from threads import removeDormantThreads
2020-03-01 21:11:01 +00:00
from media import replaceYouTube
from media import attachMedia
from blocking import addBlock
from blocking import removeBlock
from blocking import addGlobalBlock
from blocking import removeGlobalBlock
2019-08-14 10:32:15 +00:00
from blocking import isBlockedHashtag
2020-04-16 18:49:16 +00:00
from blocking import isBlockedDomain
2020-03-28 10:33:04 +00:00
from blocking import getDomainBlocklist
2019-07-18 13:10:26 +00:00
from config import setConfigParam
2019-08-12 21:20:47 +00:00
from config import getConfigParam
from roles import setRole
from roles import clearModeratorStatus
from blog import htmlBlogPageRSS2
2020-05-23 09:41:50 +00:00
from blog import htmlBlogPageRSS3
2020-02-25 13:35:41 +00:00
from blog import htmlBlogView
from blog import htmlBlogPage
from blog import htmlBlogPost
2020-03-01 11:05:20 +00:00
from blog import htmlEditBlog
from webinterface import getBlogAddress
from webinterface import setBlogAddress
2020-02-23 12:20:54 +00:00
from webinterface import htmlCalendarDeleteConfirm
2019-08-27 12:47:11 +00:00
from webinterface import htmlDeletePost
2019-08-26 16:02:47 +00:00
from webinterface import htmlAbout
from webinterface import htmlRemoveSharedItem
2019-08-25 17:34:09 +00:00
from webinterface import htmlInboxDMs
2019-09-23 20:09:11 +00:00
from webinterface import htmlInboxReplies
2019-09-28 11:29:42 +00:00
from webinterface import htmlInboxMedia
2020-02-24 14:39:25 +00:00
from webinterface import htmlInboxBlogs
2019-08-24 23:00:03 +00:00
from webinterface import htmlUnblockConfirm
2019-08-24 21:10:20 +00:00
from webinterface import htmlPersonOptions
2019-07-20 21:13:36 +00:00
from webinterface import htmlIndividualPost
from webinterface import htmlProfile
from webinterface import htmlInbox
2019-11-17 14:01:49 +00:00
from webinterface import htmlBookmarks
2019-11-02 14:31:39 +00:00
from webinterface import htmlShares
2019-07-20 21:13:36 +00:00
from webinterface import htmlOutbox
2019-08-12 13:22:17 +00:00
from webinterface import htmlModeration
2019-07-20 21:13:36 +00:00
from webinterface import htmlPostReplies
2019-07-24 22:38:42 +00:00
from webinterface import htmlLogin
2019-08-13 09:24:55 +00:00
from webinterface import htmlSuspended
2019-07-24 22:38:42 +00:00
from webinterface import htmlGetLoginCredentials
2019-07-25 21:39:09 +00:00
from webinterface import htmlNewPost
2019-07-29 09:49:46 +00:00
from webinterface import htmlFollowConfirm
2019-10-10 14:43:21 +00:00
from webinterface import htmlCalendar
2019-07-30 22:34:04 +00:00
from webinterface import htmlSearch
2019-08-19 19:02:28 +00:00
from webinterface import htmlSearchEmoji
2019-08-19 20:01:29 +00:00
from webinterface import htmlSearchEmojiTextEntry
2019-07-29 20:36:26 +00:00
from webinterface import htmlUnfollowConfirm
2019-07-30 22:34:04 +00:00
from webinterface import htmlProfileAfterSearch
2019-08-02 09:52:12 +00:00
from webinterface import htmlEditProfile
2019-08-08 13:38:33 +00:00
from webinterface import htmlTermsOfService
2019-08-27 23:01:40 +00:00
from webinterface import htmlSkillsSearch
2020-04-11 12:37:20 +00:00
from webinterface import htmlHistorySearch
2019-08-10 10:54:52 +00:00
from webinterface import htmlHashtagSearch
2019-08-13 17:25:39 +00:00
from webinterface import htmlModerationInfo
2019-08-13 21:32:18 +00:00
from webinterface import htmlSearchSharedItems
2019-08-14 10:32:15 +00:00
from webinterface import htmlHashtagBlocked
2019-07-23 12:33:09 +00:00
from shares import getSharesFeedForPerson
2019-07-28 08:34:49 +00:00
from shares import addShare
2019-08-26 09:31:45 +00:00
from shares import removeShare
2019-10-17 09:58:30 +00:00
from shares import expireShares
2020-06-06 18:16:16 +00:00
from utils import updateLikesCollection
from utils import undoLikesCollectionEntry
2020-04-02 21:35:06 +00:00
from utils import deletePost
2020-02-24 23:14:49 +00:00
from utils import isBlogPost
from utils import removeAvatarFromCache
2019-12-10 21:39:02 +00:00
from utils import locatePost
2019-11-25 13:50:39 +00:00
from utils import getCachedPostFilename
from utils import removePostFromCache
2019-07-29 16:13:48 +00:00
from utils import getNicknameFromActor
from utils import getDomainFromActor
2019-08-21 17:13:08 +00:00
from utils import getStatusNumber
2019-09-25 10:22:49 +00:00
from utils import urlPermitted
2019-10-22 11:55:06 +00:00
from utils import loadJson
from utils import saveJson
2019-07-29 19:14:14 +00:00
from manualapprove import manualDenyFollowRequest
from manualapprove import manualApproveFollowRequest
2019-07-31 16:47:45 +00:00
from announce import createAnnounce
2020-03-01 20:15:07 +00:00
from content import replaceEmojiFromTags
2019-08-09 09:09:21 +00:00
from content import addHtmlTags
2019-11-10 11:37:24 +00:00
from content import extractMediaInFormPOST
from content import saveMediaInFormPOST
from content import extractTextFieldsInPOST
2019-08-02 09:52:12 +00:00
from media import removeMetaData
2019-08-22 15:14:05 +00:00
from cache import storePersonInCache
2019-11-06 23:20:00 +00:00
from cache import getPersonFromCache
2019-09-25 10:22:49 +00:00
from httpsig import verifyPostHeaders
2019-11-23 14:13:25 +00:00
from theme import setTheme
2020-05-26 20:17:16 +00:00
from theme import getTheme
2020-01-12 20:13:44 +00:00
from schedule import runPostSchedule
from schedule import runPostScheduleWatchdog
2020-01-14 10:23:17 +00:00
from schedule import removeScheduledPosts
2020-01-13 10:35:17 +00:00
from outbox import postMessageToOutbox
2020-02-23 13:28:27 +00:00
from happening import removeCalendarEvent
2020-05-21 21:23:34 +00:00
from bookmarks import bookmark
from bookmarks import undoBookmark
2019-06-28 18:55:29 +00:00
import os
2020-04-02 21:35:06 +00:00
2019-06-28 18:55:29 +00:00
2019-06-29 14:35:26 +00:00
# maximum number of posts to list in outbox feed
2020-04-02 21:35:06 +00:00
maxPostsInFeed = 12
2019-06-29 14:35:26 +00:00
2019-09-28 18:06:17 +00:00
# reduced posts for media feed because it can take a while
2020-04-02 21:35:06 +00:00
maxPostsInMediaFeed = 6
2019-09-28 18:06:17 +00:00
2020-02-24 14:39:25 +00:00
# Blogs can be longer, so don't show many per page
2020-04-02 21:35:06 +00:00
maxPostsInBlogsFeed = 4
2020-02-24 14:39:25 +00:00
2020-02-27 20:23:27 +00:00
# Maximum number of entries in returned rss.xml
2020-04-02 21:35:06 +00:00
maxPostsInRSSFeed = 10
2020-02-27 20:23:27 +00:00
2019-06-29 20:21:37 +00:00
# number of follows/followers per page
2020-04-02 21:35:06 +00:00
followsPerPage = 12
2019-06-29 20:21:37 +00:00
2019-07-23 12:33:09 +00:00
# number of item shares per page
2020-04-02 21:35:06 +00:00
sharesPerPage = 12
2019-07-23 12:33:09 +00:00
2020-06-20 19:37:44 +00:00
def saveDomainQrcode(baseDir: str, httpPrefix: str,
domainFull: str, scale=6) -> None:
"""Saves a qrcode image for the domain name
This helps to transfer onion or i2p domains to a mobile device
"""
qrcodeFilename = baseDir + '/accounts/qrcode.png'
url = pyqrcode.create(httpPrefix + '://' + domainFull)
url.png(qrcodeFilename, scale)
2019-11-03 15:27:29 +00:00
def readFollowList(filename: str) -> None:
2019-06-28 18:55:29 +00:00
"""Returns a list of ActivityPub addresses to follow
"""
2020-04-02 21:35:06 +00:00
followlist = []
2019-06-28 18:55:29 +00:00
if not os.path.isfile(filename):
return followlist
2020-04-02 21:35:06 +00:00
followUsers = open(filename, "r")
2019-06-28 18:55:29 +00:00
for u in followUsers:
if u not in followlist:
2020-04-02 21:35:06 +00:00
nickname, domain = parseHandle(u)
2019-07-03 09:40:27 +00:00
if nickname:
2020-04-02 21:35:06 +00:00
followlist.append(nickname + '@' + domain)
2019-06-29 20:21:37 +00:00
followUsers.close()
2019-06-28 18:55:29 +00:00
return followlist
2020-04-02 21:35:06 +00:00
2019-06-28 18:55:29 +00:00
class PubServer(BaseHTTPRequestHandler):
2020-04-02 21:35:06 +00:00
protocol_version = 'HTTP/1.1'
2019-11-25 11:19:03 +00:00
2020-06-19 20:04:22 +00:00
def _pathIsImage(self) -> bool:
if self.path.endswith('.png') or \
self.path.endswith('.jpg') or \
self.path.endswith('.gif') or \
self.path.endswith('.webp'):
return True
return False
2020-06-19 20:08:06 +00:00
def _pathIsVideo(self) -> bool:
if self.path.endswith('.ogv') or \
self.path.endswith('.mp4'):
return True
return False
def _pathIsAudio(self) -> bool:
if self.path.endswith('.ogg') or \
self.path.endswith('.mp3'):
return True
return False
2020-06-08 16:46:01 +00:00
def handle_error(self, request, client_address):
print('ERROR: http server error: ' + str(request) + ', ' +
str(client_address))
pass
2020-05-23 14:23:56 +00:00
def _isMinimal(self, nickname: str) -> bool:
"""Returns true if minimal buttons should be shown
for the given account
"""
2020-05-23 14:26:49 +00:00
accountDir = self.server.baseDir + '/accounts/' + \
2020-05-23 14:23:56 +00:00
nickname + '@' + self.server.domain
if not os.path.isdir(accountDir):
return False
minimalFilename = accountDir + '/minimal'
if os.path.isfile(minimalFilename):
return True
return False
def _setMinimal(self, nickname: str, minimal: bool) -> None:
"""Sets whether an account should display minimal buttons
"""
2020-05-23 14:26:49 +00:00
accountDir = self.server.baseDir + '/accounts/' + \
2020-05-23 14:23:56 +00:00
nickname + '@' + self.server.domain
if not os.path.isdir(accountDir):
return
minimalFilename = accountDir + '/minimal'
minimalFileExists = os.path.isfile(minimalFilename)
if not minimal and minimalFileExists:
os.remove(minimalFilename)
elif minimal and not minimalFileExists:
with open(minimalFilename, 'w') as fp:
fp.write('\n')
2020-04-02 21:35:06 +00:00
def _sendReplyToQuestion(self, nickname: str, messageId: str,
answer: str) -> None:
2019-11-25 13:34:44 +00:00
"""Sends a reply to a question
2019-11-25 12:43:00 +00:00
"""
2020-04-02 21:35:06 +00:00
votesFilename = self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain + '/questions.txt'
2019-11-25 12:43:00 +00:00
2019-11-27 09:58:39 +00:00
if os.path.isfile(votesFilename):
# have we already voted on this?
if messageId in open(votesFilename).read():
2020-04-02 21:35:06 +00:00
print('Already voted on message ' + messageId)
2019-11-27 09:58:39 +00:00
return
2019-11-25 12:43:00 +00:00
2020-04-02 21:35:06 +00:00
print('Voting on message ' + messageId)
print('Vote for: ' + answer)
messageJson = \
createPublicPost(self.server.baseDir,
nickname,
self.server.domain, self.server.port,
self.server.httpPrefix,
answer, False, False, False,
None, None, None, True,
messageId, messageId, None,
False, None, None, None)
2019-11-25 12:43:00 +00:00
if messageJson:
2020-01-30 10:11:08 +00:00
# name field contains the answer
2020-04-02 21:35:06 +00:00
messageJson['object']['name'] = answer
if self._postToOutbox(messageJson, __version__, nickname):
postFilename = \
locatePost(self.server.baseDir, nickname,
self.server.domain, messageId)
2019-12-10 21:08:03 +00:00
if postFilename:
2020-04-02 21:35:06 +00:00
postJsonObject = loadJson(postFilename)
2019-12-10 21:08:03 +00:00
if postJsonObject:
2020-04-02 21:35:06 +00:00
populateReplies(self.server.baseDir,
self.server.httpPrefix,
self.server.domainFull,
postJsonObject,
self.server.maxReplies,
2019-12-10 21:08:03 +00:00
self.server.debug)
# record the vote
2020-04-02 21:35:06 +00:00
votesFile = open(votesFilename, 'a+')
2019-12-10 21:08:03 +00:00
if votesFile:
2020-04-02 21:35:06 +00:00
votesFile.write(messageId + '\n')
2019-12-10 21:08:03 +00:00
votesFile.close()
# ensure that the cached post is removed if it exists,
# so that it then will be recreated
2020-04-02 21:35:06 +00:00
cachedPostFilename = \
getCachedPostFilename(self.server.baseDir,
nickname,
self.server.domain,
2019-12-10 21:08:03 +00:00
postJsonObject)
if cachedPostFilename:
if os.path.isfile(cachedPostFilename):
os.remove(cachedPostFilename)
# remove from memory cache
2020-04-02 21:35:06 +00:00
removePostFromCache(postJsonObject,
2019-12-10 21:08:03 +00:00
self.server.recentPostsCache)
2019-11-25 12:43:00 +00:00
else:
print('ERROR: unable to post vote to outbox')
else:
print('ERROR: unable to create vote')
2020-03-22 21:16:02 +00:00
2020-04-02 21:35:06 +00:00
def _removePostInteractions(self, postJsonObject: {}) -> None:
2019-11-25 11:19:03 +00:00
"""Removes potentially sensitive interactions from a post
This is the type of thing which would be of interest to marketers
or of saleable value to them. eg. Knowing who likes who or what.
"""
if postJsonObject.get('likes'):
2020-04-02 21:35:06 +00:00
postJsonObject['likes'] = {'items': []}
2019-11-25 11:19:03 +00:00
if postJsonObject.get('shares'):
2020-04-02 21:35:06 +00:00
postJsonObject['shares'] = {}
2019-11-25 11:19:03 +00:00
if postJsonObject.get('replies'):
2020-04-02 21:35:06 +00:00
postJsonObject['replies'] = {}
2019-11-25 11:19:03 +00:00
if postJsonObject.get('bookmarks'):
2020-04-02 21:35:06 +00:00
postJsonObject['bookmarks'] = {}
2019-11-25 11:19:03 +00:00
if not postJsonObject.get('object'):
return
if not isinstance(postJsonObject['object'], dict):
return
if postJsonObject['object'].get('likes'):
2020-04-02 21:35:06 +00:00
postJsonObject['object']['likes'] = {'items': []}
2019-11-25 11:19:03 +00:00
if postJsonObject['object'].get('shares'):
2020-04-02 21:35:06 +00:00
postJsonObject['object']['shares'] = {}
2019-11-25 11:19:03 +00:00
if postJsonObject['object'].get('replies'):
2020-04-02 21:35:06 +00:00
postJsonObject['object']['replies'] = {}
2019-11-25 11:19:03 +00:00
if postJsonObject['object'].get('bookmarks'):
2020-04-02 21:35:06 +00:00
postJsonObject['object']['bookmarks'] = {}
2019-11-25 11:19:03 +00:00
2019-08-24 11:23:12 +00:00
def _requestHTTP(self) -> bool:
"""Should a http response be given?
"""
2019-08-31 12:38:57 +00:00
if not self.headers.get('Accept'):
return False
2019-11-26 14:32:09 +00:00
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('ACCEPT: ' + self.headers['Accept'])
2019-08-27 16:27:48 +00:00
if 'image/' in self.headers['Accept']:
2020-01-09 20:31:00 +00:00
if 'text/html' not in self.headers['Accept']:
return False
2019-12-03 21:47:28 +00:00
if 'video/' in self.headers['Accept']:
2020-01-09 20:31:00 +00:00
if 'text/html' not in self.headers['Accept']:
return False
2019-12-03 21:47:28 +00:00
if 'audio/' in self.headers['Accept']:
2020-01-09 20:31:00 +00:00
if 'text/html' not in self.headers['Accept']:
return False
2019-08-27 16:27:48 +00:00
if self.headers['Accept'].startswith('*'):
2019-08-24 11:28:43 +00:00
return False
2019-08-24 11:23:12 +00:00
if 'json' in self.headers['Accept']:
return False
return True
2019-09-25 09:22:10 +00:00
def _fetchAuthenticated(self) -> bool:
"""http authentication of GET requests for json
"""
if not self.server.authenticatedFetch:
return True
2019-09-25 10:22:49 +00:00
# check that the headers are signed
if not self.headers.get('signature'):
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('WARN: authenticated fetch, ' +
'GET has no signature in headers')
2019-09-25 10:22:49 +00:00
return False
# get the keyId
2020-04-02 21:35:06 +00:00
keyId = None
signatureParams = self.headers['signature'].split(',')
2019-09-25 10:22:49 +00:00
for signatureItem in signatureParams:
if signatureItem.startswith('keyId='):
if '"' in signatureItem:
2020-04-02 21:35:06 +00:00
keyId = signatureItem.split('"')[1]
2019-09-25 10:22:49 +00:00
break
if not keyId:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('WARN: authenticated fetch, ' +
'failed to obtain keyId from signature')
2019-09-25 10:22:49 +00:00
return False
# is the keyId (actor) valid?
2020-04-02 21:35:06 +00:00
if not urlPermitted(keyId, self.server.federationList, "inbox:read"):
2019-09-25 10:22:49 +00:00
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('Authorized fetch failed: ' + keyId +
' is not permitted')
2019-09-25 10:22:49 +00:00
return False
# make sure we have a session
if not self.server.session:
if self.server.debug:
print('DEBUG: creating new session during authenticated fetch')
2020-06-09 11:03:59 +00:00
self.server.session = createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to create session 1')
return False
2019-09-25 10:22:49 +00:00
# obtain the public key
2020-04-02 21:35:06 +00:00
pubKey = \
getPersonPubKey(self.server.baseDir, self.server.session, keyId,
self.server.personCache, self.server.debug,
__version__, self.server.httpPrefix,
self.server.domain, self.server.onionDomain)
2019-09-25 10:22:49 +00:00
if not pubKey:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: Authenticated fetch failed to ' +
'obtain public key for ' + keyId)
2019-09-25 10:22:49 +00:00
return False
2020-04-02 21:35:06 +00:00
# it is assumed that there will be no message body on
# authenticated fetches and also consequently no digest
GETrequestBody = ''
GETrequestDigest = None
2019-09-25 10:22:49 +00:00
# verify the GET request without any digest
2020-04-02 21:35:06 +00:00
if verifyPostHeaders(self.server.httpPrefix,
pubKey, self.headers,
self.path, True,
GETrequestDigest,
GETrequestBody,
self.server.debug):
2019-09-25 10:22:49 +00:00
return True
return False
2019-09-25 09:22:10 +00:00
2020-04-02 21:35:06 +00:00
def _login_headers(self, fileFormat: str, length: int,
2020-03-28 17:24:40 +00:00
callingDomain: str) -> None:
2019-06-28 18:55:29 +00:00
self.send_response(200)
self.send_header('Content-type', fileFormat)
2019-08-15 13:20:09 +00:00
self.send_header('Content-Length', str(length))
2020-03-28 17:24:40 +00:00
self.send_header('Host', callingDomain)
2020-04-02 21:35:06 +00:00
self.send_header('WWW-Authenticate',
2019-11-03 15:27:29 +00:00
'title="Login to Epicyon", Basic realm="epicyon"')
2020-04-02 21:35:06 +00:00
self.send_header('X-Robots-Tag', 'noindex')
2019-06-28 18:55:29 +00:00
self.end_headers()
2020-04-02 21:35:06 +00:00
def _logout_headers(self, fileFormat: str, length: int,
2020-03-28 17:24:40 +00:00
callingDomain: str) -> None:
self.send_response(200)
self.send_header('Content-type', fileFormat)
self.send_header('Content-Length', str(length))
self.send_header('Set-Cookie', 'epicyon=; SameSite=Strict')
2020-03-28 17:24:40 +00:00
self.send_header('Host', callingDomain)
2020-04-02 21:35:06 +00:00
self.send_header('WWW-Authenticate',
2019-11-03 15:27:29 +00:00
'title="Login to Epicyon", Basic realm="epicyon"')
2020-04-02 21:35:06 +00:00
self.send_header('X-Robots-Tag', 'noindex')
self.end_headers()
2020-03-22 21:16:02 +00:00
2020-04-02 21:35:06 +00:00
def _set_headers_base(self, fileFormat: str, length: int, cookie: str,
2020-03-28 17:24:40 +00:00
callingDomain: str) -> None:
2019-08-15 12:22:34 +00:00
self.send_response(200)
self.send_header('Content-type', fileFormat)
2020-04-02 21:35:06 +00:00
if length > -1:
2019-12-03 22:11:51 +00:00
self.send_header('Content-Length', str(length))
2019-08-15 12:22:34 +00:00
if cookie:
self.send_header('Cookie', cookie)
2020-03-28 17:24:40 +00:00
self.send_header('Host', callingDomain)
2019-08-15 12:22:34 +00:00
self.send_header('InstanceID', self.server.instanceId)
2020-04-02 21:35:06 +00:00
self.send_header('X-Robots-Tag', 'noindex')
self.send_header('X-Clacks-Overhead', 'GNU Natalie Nguyen')
self.send_header('Accept-Ranges', 'none')
2019-12-04 12:36:13 +00:00
2020-04-02 21:35:06 +00:00
def _set_headers(self, fileFormat: str, length: int, cookie: str,
2020-03-28 17:24:40 +00:00
callingDomain: str) -> None:
2020-04-02 21:35:06 +00:00
self._set_headers_base(fileFormat, length, cookie, callingDomain)
2020-04-13 20:18:45 +00:00
self.send_header('Cache-Control', 'public, max-age=0')
2019-12-04 12:36:13 +00:00
self.end_headers()
2020-04-02 21:35:06 +00:00
def _set_headers_head(self, fileFormat: str, length: int, etag: str,
2020-03-28 17:24:40 +00:00
callingDomain: str) -> None:
2020-04-02 21:35:06 +00:00
self._set_headers_base(fileFormat, length, None, callingDomain)
2019-12-04 13:54:59 +00:00
if etag:
2020-04-02 21:35:06 +00:00
self.send_header('ETag', etag)
2019-12-04 13:54:59 +00:00
self.end_headers()
2020-04-02 21:35:06 +00:00
def _set_headers_etag(self, mediaFilename: str, fileFormat: str,
data, cookie: str, callingDomain: str) -> None:
self._set_headers_base(fileFormat, len(data), cookie, callingDomain)
2020-04-13 19:58:29 +00:00
self.send_header('Cache-Control', 'public, max-age=86400')
2020-04-02 21:35:06 +00:00
etag = None
if os.path.isfile(mediaFilename + '.etag'):
2019-12-04 12:36:13 +00:00
try:
2020-04-02 21:35:06 +00:00
with open(mediaFilename + '.etag', 'r') as etagFile:
etag = etagFile.read()
except BaseException:
2019-12-04 12:36:13 +00:00
pass
if not etag:
2020-04-02 21:35:06 +00:00
etag = sha1(data).hexdigest()
2019-12-04 12:36:13 +00:00
try:
2020-04-02 21:35:06 +00:00
with open(mediaFilename + '.etag', 'w') as etagFile:
2019-12-04 12:36:13 +00:00
etagFile.write(etag)
2020-04-02 21:35:06 +00:00
except BaseException:
2019-12-04 12:36:13 +00:00
pass
if etag:
2020-04-02 21:35:06 +00:00
self.send_header('ETag', etag)
2019-08-15 12:22:34 +00:00
self.end_headers()
2020-04-13 19:44:01 +00:00
def _etag_exists(self, mediaFilename: str) -> bool:
"""Does an etag header exist for the given file?
"""
etagHeader = 'If-None-Match'
if not self.headers.get(etagHeader):
etagHeader = 'if-none-match'
if not self.headers.get(etagHeader):
etagHeader = 'If-none-match'
if self.headers.get(etagHeader):
oldEtag = self.headers['If-None-Match']
if os.path.isfile(mediaFilename + '.etag'):
# load the etag from file
currEtag = ''
try:
with open(mediaFilename, 'r') as etagFile:
currEtag = etagFile.read()
except BaseException:
pass
if oldEtag == currEtag:
# The file has not changed
return True
return False
2020-04-02 21:35:06 +00:00
def _redirect_headers(self, redirect: str, cookie: str,
2020-06-19 11:14:49 +00:00
callingDomain: str) -> None:
2019-11-26 15:30:13 +00:00
if '://' not in redirect:
2020-04-02 21:35:06 +00:00
print('REDIRECT ERROR: redirect is not an absolute url ' +
redirect)
2020-06-19 10:47:25 +00:00
2020-06-19 11:14:49 +00:00
self.send_response(303)
2020-06-19 11:01:42 +00:00
if cookie:
if not cookie.startswith('SET:'):
self.send_header('Cookie', cookie)
else:
2020-06-21 11:44:57 +00:00
setCookieStr = cookie.replace('SET:', '').strip()
if self.server.httpPrefix == 'https':
setCookieStr += '; Secure'
setCookieStr += '; HttpOnly; SameSite=Strict'
self.send_header('Set-Cookie', setCookieStr)
2020-06-19 11:01:42 +00:00
self.send_header('Location', redirect)
self.send_header('Host', callingDomain)
self.send_header('InstanceID', self.server.instanceId)
self.send_header('Content-Length', '0')
self.send_header('X-Robots-Tag', 'noindex')
self.end_headers()
2019-07-28 14:09:48 +00:00
2020-04-02 21:35:06 +00:00
def _httpReturnCode(self, httpCode: int, httpDescription: str) -> None:
msg = "<html><head></head><body><h1>" + str(httpCode) + " " + \
httpDescription + "</h1></body></html>"
msg = msg.encode('utf-8')
2019-11-15 14:03:43 +00:00
self.send_response(httpCode)
2019-06-28 18:55:29 +00:00
self.send_header('Content-Type', 'text/html; charset=utf-8')
2019-08-15 13:20:09 +00:00
self.send_header('Content-Length', str(len(msg)))
2020-04-02 21:35:06 +00:00
self.send_header('X-Robots-Tag', 'noindex')
2019-06-28 18:55:29 +00:00
self.end_headers()
2019-08-16 19:45:48 +00:00
try:
self.wfile.write(msg)
except Exception as e:
2020-04-02 21:35:06 +00:00
print('Error when showing ' + str(httpCode))
2019-08-16 19:45:48 +00:00
print(e)
2019-06-28 18:55:29 +00:00
2020-02-19 15:12:05 +00:00
def _200(self) -> None:
2020-04-02 21:35:06 +00:00
self._httpReturnCode(200, 'Ok')
2020-02-19 15:12:05 +00:00
2019-11-15 14:03:43 +00:00
def _404(self) -> None:
2020-04-02 21:35:06 +00:00
self._httpReturnCode(404, 'Not Found')
2019-11-15 14:03:43 +00:00
2019-12-04 13:05:12 +00:00
def _304(self) -> None:
2020-04-02 21:35:06 +00:00
self._httpReturnCode(304, 'Resource has not changed')
2019-12-04 13:05:12 +00:00
2019-11-15 14:03:43 +00:00
def _400(self) -> None:
2020-04-02 21:35:06 +00:00
self._httpReturnCode(400, 'Bad Request')
2019-11-15 21:43:20 +00:00
def _503(self) -> None:
2020-04-02 21:35:06 +00:00
self._httpReturnCode(503, 'Service Unavailable')
2020-03-22 21:16:02 +00:00
2020-04-02 21:35:06 +00:00
def _write(self, msg) -> None:
tries = 0
while tries < 5:
2019-10-16 13:45:51 +00:00
try:
self.wfile.write(msg)
break
except Exception as e:
print(e)
time.sleep(1)
2020-04-02 21:35:06 +00:00
tries += 1
2019-10-22 12:35:51 +00:00
def _robotsTxt(self) -> bool:
if not self.path.lower().startswith('/robot'):
return False
2020-04-02 21:35:06 +00:00
msg = 'User-agent: *\nDisallow: /'
msg = msg.encode('utf-8')
self._set_headers('text/plain; charset=utf-8', len(msg),
None, self.server.domainFull)
2019-10-22 12:35:51 +00:00
self._write(msg)
return True
2020-06-18 20:56:29 +00:00
def _hasAccept(self, callingDomain: str) -> bool:
if self.headers.get('Accept') or callingDomain.endswith('.b32.i2p'):
2020-06-18 21:20:56 +00:00
if not self.headers.get('Accept'):
self.headers['Accept'] = \
'text/html,application/xhtml+xml,' \
'application/xml;q=0.9,image/webp,*/*;q=0.8'
2020-06-18 20:56:29 +00:00
return True
return False
2020-04-02 21:35:06 +00:00
def _mastoApi(self, callingDomain: str) -> bool:
2019-11-13 11:24:27 +00:00
"""This is a vestigil mastodon API for the purpose
of returning an empty result to sites like
https://mastopeek.app-dist.eu
"""
if not self.path.startswith('/api/v1/'):
return False
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: mastodon api ' + self.path)
if self.path == '/api/v1/instance':
adminNickname = getConfigParam(self.server.baseDir, 'admin')
instanceDescriptionShort = \
getConfigParam(self.server.baseDir,
'instanceDescriptionShort')
instanceDescription = getConfigParam(self.server.baseDir,
'instanceDescription')
instanceTitle = getConfigParam(self.server.baseDir,
'instanceTitle')
instanceJson = \
metaDataInstance(instanceTitle,
instanceDescriptionShort,
instanceDescription,
self.server.httpPrefix,
self.server.baseDir,
adminNickname,
self.server.domain,
self.server.domainFull,
self.server.registration,
self.server.systemLanguage,
2019-11-13 12:45:41 +00:00
self.server.projectVersion)
2020-04-02 21:35:06 +00:00
msg = json.dumps(instanceJson).encode('utf-8')
2020-06-18 20:56:29 +00:00
if self._hasAccept(callingDomain):
2019-11-13 15:41:03 +00:00
if 'application/ld+json' in self.headers['Accept']:
2020-04-02 21:35:06 +00:00
self._set_headers('application/ld+json', len(msg),
None, callingDomain)
2019-11-13 15:41:03 +00:00
else:
2020-04-02 21:35:06 +00:00
self._set_headers('application/json', len(msg),
None, callingDomain)
2019-11-13 14:21:21 +00:00
else:
2020-04-02 21:35:06 +00:00
self._set_headers('application/ld+json', len(msg),
None, callingDomain)
2019-11-13 12:45:41 +00:00
self._write(msg)
2019-11-13 14:32:44 +00:00
print('instance metadata sent')
2020-03-22 21:16:02 +00:00
return True
2019-11-13 11:26:02 +00:00
if self.path.startswith('/api/v1/instance/peers'):
2019-11-13 11:24:27 +00:00
# This is just a dummy result.
# Showing the full list of peers would have privacy implications.
2020-04-02 21:35:06 +00:00
# On a large instance you are somewhat lost in the crowd, but on
# small instances a full list of peers would convey a lot of
# information about the interests of a small number of accounts
msg = json.dumps(['mastodon.social',
self.server.domainFull]).encode('utf-8')
2020-06-18 20:56:29 +00:00
if self._hasAccept(callingDomain):
2019-11-13 15:41:03 +00:00
if 'application/ld+json' in self.headers['Accept']:
2020-04-02 21:35:06 +00:00
self._set_headers('application/ld+json', len(msg),
None, callingDomain)
2019-11-13 15:41:03 +00:00
else:
2020-04-02 21:35:06 +00:00
self._set_headers('application/json', len(msg),
None, callingDomain)
2019-11-13 14:21:21 +00:00
else:
2020-04-02 21:35:06 +00:00
self._set_headers('application/ld+json', len(msg),
None, callingDomain)
2019-11-13 11:24:27 +00:00
self._write(msg)
2019-11-13 14:32:44 +00:00
print('instance peers metadata sent')
2019-11-13 11:24:27 +00:00
return True
2019-11-13 11:26:02 +00:00
if self.path.startswith('/api/v1/instance/activity'):
2019-11-13 11:24:27 +00:00
# This is just a dummy result.
2020-04-02 21:35:06 +00:00
msg = json.dumps([]).encode('utf-8')
2020-06-18 20:56:29 +00:00
if self._hasAccept(callingDomain):
2019-11-13 15:41:03 +00:00
if 'application/ld+json' in self.headers['Accept']:
2020-04-02 21:35:06 +00:00
self._set_headers('application/ld+json', len(msg),
None, callingDomain)
2019-11-13 15:41:03 +00:00
else:
2020-04-02 21:35:06 +00:00
self._set_headers('application/json', len(msg),
None, callingDomain)
2019-11-13 14:21:21 +00:00
else:
2020-04-02 21:35:06 +00:00
self._set_headers('application/ld+json', len(msg),
None, callingDomain)
2019-11-13 11:24:27 +00:00
self._write(msg)
2019-11-13 14:32:44 +00:00
print('instance activity metadata sent')
2019-11-13 11:24:27 +00:00
return True
2020-03-22 21:16:02 +00:00
self._404()
2019-11-15 13:45:21 +00:00
return True
2020-03-22 21:16:02 +00:00
2020-04-02 21:35:06 +00:00
def _nodeinfo(self, callingDomain: str) -> bool:
2019-11-13 10:32:12 +00:00
if not self.path.startswith('/nodeinfo/2.0'):
return False
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: nodeinfo ' + self.path)
info = metaDataNodeInfo(self.server.baseDir,
self.server.registration,
self.server.projectVersion)
2019-11-13 10:32:12 +00:00
if info:
2020-04-02 21:35:06 +00:00
msg = json.dumps(info).encode('utf-8')
2020-06-18 20:56:29 +00:00
if self._hasAccept(callingDomain):
2019-11-13 15:41:03 +00:00
if 'application/ld+json' in self.headers['Accept']:
2020-04-02 21:35:06 +00:00
self._set_headers('application/ld+json', len(msg),
None, callingDomain)
2019-11-13 15:41:03 +00:00
else:
2020-04-02 21:35:06 +00:00
self._set_headers('application/json', len(msg),
None, callingDomain)
2019-11-13 14:21:21 +00:00
else:
2020-04-02 21:35:06 +00:00
self._set_headers('application/ld+json', len(msg),
None, callingDomain)
2019-11-13 10:32:12 +00:00
self._write(msg)
2019-11-13 14:32:44 +00:00
print('nodeinfo sent')
2019-11-15 13:45:21 +00:00
return True
self._404()
2019-11-13 10:32:12 +00:00
return True
2020-03-22 21:16:02 +00:00
2020-04-02 21:35:06 +00:00
def _webfinger(self, callingDomain: str) -> bool:
2019-06-28 18:55:29 +00:00
if not self.path.startswith('/.well-known'):
return False
2019-07-04 14:36:29 +00:00
if self.server.debug:
print('DEBUG: WEBFINGER well-known')
2019-06-28 18:55:29 +00:00
2019-07-03 16:14:45 +00:00
if self.server.debug:
print('DEBUG: WEBFINGER host-meta')
2019-06-28 18:55:29 +00:00
if self.path.startswith('/.well-known/host-meta'):
2020-06-03 19:14:24 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
2020-04-02 21:35:06 +00:00
wfResult = \
2020-06-03 19:14:24 +00:00
webfingerMeta('http', self.server.onionDomain)
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
wfResult = \
2020-06-19 22:50:41 +00:00
webfingerMeta('http', self.server.i2pDomain)
2020-03-27 12:18:11 +00:00
else:
2020-04-02 21:35:06 +00:00
wfResult = \
2020-06-03 19:14:24 +00:00
webfingerMeta(self.server.httpPrefix,
self.server.domainFull)
2019-06-28 18:55:29 +00:00
if wfResult:
2020-04-02 21:35:06 +00:00
msg = wfResult.encode('utf-8')
self._set_headers('application/xrd+xml', len(msg),
None, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-11-15 13:45:21 +00:00
return True
2020-03-22 21:16:02 +00:00
self._404()
2019-11-13 10:32:12 +00:00
return True
if self.path.startswith('/.well-known/nodeinfo'):
2020-06-03 19:14:24 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
2020-04-02 21:35:06 +00:00
wfResult = \
2020-06-03 19:14:24 +00:00
webfingerNodeInfo('http', self.server.onionDomain)
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
wfResult = \
2020-06-19 22:50:41 +00:00
webfingerNodeInfo('http', self.server.i2pDomain)
2020-03-27 12:18:11 +00:00
else:
2020-04-02 21:35:06 +00:00
wfResult = \
2020-06-03 19:14:24 +00:00
webfingerNodeInfo(self.server.httpPrefix,
self.server.domainFull)
2019-11-13 10:32:12 +00:00
if wfResult:
2020-04-02 21:35:06 +00:00
msg = json.dumps(wfResult).encode('utf-8')
2020-06-18 20:56:29 +00:00
if self._hasAccept(callingDomain):
2019-11-13 15:41:03 +00:00
if 'application/ld+json' in self.headers['Accept']:
2020-04-02 21:35:06 +00:00
self._set_headers('application/ld+json', len(msg),
None, callingDomain)
2019-11-13 15:41:03 +00:00
else:
2020-04-02 21:35:06 +00:00
self._set_headers('application/json', len(msg),
None, callingDomain)
2019-11-13 14:21:21 +00:00
else:
2020-04-02 21:35:06 +00:00
self._set_headers('application/ld+json', len(msg),
None, callingDomain)
2019-11-13 10:32:12 +00:00
self._write(msg)
2019-11-15 13:45:21 +00:00
return True
2020-03-22 21:16:02 +00:00
self._404()
2019-11-13 10:32:12 +00:00
return True
2019-06-28 18:55:29 +00:00
2019-07-03 16:14:45 +00:00
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: WEBFINGER lookup ' + self.path + ' ' +
str(self.server.baseDir))
wfResult = \
webfingerLookup(self.path, self.server.baseDir,
self.server.domain, self.server.onionDomain,
self.server.port, self.server.debug)
2019-06-28 18:55:29 +00:00
if wfResult:
2020-04-02 21:35:06 +00:00
msg = json.dumps(wfResult).encode('utf-8')
self._set_headers('application/jrd+json', len(msg),
None, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-06-28 18:55:29 +00:00
else:
2019-07-03 16:14:45 +00:00
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: WEBFINGER lookup 404 ' + self.path)
2019-06-28 18:55:29 +00:00
self._404()
return True
2020-04-02 21:35:06 +00:00
def _permittedDir(self, path: str) -> bool:
"""These are special paths which should not be accessible
directly via GET or POST
"""
2019-06-28 18:55:29 +00:00
if path.startswith('/wfendpoints') or \
path.startswith('/keys') or \
path.startswith('/accounts'):
return False
return True
2020-03-22 21:16:02 +00:00
2020-04-02 21:35:06 +00:00
def _postToOutbox(self, messageJson: {}, version: str,
postToNickname=None) -> bool:
"""post is received by the outbox
Client to server message post
https://www.w3.org/TR/activitypub/#client-to-server-outbox-delivery
"""
2020-01-13 11:01:31 +00:00
if postToNickname:
2020-04-02 21:35:06 +00:00
print('Posting to nickname ' + postToNickname)
self.postToNickname = postToNickname
return postMessageToOutbox(messageJson, self.postToNickname,
self.server, self.server.baseDir,
self.server.httpPrefix,
self.server.domain,
self.server.domainFull,
self.server.onionDomain,
2020-06-03 20:21:44 +00:00
self.server.i2pDomain,
2020-04-02 21:35:06 +00:00
self.server.port,
self.server.recentPostsCache,
self.server.followersThreads,
self.server.federationList,
self.server.sendThreads,
self.server.postLog,
self.server.cachedWebfingers,
self.server.personCache,
self.server.allowDeletion,
2020-06-09 11:03:59 +00:00
self.server.proxyType, version,
2020-04-02 21:35:06 +00:00
self.server.debug)
def _postToOutboxThread(self, messageJson: {}) -> bool:
2019-09-03 17:16:26 +00:00
"""Creates a thread to send a post
"""
2020-04-02 21:35:06 +00:00
accountOutboxThreadName = self.postToNickname
2019-09-03 19:40:44 +00:00
if not accountOutboxThreadName:
2020-04-02 21:35:06 +00:00
accountOutboxThreadName = '*'
2020-03-22 21:16:02 +00:00
2019-09-03 19:40:44 +00:00
if self.server.outboxThread.get(accountOutboxThreadName):
2019-09-03 17:16:26 +00:00
print('Waiting for previous outbox thread to end')
2020-04-02 21:35:06 +00:00
waitCtr = 0
thName = accountOutboxThreadName
while self.server.outboxThread[thName].isAlive() and waitCtr < 8:
2019-09-03 17:16:26 +00:00
time.sleep(1)
2020-04-02 21:35:06 +00:00
waitCtr += 1
if waitCtr >= 8:
2019-09-03 19:40:44 +00:00
self.server.outboxThread[accountOutboxThreadName].kill()
2019-09-03 17:16:26 +00:00
print('Creating outbox thread')
2020-04-02 21:35:06 +00:00
self.server.outboxThread[accountOutboxThreadName] = \
threadWithTrace(target=self._postToOutbox,
args=(messageJson.copy(), __version__),
daemon=True)
2019-09-03 17:16:26 +00:00
print('Starting outbox thread')
2019-09-03 19:40:44 +00:00
self.server.outboxThread[accountOutboxThreadName].start()
2019-09-03 17:16:26 +00:00
return True
2020-04-02 21:35:06 +00:00
def _updateInboxQueue(self, nickname: str, messageJson: {},
2019-11-03 15:27:29 +00:00
messageBytes: str) -> int:
2019-09-03 09:44:50 +00:00
"""Update the inbox queue
"""
2020-04-27 09:41:38 +00:00
if self.server.restartInboxQueueInProgress:
self._503()
print('Message arrrived but currently restarting inbox queue')
self.server.POSTbusy = False
return 2
2020-04-16 18:49:16 +00:00
# check for blocked domains so that they can be rejected early
messageDomain = None
if messageJson.get('actor'):
2020-04-16 18:54:38 +00:00
messageDomain, messagePort = \
getDomainFromActor(messageJson['actor'])
2020-04-16 18:49:16 +00:00
if isBlockedDomain(self.server.baseDir, messageDomain):
2020-04-16 18:51:44 +00:00
print('POST from blocked domain ' + messageDomain)
2020-04-16 18:49:16 +00:00
self._400()
self.server.POSTbusy = False
return 3
2020-04-16 11:48:00 +00:00
# if the inbox queue is full then return a busy code
if len(self.server.inboxQueue) >= self.server.maxQueueLength:
2020-04-16 18:49:16 +00:00
if messageDomain:
2020-04-16 11:57:13 +00:00
print('Queue: Inbox queue is full. Incoming post from ' +
messageJson['actor'])
else:
print('Queue: Inbox queue is full')
2020-04-16 11:48:00 +00:00
self._503()
2020-05-22 11:48:13 +00:00
clearQueueItems(self.server.baseDir, self.server.inboxQueue)
2020-05-02 10:19:24 +00:00
if not self.server.restartInboxQueueInProgress:
self.server.restartInboxQueue = True
2020-05-22 11:48:13 +00:00
self.server.POSTbusy = False
2020-04-16 11:48:00 +00:00
return 2
2019-08-15 22:12:58 +00:00
2019-08-16 08:39:01 +00:00
# Convert the headers needed for signature verification to dict
2020-04-02 21:35:06 +00:00
headersDict = {}
headersDict['host'] = self.headers['host']
headersDict['signature'] = self.headers['signature']
2019-08-15 22:12:58 +00:00
if self.headers.get('Date'):
2020-04-02 21:35:06 +00:00
headersDict['Date'] = self.headers['Date']
2019-08-15 22:12:58 +00:00
if self.headers.get('digest'):
2020-04-02 21:35:06 +00:00
headersDict['digest'] = self.headers['digest']
2019-08-15 22:12:58 +00:00
if self.headers.get('Content-type'):
2020-04-02 21:35:06 +00:00
headersDict['Content-type'] = self.headers['Content-type']
2019-11-12 17:30:31 +00:00
if self.headers.get('Content-Length'):
2020-04-02 21:35:06 +00:00
headersDict['Content-Length'] = self.headers['Content-Length']
2019-11-12 18:32:33 +00:00
elif self.headers.get('content-length'):
2020-04-02 21:35:06 +00:00
headersDict['content-length'] = self.headers['content-length']
2019-08-18 09:39:12 +00:00
2019-11-03 15:27:29 +00:00
# For follow activities add a 'to' field, which is a copy
# of the object field
2020-04-02 21:35:06 +00:00
messageJson, toFieldExists = \
addToField('Follow', messageJson, self.server.debug)
2020-03-22 21:16:02 +00:00
2019-11-03 15:27:29 +00:00
# For like activities add a 'to' field, which is a copy of
# the actor within the object field
2020-04-02 21:35:06 +00:00
messageJson, toFieldExists = \
addToField('Like', messageJson, self.server.debug)
2019-08-18 09:39:12 +00:00
2020-04-02 21:35:06 +00:00
beginSaveTime = time.time()
2019-08-15 22:12:58 +00:00
# save the json for later queue processing
2020-04-02 21:35:06 +00:00
queueFilename = \
savePostToInboxQueue(self.server.baseDir,
self.server.httpPrefix,
nickname,
self.server.domainFull,
2019-07-05 11:27:18 +00:00
messageJson,
messageBytes.decode('utf-8'),
2019-08-15 22:12:58 +00:00
headersDict,
2019-08-16 08:39:01 +00:00
self.path,
2019-07-06 13:49:25 +00:00
self.server.debug)
if queueFilename:
2019-07-15 12:27:26 +00:00
# add json to the queue
2019-07-06 13:49:25 +00:00
if queueFilename not in self.server.inboxQueue:
self.server.inboxQueue.append(queueFilename)
2019-11-15 23:03:37 +00:00
if self.server.debug:
2020-04-02 21:35:06 +00:00
timeDiff = int((time.time() - beginSaveTime) * 1000)
if timeDiff > 200:
print('SLOW: slow save of inbox queue item ' +
queueFilename + ' took ' + str(timeDiff) + ' mS')
2019-07-05 11:27:18 +00:00
self.send_response(201)
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-07-15 12:27:26 +00:00
return 0
2020-04-16 11:48:00 +00:00
self._503()
2020-04-16 10:28:56 +00:00
self.server.POSTbusy = False
2020-04-16 10:27:55 +00:00
return 1
2019-07-05 11:27:18 +00:00
2019-07-12 11:05:43 +00:00
def _isAuthorized(self) -> bool:
if self.path.startswith('/icons/') or \
self.path.startswith('/avatars/') or \
self.path.startswith('/favicon.ico'):
return False
# token based authenticated used by the web interface
2020-05-25 16:21:53 +00:00
if self.headers.get('Cookie'):
2019-11-15 12:56:07 +00:00
if self.headers['Cookie'].startswith('epicyon='):
2020-04-02 21:35:06 +00:00
tokenStr = self.headers['Cookie'].split('=', 1)[1].strip()
2019-11-26 17:30:22 +00:00
if ';' in tokenStr:
2020-04-02 21:35:06 +00:00
tokenStr = tokenStr.split(';')[0].strip()
if self.server.tokensLookup.get(tokenStr):
2020-04-02 21:35:06 +00:00
nickname = self.server.tokensLookup[tokenStr]
2019-10-23 22:27:52 +00:00
# default to the inbox of the person
2020-04-02 21:35:06 +00:00
if self.path == '/':
self.path = '/users/' + nickname + '/inbox'
# check that the path contains the same nickname
# as the cookie otherwise it would be possible
# to be authorized to use an account you don't own
if '/' + nickname + '/' in self.path:
return True
if self.path.endswith('/'+nickname):
return True
2020-04-02 21:35:06 +00:00
print('AUTH: nickname ' + nickname +
' was not found in path ' + self.path)
2019-11-26 17:52:08 +00:00
return False
2019-11-26 18:00:44 +00:00
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('AUTH: epicyon cookie ' +
'authorization failed, header=' +
self.headers['Cookie'].replace('epicyon=', '') +
' tokenStr=' + tokenStr + ' tokens=' +
2020-03-27 14:09:48 +00:00
str(self.server.tokensLookup))
2019-11-26 17:52:08 +00:00
return False
2019-11-26 17:19:03 +00:00
print('AUTH: Header cookie was not authorized')
return False
# basic auth
2019-07-12 11:05:43 +00:00
if self.headers.get('Authorization'):
2020-04-02 21:35:06 +00:00
if authorize(self.server.baseDir, self.path,
self.headers['Authorization'],
2019-07-12 11:05:43 +00:00
self.server.debug):
return True
2020-04-02 21:35:06 +00:00
print('AUTH: Basic auth did not authorize ' +
self.headers['Authorization'])
2019-07-12 11:05:43 +00:00
return False
2020-03-22 21:16:02 +00:00
2020-06-19 09:40:35 +00:00
def _clearLoginDetails(self, nickname: str, callingDomain: str):
"""Clears login details for the given account
"""
# remove any token
if self.server.tokens.get(nickname):
del self.server.tokensLookup[self.server.tokens[nickname]]
del self.server.tokens[nickname]
2020-06-19 09:40:35 +00:00
self._redirect_headers(self.server.httpPrefix + '://' +
self.server.domainFull + '/login',
'epicyon=; SameSite=Strict',
callingDomain)
2020-04-02 21:35:06 +00:00
def _benchmarkGETtimings(self, GETstartTime, GETtimings: [], getID: int):
2019-11-16 13:25:44 +00:00
"""Updates a list containing how long each segment of GET takes
"""
2019-11-15 18:59:15 +00:00
if self.server.debug:
2020-04-02 21:35:06 +00:00
timeDiff = int((time.time() - GETstartTime) * 1000)
logEvent = False
if timeDiff > 100:
logEvent = True
2019-11-16 13:25:44 +00:00
if GETtimings:
2020-04-02 21:35:06 +00:00
timeDiff = int(timeDiff - int(GETtimings[-1]))
2019-11-16 13:25:44 +00:00
GETtimings.append(str(timeDiff))
if logEvent:
2020-04-02 21:35:06 +00:00
ctr = 1
2019-11-16 13:25:44 +00:00
for timeDiff in GETtimings:
2020-04-02 21:35:06 +00:00
print('GET TIMING|' + str(ctr) + '|' + timeDiff)
ctr += 1
2019-11-15 18:59:15 +00:00
2020-04-02 21:35:06 +00:00
def _benchmarkPOSTtimings(self, POSTstartTime, POSTtimings: [],
postID: int):
2019-11-16 11:03:02 +00:00
"""Updates a list containing how long each segment of POST takes
"""
if self.server.debug:
2020-04-02 21:35:06 +00:00
timeDiff = int((time.time() - POSTstartTime) * 1000)
logEvent = False
if timeDiff > 100:
logEvent = True
2019-11-16 11:03:02 +00:00
if POSTtimings:
2020-04-02 21:35:06 +00:00
timeDiff = int(timeDiff - int(POSTtimings[-1]))
2019-11-16 11:03:02 +00:00
POSTtimings.append(str(timeDiff))
if logEvent:
2020-04-02 21:35:06 +00:00
ctr = 1
2019-11-16 11:03:02 +00:00
for timeDiff in POSTtimings:
2020-04-02 21:35:06 +00:00
print('POST TIMING|' + str(ctr) + '|' + timeDiff)
ctr += 1
2019-11-16 11:03:02 +00:00
2020-04-02 21:35:06 +00:00
def _pathContainsBlogLink(self, baseDir: str,
httpPrefix: str, domain: str,
domainFull: str, path: str) -> (str, str):
2020-02-24 18:26:07 +00:00
"""If the path contains a blog entry then return its filename
"""
2020-02-24 18:40:23 +00:00
if '/users/' not in path:
2020-04-02 21:35:06 +00:00
return None, None
userEnding = path.split('/users/', 1)[1]
2020-02-24 18:26:07 +00:00
if '/' not in userEnding:
2020-04-02 21:35:06 +00:00
return None, None
userEnding2 = userEnding.split('/')
nickname = userEnding2[0]
if len(userEnding2) != 2:
return None, None
if len(userEnding2[1]) < 14:
return None, None
userEnding2[1] = userEnding2[1].strip()
2020-02-24 20:10:49 +00:00
if not userEnding2[1].isdigit():
2020-04-02 21:35:06 +00:00
return None, None
2020-02-24 18:26:07 +00:00
# check for blog posts
2020-04-02 21:35:06 +00:00
blogIndexFilename = baseDir + '/accounts/' + \
nickname + '@' + domain + '/tlblogs.index'
2020-02-24 18:26:07 +00:00
if not os.path.isfile(blogIndexFilename):
2020-04-02 21:35:06 +00:00
return None, None
if '#' + userEnding2[1] + '.' not in open(blogIndexFilename).read():
return None, None
messageId = httpPrefix + '://' + domainFull + \
'/users/' + nickname + '/statuses/' + userEnding2[1]
return locatePost(baseDir, nickname, domain, messageId), nickname
2020-02-24 18:26:07 +00:00
2019-09-05 11:44:09 +00:00
def do_GET(self):
2020-04-02 21:35:06 +00:00
callingDomain = self.server.domainFull
2020-03-27 11:46:36 +00:00
if self.headers.get('Host'):
2020-04-02 21:35:06 +00:00
callingDomain = self.headers['Host']
2020-03-28 15:42:27 +00:00
if self.server.onionDomain:
if callingDomain != self.server.domain and \
callingDomain != self.server.domainFull and \
callingDomain != self.server.onionDomain:
2020-04-02 21:35:06 +00:00
print('GET domain blocked: ' + callingDomain)
2020-03-28 15:42:27 +00:00
self._400()
return
else:
if callingDomain != self.server.domain and \
callingDomain != self.server.domainFull:
2020-04-02 21:35:06 +00:00
print('GET domain blocked: ' + callingDomain)
2020-03-28 15:42:27 +00:00
self._400()
return
2020-03-28 10:33:04 +00:00
2020-04-02 21:35:06 +00:00
GETstartTime = time.time()
GETtimings = []
2019-11-15 18:59:15 +00:00
2020-04-02 21:35:06 +00:00
# Since fediverse crawlers are quite active,
# make returning info to them high priority
2019-11-15 13:17:28 +00:00
# get nodeinfo endpoint
2020-03-28 17:24:40 +00:00
if self._nodeinfo(callingDomain):
2019-11-15 13:17:28 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 1)
2019-11-16 13:25:44 +00:00
2019-11-15 13:17:28 +00:00
# minimal mastodon api
2020-03-28 17:24:40 +00:00
if self._mastoApi(callingDomain):
2019-11-15 13:17:28 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 2)
2019-11-16 13:25:44 +00:00
2020-04-02 21:35:06 +00:00
if self.path == '/logout':
msg = \
htmlLogin(self.server.translate,
self.server.baseDir, False).encode('utf-8')
self._logout_headers('text/html', len(msg), callingDomain)
2020-03-22 21:16:02 +00:00
self._write(msg)
2019-10-30 11:35:40 +00:00
return
2019-11-15 12:56:07 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 3)
2019-11-16 13:25:44 +00:00
2019-11-06 18:51:51 +00:00
# replace https://domain/@nick with https://domain/users/nick
if self.path.startswith('/@'):
2020-04-02 21:35:06 +00:00
self.path = self.path.replace('/@', '/users/')
2019-11-06 18:51:51 +00:00
2019-09-05 11:44:09 +00:00
# redirect music to #nowplaying list
2020-04-02 21:35:06 +00:00
if self.path == '/music' or self.path == '/nowplaying':
self.path = '/tags/nowplaying'
2019-09-05 11:44:09 +00:00
2019-07-03 16:14:45 +00:00
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: GET from ' + self.server.baseDir +
' path: ' + self.path + ' busy: ' +
2019-07-06 17:00:22 +00:00
str(self.server.GETbusy))
2019-07-25 11:18:35 +00:00
if self.server.debug:
print(str(self.headers))
2020-05-25 16:29:27 +00:00
cookie = None
if self.headers.get('Cookie'):
cookie = self.headers['Cookie']
self._benchmarkGETtimings(GETstartTime, GETtimings, 4)
2020-05-29 19:21:08 +00:00
# favicon image
if 'favicon.ico' in self.path:
2020-05-29 19:37:33 +00:00
favType = 'image/x-icon'
2020-05-29 19:21:08 +00:00
favFilename = 'favicon.ico'
2020-06-18 20:56:29 +00:00
if self._hasAccept(callingDomain):
2020-05-29 19:27:45 +00:00
if 'image/webp' in self.headers['Accept']:
2020-05-29 19:37:33 +00:00
favType = 'image/webp'
2020-05-29 19:27:45 +00:00
favFilename = 'favicon.webp'
2020-05-29 19:21:08 +00:00
# custom favicon
faviconFilename = \
self.server.baseDir + '/' + favFilename
if not os.path.isfile(faviconFilename):
# default favicon
faviconFilename = \
self.server.baseDir + '/img/icons/' + favFilename
if self._etag_exists(faviconFilename):
# The file has not changed
self._304()
return
if self.server.iconsCache.get(favFilename):
favBinary = self.server.iconsCache[favFilename]
self._set_headers_etag(faviconFilename,
2020-05-29 19:37:33 +00:00
favType,
2020-05-29 19:21:08 +00:00
favBinary, cookie,
callingDomain)
self._write(favBinary)
return
else:
if os.path.isfile(faviconFilename):
with open(faviconFilename, 'rb') as favFile:
favBinary = favFile.read()
self._set_headers_etag(faviconFilename,
2020-05-29 19:37:33 +00:00
favType,
2020-05-29 19:21:08 +00:00
favBinary, cookie,
callingDomain)
self._write(favBinary)
self.server.iconsCache[favFilename] = favBinary
return
self._404()
return
# check authorization
authorized = self._isAuthorized()
if self.server.debug:
if authorized:
print('GET Authorization granted')
else:
print('GET Not authorized')
self._benchmarkGETtimings(GETstartTime, GETtimings, 5)
if not self.server.session:
print('Starting new session')
2020-06-09 11:03:59 +00:00
self.server.session = createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to create session 2')
self._404()
return
self._benchmarkGETtimings(GETstartTime, GETtimings, 6)
# is this a html request?
htmlGET = False
2020-06-18 20:56:29 +00:00
if self._hasAccept(callingDomain):
if self._requestHTTP():
htmlGET = True
else:
if self.headers.get('Connection'):
# https://developer.mozilla.org/en-US/
# docs/Web/HTTP/Protocol_upgrade_mechanism
if self.headers.get('Upgrade'):
print('HTTP Connection request: ' +
self.headers['Upgrade'])
else:
print('HTTP Connection request: ' +
self.headers['Connection'])
self._200()
else:
print('WARN: No Accept header ' + str(self.headers))
self._400()
return
2020-05-25 16:21:53 +00:00
# get fonts
if htmlGET and '/fonts/' in self.path:
2020-05-25 16:21:53 +00:00
fontStr = self.path.split('/fonts/')[1]
if fontStr.endswith('.otf') or \
fontStr.endswith('.ttf') or \
fontStr.endswith('.woff') or \
fontStr.endswith('.woff2'):
if fontStr.endswith('.otf'):
fontType = 'font/otf'
2020-05-25 16:21:53 +00:00
elif fontStr.endswith('.ttf'):
fontType = 'font/ttf'
2020-05-25 16:21:53 +00:00
elif fontStr.endswith('.woff'):
fontType = 'font/woff'
2020-05-25 16:21:53 +00:00
else:
fontType = 'font/woff2'
2020-05-25 16:21:53 +00:00
fontFilename = \
self.server.baseDir + '/fonts/' + fontStr
2020-05-25 16:32:13 +00:00
if self._etag_exists(fontFilename):
2020-05-25 16:21:53 +00:00
# The file has not changed
2020-05-25 16:32:13 +00:00
self._304()
2020-05-25 16:21:53 +00:00
return
2020-05-25 16:32:13 +00:00
if self.server.fontsCache.get(fontStr):
fontBinary = self.server.fontsCache[fontStr]
self._set_headers_etag(fontFilename,
fontType,
fontBinary, cookie,
callingDomain)
self._write(fontBinary)
return
else:
if os.path.isfile(fontFilename):
with open(fontFilename, 'rb') as avFile:
fontBinary = avFile.read()
self._set_headers_etag(fontFilename,
fontType,
fontBinary, cookie,
callingDomain)
self._write(fontBinary)
self.server.fontsCache[fontStr] = fontBinary
return
2020-05-25 16:21:53 +00:00
self._404()
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 7)
2019-11-16 13:25:44 +00:00
2019-08-05 11:08:52 +00:00
# treat shared inbox paths consistently
2020-04-02 21:35:06 +00:00
if self.path == '/sharedInbox' or \
self.path == '/users/inbox' or \
self.path == '/actor/inbox' or \
self.path == '/users/'+self.server.domain:
2019-11-15 21:43:20 +00:00
# if shared inbox is not enabled
if not self.server.enableSharedInbox:
self._503()
return
2020-03-22 21:16:02 +00:00
2020-04-02 21:35:06 +00:00
self.path = '/inbox'
2019-08-05 11:08:52 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 8)
2019-11-16 13:25:44 +00:00
2020-05-23 09:41:50 +00:00
# RSS 2.0
2020-04-02 21:35:06 +00:00
if self.path.startswith('/blog/') and \
self.path.endswith('/rss.xml'):
nickname = self.path.split('/blog/')[1]
2020-02-27 20:37:42 +00:00
if '/' in nickname:
2020-04-02 21:35:06 +00:00
nickname = nickname.split('/')[0]
2020-02-27 20:37:42 +00:00
if not nickname.startswith('rss.'):
2020-04-02 21:35:06 +00:00
if os.path.isdir(self.server.baseDir +
'/accounts/' + nickname +
'@' + self.server.domain):
2020-02-27 20:37:42 +00:00
if not self.server.session:
2020-04-02 21:35:06 +00:00
self.server.session = \
2020-06-09 11:03:59 +00:00
createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to create session 3')
self._404()
return
2020-04-02 21:35:06 +00:00
msg = \
htmlBlogPageRSS2(authorized,
self.server.session,
self.server.baseDir,
self.server.httpPrefix,
self.server.translate,
nickname,
self.server.domain,
self.server.port,
maxPostsInRSSFeed, 1)
2020-04-02 21:35:06 +00:00
if msg is not None:
2020-06-14 19:06:10 +00:00
msg = msg.encode('utf-8')
2020-04-02 21:35:06 +00:00
self._set_headers('text/xml', len(msg),
cookie, callingDomain)
2020-02-27 20:37:42 +00:00
self._write(msg)
return
self._404()
2020-03-22 21:16:02 +00:00
return
2020-02-27 20:23:27 +00:00
2020-05-23 09:41:50 +00:00
# RSS 3.0
if self.path.startswith('/blog/') and \
self.path.endswith('/rss.txt'):
nickname = self.path.split('/blog/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
if not nickname.startswith('rss.'):
if os.path.isdir(self.server.baseDir +
'/accounts/' + nickname +
'@' + self.server.domain):
if not self.server.session:
self.server.session = \
2020-06-09 11:03:59 +00:00
createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to create session 4')
self._404()
return
2020-05-23 09:41:50 +00:00
msg = \
htmlBlogPageRSS3(authorized,
self.server.session,
self.server.baseDir,
self.server.httpPrefix,
self.server.translate,
nickname,
self.server.domain,
self.server.port,
maxPostsInRSSFeed, 1)
if msg is not None:
2020-06-14 19:06:10 +00:00
msg = msg.encode('utf-8')
2020-05-23 09:41:50 +00:00
self._set_headers('text/plain; charset=utf-8',
len(msg), cookie, callingDomain)
self._write(msg)
return
self._404()
return
2020-02-25 13:35:41 +00:00
# show the main blog page
2020-04-02 21:35:06 +00:00
if htmlGET and (self.path == '/blog' or
self.path == '/blog/' or
self.path == '/blogs' or
self.path == '/blogs/'):
2020-02-27 20:33:49 +00:00
if '/rss.xml' not in self.path:
if not self.server.session:
2020-04-02 21:35:06 +00:00
self.server.session = \
2020-06-09 11:03:59 +00:00
createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to create session 5')
self._404()
return
2020-04-02 21:35:06 +00:00
msg = htmlBlogView(authorized,
self.server.session,
self.server.baseDir,
self.server.httpPrefix,
self.server.translate,
self.server.domain,
self.server.port,
maxPostsInBlogsFeed)
if msg is not None:
2020-06-14 19:06:10 +00:00
msg = msg.encode('utf-8')
2020-04-02 21:35:06 +00:00
self._set_headers('text/html', len(msg),
cookie, callingDomain)
2020-02-27 20:33:49 +00:00
self._write(msg)
return
self._404()
2020-02-25 13:35:41 +00:00
return
2020-02-25 13:49:38 +00:00
# show a particular page of blog entries
# for a particular account
2020-02-25 13:35:41 +00:00
if htmlGET and self.path.startswith('/blog/'):
2020-02-27 20:33:49 +00:00
if '/rss.xml' not in self.path:
2020-04-02 21:35:06 +00:00
pageNumber = 1
nickname = self.path.split('/blog/')[1]
2020-02-27 20:33:49 +00:00
if '/' in nickname:
2020-04-02 21:35:06 +00:00
nickname = nickname.split('/')[0]
2020-02-27 20:33:49 +00:00
if '?' in nickname:
2020-04-02 21:35:06 +00:00
nickname = nickname.split('?')[0]
2020-02-27 20:33:49 +00:00
if '?page=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumberStr = self.path.split('?page=')[1]
2020-02-27 20:33:49 +00:00
if '?' in pageNumberStr:
2020-04-02 21:35:06 +00:00
pageNumberStr = pageNumberStr.split('?')[0]
2020-05-22 12:41:47 +00:00
if '#' in pageNumberStr:
pageNumberStr = pageNumberStr.split('#')[0]
2020-02-27 20:33:49 +00:00
if pageNumberStr.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumberStr)
if pageNumber < 1:
pageNumber = 1
elif pageNumber > 10:
pageNumber = 10
2020-02-27 20:33:49 +00:00
if not self.server.session:
2020-04-02 21:35:06 +00:00
self.server.session = \
2020-06-09 11:03:59 +00:00
createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to create session 6')
self._404()
return
2020-04-02 21:35:06 +00:00
msg = htmlBlogPage(authorized,
self.server.session,
self.server.baseDir,
self.server.httpPrefix,
self.server.translate,
nickname,
self.server.domain, self.server.port,
maxPostsInBlogsFeed, pageNumber)
if msg is not None:
2020-06-14 19:06:10 +00:00
msg = msg.encode('utf-8')
2020-04-02 21:35:06 +00:00
self._set_headers('text/html', len(msg),
cookie, callingDomain)
2020-02-27 20:33:49 +00:00
self._write(msg)
return
self._404()
2020-02-25 13:35:41 +00:00
return
2020-03-22 21:16:02 +00:00
if htmlGET and '/users/' in self.path:
2020-02-25 09:34:45 +00:00
# show the person options screen with view/follow/block/report
if '?options=' in self.path:
2020-04-02 21:35:06 +00:00
optionsStr = self.path.split('?options=')[1]
originPathStr = self.path.split('?options=')[0]
2020-02-25 09:34:45 +00:00
if ';' in optionsStr:
2020-04-02 21:35:06 +00:00
pageNumber = 1
optionsList = optionsStr.split(';')
optionsActor = optionsList[0]
optionsPageNumber = optionsList[1]
optionsProfileUrl = optionsList[2]
2020-02-25 09:34:45 +00:00
if optionsPageNumber.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(optionsPageNumber)
optionsLink = None
if len(optionsList) > 3:
optionsLink = optionsList[3]
donateUrl = None
PGPpubKey = None
xmppAddress = None
matrixAddress = None
2020-05-04 17:48:07 +00:00
blogAddress = None
2020-04-02 21:35:06 +00:00
toxAddress = None
ssbAddress = None
emailAddress = None
actorJson = getPersonFromCache(self.server.baseDir,
optionsActor,
self.server.personCache)
2020-02-25 09:34:45 +00:00
if actorJson:
2020-04-02 21:35:06 +00:00
donateUrl = getDonationUrl(actorJson)
xmppAddress = getXmppAddress(actorJson)
matrixAddress = getMatrixAddress(actorJson)
ssbAddress = getSSBAddress(actorJson)
2020-05-04 11:28:43 +00:00
blogAddress = getBlogAddress(actorJson)
2020-04-02 21:35:06 +00:00
toxAddress = getToxAddress(actorJson)
emailAddress = getEmailAddress(actorJson)
PGPpubKey = getPGPpubKey(actorJson)
msg = htmlPersonOptions(self.server.translate,
self.server.baseDir,
self.server.domain,
originPathStr,
optionsActor,
optionsProfileUrl,
optionsLink,
pageNumber, donateUrl,
xmppAddress, matrixAddress,
2020-05-04 11:28:43 +00:00
ssbAddress, blogAddress,
toxAddress, PGPpubKey,
2020-06-14 19:06:10 +00:00
emailAddress).encode('utf-8')
2020-04-02 21:35:06 +00:00
self._set_headers('text/html', len(msg),
cookie, callingDomain)
2020-02-25 09:34:45 +00:00
self._write(msg)
return
2020-06-03 19:14:24 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
2020-04-02 21:35:06 +00:00
originPathStrAbsolute = \
2020-06-03 19:14:24 +00:00
'http://' + self.server.onionDomain + originPathStr
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
originPathStrAbsolute = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + originPathStr
else:
2020-04-02 21:35:06 +00:00
originPathStrAbsolute = \
2020-06-03 19:14:24 +00:00
self.server.httpPrefix + '://' + \
self.server.domainFull + originPathStr
2020-04-02 21:35:06 +00:00
self._redirect_headers(originPathStrAbsolute, cookie,
callingDomain)
2020-02-25 09:34:45 +00:00
return
2019-08-24 21:14:33 +00:00
2020-02-25 13:35:41 +00:00
# show blog post
2020-04-02 21:35:06 +00:00
blogFilename, nickname = \
self._pathContainsBlogLink(self.server.baseDir,
self.server.httpPrefix,
self.server.domain,
self.server.domainFull,
2020-02-25 13:35:41 +00:00
self.path)
if blogFilename and nickname:
2020-04-02 21:35:06 +00:00
postJsonObject = loadJson(blogFilename)
2020-02-25 13:35:41 +00:00
if isBlogPost(postJsonObject):
2020-05-18 14:00:47 +00:00
msg = htmlBlogPost(authorized,
2020-04-02 21:35:06 +00:00
self.server.baseDir,
self.server.httpPrefix,
self.server.translate,
nickname, self.server.domain,
self.server.domainFull,
postJsonObject)
if msg is not None:
2020-06-14 19:06:10 +00:00
msg = msg.encode('utf-8')
2020-04-02 21:35:06 +00:00
self._set_headers('text/html', len(msg),
cookie, callingDomain)
2020-02-25 13:35:41 +00:00
self._write(msg)
return
self._404()
return
2020-03-22 21:16:02 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 9)
2019-11-16 13:25:44 +00:00
2019-08-26 09:15:48 +00:00
# remove a shared item
if htmlGET and '?rmshare=' in self.path:
2020-04-02 21:35:06 +00:00
shareName = self.path.split('?rmshare=')[1]
2020-04-15 10:57:04 +00:00
shareName = urllib.parse.unquote(shareName.strip())
2020-04-02 21:35:06 +00:00
usersPath = self.path.split('?rmshare=')[0]
actor = \
self.server.httpPrefix + '://' + \
self.server.domainFull + usersPath
msg = htmlRemoveSharedItem(self.server.translate,
self.server.baseDir,
2020-06-14 19:06:10 +00:00
actor, shareName).encode('utf-8')
2019-08-26 09:15:48 +00:00
if not msg:
2020-04-02 21:35:06 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actor = 'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
actor = 'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
self._redirect_headers(actor + '/tlshares',
cookie, callingDomain)
return
self._set_headers('text/html', len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-08-26 09:15:48 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 10)
2019-11-16 13:25:44 +00:00
if self.path.startswith('/terms'):
2020-06-03 19:14:24 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
msg = htmlTermsOfService(self.server.baseDir, 'http',
2020-06-14 19:06:10 +00:00
self.server.onionDomain)
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 22:50:41 +00:00
msg = htmlTermsOfService(self.server.baseDir, 'http',
2020-06-14 19:06:10 +00:00
self.server.i2pDomain)
2020-06-03 19:14:24 +00:00
else:
2020-04-02 21:35:06 +00:00
msg = htmlTermsOfService(self.server.baseDir,
self.server.httpPrefix,
2020-06-14 19:06:10 +00:00
self.server.domainFull)
msg = msg.encode('utf-8')
2020-04-02 21:35:06 +00:00
self._login_headers('text/html', len(msg), callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-08-26 16:07:04 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 11)
2019-11-16 13:25:44 +00:00
# show a list of who you are following
if htmlGET and authorized and '/users/' in self.path and \
self.path.endswith('/followingaccounts'):
nickname = getNicknameFromActor(self.path)
followingFilename = \
2020-06-17 16:52:45 +00:00
self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain + '/following.txt'
if not os.path.isfile(followingFilename):
self._404()
return
msg = ''
with open(followingFilename, 'r') as followingFile:
msg = followingFile.read()
self._login_headers('text/plain', len(msg), callingDomain)
self._write(msg.encode('utf-8'))
return
if self.path.startswith('/about'):
2020-06-03 19:14:24 +00:00
if callingDomain.endswith('.onion'):
msg = \
htmlAbout(self.server.baseDir, 'http',
self.server.onionDomain,
2020-06-14 19:06:10 +00:00
None)
2020-06-03 19:14:24 +00:00
elif callingDomain.endswith('.i2p'):
msg = \
2020-06-19 22:50:41 +00:00
htmlAbout(self.server.baseDir, 'http',
2020-06-03 19:14:24 +00:00
self.server.i2pDomain,
2020-06-14 19:06:10 +00:00
None)
2020-06-03 19:14:24 +00:00
else:
2020-04-02 21:35:06 +00:00
msg = \
htmlAbout(self.server.baseDir,
self.server.httpPrefix,
2020-04-17 16:30:06 +00:00
self.server.domainFull,
2020-06-14 19:06:10 +00:00
self.server.onionDomain)
msg = msg.encode('utf-8')
2020-04-02 21:35:06 +00:00
self._login_headers('text/html', len(msg), callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-08-26 16:07:04 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 12)
2019-11-16 13:25:44 +00:00
2019-09-03 20:27:49 +00:00
# send robots.txt if asked
if self._robotsTxt():
return
2020-03-22 21:16:02 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 13)
2019-11-16 13:25:44 +00:00
2019-08-18 20:11:26 +00:00
# if not authorized then show the login screen
2020-06-19 19:40:40 +00:00
if htmlGET and self.path != '/login' and \
2020-06-19 20:04:22 +00:00
not self._pathIsImage() and self.path != '/':
2019-08-18 20:11:26 +00:00
if '/media/' not in self.path and \
'/sharefiles/' not in self.path and \
'/statuses/' not in self.path and \
'/emoji/' not in self.path and \
'/tags/' not in self.path and \
2019-09-14 17:29:55 +00:00
'/avatars/' not in self.path and \
2019-08-18 20:11:26 +00:00
'/icons/' not in self.path:
2020-04-02 21:35:06 +00:00
divertToLoginScreen = True
2019-08-18 20:11:26 +00:00
if self.path.startswith('/users/'):
2020-04-02 21:35:06 +00:00
nickStr = self.path.split('/users/')[1]
2019-08-18 20:11:26 +00:00
if '/' not in nickStr and '?' not in nickStr:
2020-04-02 21:35:06 +00:00
divertToLoginScreen = False
2019-08-18 20:11:26 +00:00
else:
if self.path.endswith('/following') or \
self.path.endswith('/followers') or \
self.path.endswith('/skills') or \
self.path.endswith('/roles') or \
self.path.endswith('/shares'):
2020-04-02 21:35:06 +00:00
divertToLoginScreen = False
2019-08-18 20:11:26 +00:00
if divertToLoginScreen and not authorized:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: divertToLoginScreen=' +
2020-03-30 19:09:45 +00:00
str(divertToLoginScreen))
2020-04-02 21:35:06 +00:00
print('DEBUG: authorized=' + str(authorized))
print('DEBUG: path=' + self.path)
2020-06-03 19:14:24 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
2020-06-19 09:40:35 +00:00
self._redirect_headers('http://' +
self.server.onionDomain +
'/login',
None, callingDomain)
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:40:35 +00:00
self._redirect_headers('http://' +
self.server.i2pDomain +
'/login',
None, callingDomain)
2020-06-03 19:14:24 +00:00
else:
2020-06-19 09:40:35 +00:00
self._redirect_headers(self.server.httpPrefix + '://' +
self.server.domainFull +
'/login', None, callingDomain)
2019-08-18 20:11:26 +00:00
return
2019-11-15 14:34:11 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 14)
2019-11-16 13:25:44 +00:00
2019-07-21 09:09:28 +00:00
# get css
# Note that this comes before the busy flag to avoid conflicts
if self.path.endswith('.css'):
2019-07-24 11:03:56 +00:00
if os.path.isfile('epicyon-profile.css'):
2020-04-02 21:35:06 +00:00
tries = 0
while tries < 5:
2019-10-11 18:03:58 +00:00
try:
with open('epicyon-profile.css', 'r') as cssfile:
2020-04-02 21:35:06 +00:00
css = cssfile.read()
2019-10-11 18:03:58 +00:00
break
except Exception as e:
print(e)
time.sleep(1)
2020-04-02 21:35:06 +00:00
tries += 1
msg = css.encode('utf-8')
self._set_headers('text/css', len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-07-21 09:09:28 +00:00
return
2019-11-15 14:03:43 +00:00
self._404()
2019-11-15 18:59:15 +00:00
return
2019-10-23 12:04:08 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 15)
2019-11-16 13:25:44 +00:00
2020-06-20 19:37:44 +00:00
# image on login screen or qrcode
2020-04-02 21:35:06 +00:00
if self.path == '/login.png' or \
self.path == '/login.gif' or \
self.path == '/login.webp' or \
self.path == '/login.jpeg' or \
2020-06-20 19:37:44 +00:00
self.path == '/login.jpg' or \
self.path == '/qrcode.png':
2020-04-02 21:35:06 +00:00
mediaFilename = \
self.server.baseDir + '/accounts' + self.path
2019-07-24 22:38:42 +00:00
if os.path.isfile(mediaFilename):
2020-04-13 19:44:01 +00:00
if self._etag_exists(mediaFilename):
# The file has not changed
self._304()
return
2020-04-02 21:35:06 +00:00
tries = 0
mediaBinary = None
while tries < 5:
2019-10-14 21:38:03 +00:00
try:
with open(mediaFilename, 'rb') as avFile:
2020-04-02 21:35:06 +00:00
mediaBinary = avFile.read()
2019-10-14 21:38:03 +00:00
break
except Exception as e:
print(e)
time.sleep(1)
2020-04-02 21:35:06 +00:00
tries += 1
2019-10-14 21:38:03 +00:00
if mediaBinary:
2020-04-13 19:28:35 +00:00
self._set_headers_etag(mediaFilename,
'image/png',
mediaBinary, cookie,
callingDomain)
2019-10-22 12:35:51 +00:00
self._write(mediaBinary)
2019-10-14 21:29:46 +00:00
return
2019-08-16 22:21:34 +00:00
self._404()
2019-10-23 12:04:08 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 16)
2019-11-16 13:25:44 +00:00
2019-07-25 19:56:25 +00:00
# login screen background image
2020-04-02 21:35:06 +00:00
if self.path == '/login-background.png':
mediaFilename = \
self.server.baseDir + '/accounts/login-background.png'
2019-07-25 19:56:25 +00:00
if os.path.isfile(mediaFilename):
2020-04-13 19:44:01 +00:00
if self._etag_exists(mediaFilename):
# The file has not changed
self._304()
return
2020-04-02 21:35:06 +00:00
tries = 0
mediaBinary = None
while tries < 5:
2019-10-14 21:38:03 +00:00
try:
with open(mediaFilename, 'rb') as avFile:
2020-04-02 21:35:06 +00:00
mediaBinary = avFile.read()
2019-10-14 21:38:03 +00:00
break
except Exception as e:
print(e)
time.sleep(1)
2020-04-02 21:35:06 +00:00
tries += 1
2019-10-14 21:38:03 +00:00
if mediaBinary:
2020-04-13 19:28:35 +00:00
self._set_headers_etag(mediaFilename, 'image/png',
mediaBinary, cookie,
callingDomain)
2019-10-22 12:35:51 +00:00
self._write(mediaBinary)
2019-08-16 22:21:34 +00:00
return
self._404()
return
2019-10-23 12:04:08 +00:00
2020-06-10 12:59:01 +00:00
# search screen banner image
2020-06-10 13:05:47 +00:00
if '/users/' in self.path and \
self.path.endswith('/search_banner.png'):
nickname = getNicknameFromActor(self.path)
2020-06-10 12:59:01 +00:00
mediaFilename = \
2020-06-10 13:05:47 +00:00
self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain + '/search_banner.png'
2020-06-10 12:59:01 +00:00
if os.path.isfile(mediaFilename):
if self._etag_exists(mediaFilename):
# The file has not changed
self._304()
return
tries = 0
mediaBinary = None
while tries < 5:
try:
with open(mediaFilename, 'rb') as avFile:
mediaBinary = avFile.read()
break
except Exception as e:
print(e)
time.sleep(1)
tries += 1
if mediaBinary:
self._set_headers_etag(mediaFilename, 'image/png',
mediaBinary, cookie,
callingDomain)
self._write(mediaBinary)
return
self._404()
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 17)
2019-11-16 13:25:44 +00:00
2019-07-29 09:49:46 +00:00
# follow screen background image
2020-04-02 21:35:06 +00:00
if self.path == '/follow-background.png':
mediaFilename = \
self.server.baseDir + '/accounts/follow-background.png'
2019-07-29 09:49:46 +00:00
if os.path.isfile(mediaFilename):
2020-04-13 19:44:01 +00:00
if self._etag_exists(mediaFilename):
# The file has not changed
self._304()
return
2020-04-02 21:35:06 +00:00
tries = 0
mediaBinary = None
while tries < 5:
2019-10-14 21:38:03 +00:00
try:
with open(mediaFilename, 'rb') as avFile:
2020-04-02 21:35:06 +00:00
mediaBinary = avFile.read()
2019-10-14 21:38:03 +00:00
break
except Exception as e:
print(e)
time.sleep(1)
2020-04-02 21:35:06 +00:00
tries += 1
2019-10-14 21:38:03 +00:00
if mediaBinary:
2020-04-13 19:28:35 +00:00
self._set_headers_etag(mediaFilename, 'image/png',
mediaBinary, cookie,
callingDomain)
2019-10-22 12:35:51 +00:00
self._write(mediaBinary)
2019-10-14 21:29:46 +00:00
return
2019-08-16 22:21:34 +00:00
self._404()
2019-08-09 12:50:49 +00:00
return
2019-10-23 12:04:08 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 18)
2019-11-16 13:25:44 +00:00
2019-08-09 12:50:49 +00:00
# emoji images
if '/emoji/' in self.path:
2020-06-19 20:04:22 +00:00
if self._pathIsImage():
2020-04-02 21:35:06 +00:00
emojiStr = self.path.split('/emoji/')[1]
emojiFilename = \
self.server.baseDir + '/emoji/' + emojiStr
2019-08-09 12:50:49 +00:00
if os.path.isfile(emojiFilename):
2020-04-13 19:44:01 +00:00
if self._etag_exists(emojiFilename):
# The file has not changed
self._304()
return
2020-04-02 21:35:06 +00:00
mediaImageType = 'png'
2019-08-09 12:50:49 +00:00
if emojiFilename.endswith('.png'):
2020-04-02 21:35:06 +00:00
mediaImageType = 'png'
2019-08-09 12:50:49 +00:00
elif emojiFilename.endswith('.jpg'):
2020-04-02 21:35:06 +00:00
mediaImageType = 'jpeg'
2019-11-14 15:11:20 +00:00
elif emojiFilename.endswith('.webp'):
2020-04-02 21:35:06 +00:00
mediaImageType = 'webp'
2019-08-09 12:50:49 +00:00
else:
2020-04-02 21:35:06 +00:00
mediaImageType = 'gif'
2019-08-09 12:50:49 +00:00
with open(emojiFilename, 'rb') as avFile:
2020-04-02 21:35:06 +00:00
mediaBinary = avFile.read()
2020-04-13 19:28:35 +00:00
self._set_headers_etag(emojiFilename,
'image/' + mediaImageType,
mediaBinary, cookie,
callingDomain)
2019-10-22 12:35:51 +00:00
self._write(mediaBinary)
2019-08-09 17:42:11 +00:00
return
2019-08-09 12:50:49 +00:00
self._404()
return
2019-10-23 12:04:08 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 19)
2019-11-16 13:25:44 +00:00
2019-07-12 19:33:34 +00:00
# show media
# Note that this comes before the busy flag to avoid conflicts
if '/media/' in self.path:
2020-06-19 20:04:22 +00:00
if self._pathIsImage() or \
2020-06-19 20:08:06 +00:00
self._pathIsVideo() or \
self._pathIsAudio():
2020-04-02 21:35:06 +00:00
mediaStr = self.path.split('/media/')[1]
mediaFilename = \
self.server.baseDir + '/media/' + mediaStr
2019-07-12 19:33:34 +00:00
if os.path.isfile(mediaFilename):
2020-04-13 19:44:01 +00:00
if self._etag_exists(mediaFilename):
# The file has not changed
self._304()
return
2020-04-02 21:35:06 +00:00
mediaFileType = 'image/png'
2019-07-12 19:33:34 +00:00
if mediaFilename.endswith('.png'):
2020-04-02 21:35:06 +00:00
mediaFileType = 'image/png'
2019-07-12 19:33:34 +00:00
elif mediaFilename.endswith('.jpg'):
2020-04-02 21:35:06 +00:00
mediaFileType = 'image/jpeg'
2019-08-30 19:57:24 +00:00
elif mediaFilename.endswith('.gif'):
2020-04-02 21:35:06 +00:00
mediaFileType = 'image/gif'
2019-11-14 15:11:20 +00:00
elif mediaFilename.endswith('.webp'):
2020-04-02 21:35:06 +00:00
mediaFileType = 'image/webp'
2019-08-30 19:57:24 +00:00
elif mediaFilename.endswith('.mp4'):
2020-04-02 21:35:06 +00:00
mediaFileType = 'video/mp4'
2019-08-30 19:57:24 +00:00
elif mediaFilename.endswith('.ogv'):
2020-04-02 21:35:06 +00:00
mediaFileType = 'video/ogv'
2019-08-30 19:57:24 +00:00
elif mediaFilename.endswith('.mp3'):
2020-04-02 21:35:06 +00:00
mediaFileType = 'audio/mpeg'
2019-08-30 19:57:24 +00:00
elif mediaFilename.endswith('.ogg'):
2020-04-02 21:35:06 +00:00
mediaFileType = 'audio/ogg'
2019-12-04 12:47:16 +00:00
2019-07-12 19:33:34 +00:00
with open(mediaFilename, 'rb') as avFile:
2020-04-02 21:35:06 +00:00
mediaBinary = avFile.read()
self._set_headers_etag(mediaFilename, mediaFileType,
mediaBinary, cookie,
callingDomain)
2019-10-22 12:35:51 +00:00
self._write(mediaBinary)
2020-03-22 21:16:02 +00:00
return
2019-07-16 16:10:52 +00:00
self._404()
return
2019-10-23 12:04:08 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 20)
2019-11-16 13:25:44 +00:00
2019-07-23 12:33:09 +00:00
# show shared item images
# Note that this comes before the busy flag to avoid conflicts
if '/sharefiles/' in self.path:
2020-06-19 20:04:22 +00:00
if self._pathIsImage():
2020-04-02 21:35:06 +00:00
mediaStr = self.path.split('/sharefiles/')[1]
mediaFilename = \
self.server.baseDir + '/sharefiles/' + mediaStr
2019-07-23 12:33:09 +00:00
if os.path.isfile(mediaFilename):
2020-04-13 20:57:18 +00:00
if self._etag_exists(mediaFilename):
# The file has not changed
self._304()
return
2020-04-02 21:35:06 +00:00
mediaFileType = 'png'
2019-07-23 12:33:09 +00:00
if mediaFilename.endswith('.png'):
2020-04-02 21:35:06 +00:00
mediaFileType = 'png'
2019-07-23 12:33:09 +00:00
elif mediaFilename.endswith('.jpg'):
2020-04-02 21:35:06 +00:00
mediaFileType = 'jpeg'
2019-11-14 15:11:20 +00:00
elif mediaFilename.endswith('.webp'):
2020-04-02 21:35:06 +00:00
mediaFileType = 'webp'
2019-07-23 12:33:09 +00:00
else:
2020-04-02 21:35:06 +00:00
mediaFileType = 'gif'
2019-07-23 12:33:09 +00:00
with open(mediaFilename, 'rb') as avFile:
2020-04-02 21:35:06 +00:00
mediaBinary = avFile.read()
2020-04-13 20:57:18 +00:00
self._set_headers_etag(mediaFilename,
'image/' + mediaFileType,
mediaBinary, cookie,
callingDomain)
2019-10-22 12:35:51 +00:00
self._write(mediaBinary)
2020-03-22 21:16:02 +00:00
return
2019-07-23 12:33:09 +00:00
self._404()
return
2019-10-23 12:04:08 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 21)
2019-11-16 13:25:44 +00:00
2019-07-26 10:30:13 +00:00
# icon images
# Note that this comes before the busy flag to avoid conflicts
if self.path.startswith('/icons/'):
2019-07-26 10:30:13 +00:00
if self.path.endswith('.png'):
2020-04-02 21:35:06 +00:00
mediaStr = self.path.split('/icons/')[1]
mediaFilename = \
self.server.baseDir + '/img/icons/' + mediaStr
2020-04-13 20:14:46 +00:00
if self._etag_exists(mediaFilename):
# The file has not changed
self._304()
return
2019-11-24 18:06:54 +00:00
if self.server.iconsCache.get(mediaStr):
2020-04-02 21:35:06 +00:00
mediaBinary = self.server.iconsCache[mediaStr]
2020-04-13 20:14:46 +00:00
self._set_headers_etag(mediaFilename,
'image/png',
mediaBinary, cookie,
callingDomain)
2019-11-24 18:06:54 +00:00
self._write(mediaBinary)
2020-03-22 21:16:02 +00:00
return
2019-11-24 18:06:54 +00:00
else:
if os.path.isfile(mediaFilename):
with open(mediaFilename, 'rb') as avFile:
2020-04-02 21:35:06 +00:00
mediaBinary = avFile.read()
2020-04-13 20:14:46 +00:00
self._set_headers_etag(mediaFilename,
'image/png',
mediaBinary, cookie,
callingDomain)
2019-11-24 18:06:54 +00:00
self._write(mediaBinary)
2020-04-02 21:35:06 +00:00
self.server.iconsCache[mediaStr] = mediaBinary
2019-11-24 18:06:54 +00:00
return
2019-07-26 10:30:13 +00:00
self._404()
return
2019-10-23 12:04:08 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 22)
2019-11-16 13:25:44 +00:00
2019-09-14 17:29:55 +00:00
# cached avatar images
# Note that this comes before the busy flag to avoid conflicts
if self.path.startswith('/avatars/'):
2020-04-02 21:35:06 +00:00
mediaFilename = \
self.server.baseDir + '/cache/' + self.path
2019-09-14 17:29:55 +00:00
if os.path.isfile(mediaFilename):
2020-04-13 20:57:18 +00:00
if self._etag_exists(mediaFilename):
# The file has not changed
self._304()
return
2019-09-14 17:29:55 +00:00
with open(mediaFilename, 'rb') as avFile:
2020-04-02 21:35:06 +00:00
mediaBinary = avFile.read()
2019-09-14 17:29:55 +00:00
if mediaFilename.endswith('.png'):
2020-04-13 20:57:18 +00:00
self._set_headers_etag(mediaFilename,
'image/png',
mediaBinary, cookie,
callingDomain)
2019-09-14 17:29:55 +00:00
elif mediaFilename.endswith('.jpg'):
2020-04-13 20:57:18 +00:00
self._set_headers_etag(mediaFilename,
'image/jpeg',
mediaBinary, cookie,
callingDomain)
2019-09-14 17:55:52 +00:00
elif mediaFilename.endswith('.gif'):
2020-04-13 20:57:18 +00:00
self._set_headers_etag(mediaFilename,
'image/gif',
mediaBinary, cookie,
callingDomain)
elif mediaFilename.endswith('.webp'):
self._set_headers_etag(mediaFilename,
'image/webp',
mediaBinary, cookie,
callingDomain)
2019-09-14 17:29:55 +00:00
else:
2019-10-18 09:00:16 +00:00
# default to jpeg
2020-04-13 20:57:18 +00:00
self._set_headers_etag(mediaFilename,
'image/jpeg',
mediaBinary, cookie,
callingDomain)
2020-04-02 21:35:06 +00:00
# self._404()
2019-09-14 17:29:55 +00:00
return
2019-10-22 12:35:51 +00:00
self._write(mediaBinary)
2020-03-22 21:16:02 +00:00
return
2019-09-14 17:29:55 +00:00
self._404()
return
2019-11-14 14:40:51 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 23)
2019-11-16 13:25:44 +00:00
2019-07-12 16:09:25 +00:00
# show avatar or background image
# Note that this comes before the busy flag to avoid conflicts
if '/users/' in self.path:
2020-06-19 20:04:22 +00:00
if self._pathIsImage():
2020-04-02 21:35:06 +00:00
avatarStr = self.path.split('/users/')[1]
2019-11-14 14:40:51 +00:00
if '/' in avatarStr and '.temp.' not in self.path:
2020-04-02 21:35:06 +00:00
avatarNickname = avatarStr.split('/')[0]
avatarFile = avatarStr.split('/')[1]
2020-01-19 23:09:37 +00:00
# remove any numbers, eg. avatar123.png becomes avatar.png
if avatarFile.startswith('avatar'):
2020-04-02 21:35:06 +00:00
avatarFile = 'avatar.' + avatarFile.split('.')[1]
elif avatarFile.startswith('image'):
2020-04-02 21:35:06 +00:00
avatarFile = 'image.'+avatarFile.split('.')[1]
avatarFilename = \
self.server.baseDir + '/accounts/' + \
avatarNickname + '@' + \
self.server.domain + '/' + avatarFile
2019-07-12 16:03:01 +00:00
if os.path.isfile(avatarFilename):
2020-04-13 20:59:56 +00:00
if self._etag_exists(avatarFilename):
# The file has not changed
self._304()
return
2020-04-02 21:35:06 +00:00
mediaImageType = 'png'
2019-07-12 16:03:01 +00:00
if avatarFile.endswith('.png'):
2020-04-02 21:35:06 +00:00
mediaImageType = 'png'
2019-07-12 16:03:01 +00:00
elif avatarFile.endswith('.jpg'):
2020-04-02 21:35:06 +00:00
mediaImageType = 'jpeg'
2019-11-14 14:40:51 +00:00
elif avatarFile.endswith('.gif'):
2020-04-02 21:35:06 +00:00
mediaImageType = 'gif'
2019-11-14 14:40:51 +00:00
else:
2020-04-02 21:35:06 +00:00
mediaImageType = 'webp'
2019-07-12 16:03:01 +00:00
with open(avatarFilename, 'rb') as avFile:
2020-04-02 21:35:06 +00:00
mediaBinary = avFile.read()
2020-04-13 20:59:56 +00:00
self._set_headers_etag(avatarFilename,
'image/' + mediaImageType,
mediaBinary, cookie,
callingDomain)
2019-10-22 12:35:51 +00:00
self._write(mediaBinary)
2019-07-22 15:14:39 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 24)
2019-11-16 13:25:44 +00:00
2019-07-22 15:14:39 +00:00
# This busy state helps to avoid flooding
# Resources which are expected to be called from a web page
# should be above this
if self.server.GETbusy:
2020-04-02 21:35:06 +00:00
currTimeGET = int(time.time())
if currTimeGET - self.server.lastGET == 0:
if self.server.debug:
print('DEBUG: GET Busy')
2020-03-22 21:16:02 +00:00
self.send_response(429)
self.end_headers()
return
2020-04-02 21:35:06 +00:00
self.server.lastGET = currTimeGET
self.server.GETbusy = True
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 25)
2019-11-16 13:25:44 +00:00
if not self._permittedDir(self.path):
if self.server.debug:
print('DEBUG: GET Not permitted')
self._404()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
return
# get webfinger endpoint for a person
2020-03-27 12:18:11 +00:00
if self._webfinger(callingDomain):
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
return
2019-08-08 13:38:33 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 26)
2019-11-16 13:25:44 +00:00
if self.path.startswith('/login') or \
2020-04-02 21:35:06 +00:00
(self.path == '/' and not authorized):
2019-07-24 22:38:42 +00:00
# request basic auth
2020-04-02 21:35:06 +00:00
msg = htmlLogin(self.server.translate,
self.server.baseDir).encode('utf-8')
self._login_headers('text/html', len(msg), callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-07-25 21:39:09 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 27)
2019-11-16 13:25:44 +00:00
2019-08-10 10:54:52 +00:00
# hashtag search
2019-12-13 10:33:33 +00:00
if self.path.startswith('/tags/') or \
(authorized and '/tags/' in self.path):
2020-04-02 21:35:06 +00:00
pageNumber = 1
2019-08-10 10:54:52 +00:00
if '?page=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumberStr = self.path.split('?page=')[1]
2020-05-22 12:41:47 +00:00
if '#' in pageNumberStr:
pageNumberStr = pageNumberStr.split('#')[0]
2019-08-10 10:54:52 +00:00
if pageNumberStr.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumberStr)
hashtag = self.path.split('/tags/')[1]
2019-08-10 10:54:52 +00:00
if '?page=' in hashtag:
2020-04-02 21:35:06 +00:00
hashtag = hashtag.split('?page=')[0]
if isBlockedHashtag(self.server.baseDir, hashtag):
msg = htmlHashtagBlocked(self.server.baseDir).encode('utf-8')
self._login_headers('text/html', len(msg), callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-08-14 10:32:15 +00:00
return
2020-04-02 21:35:06 +00:00
nickname = None
2019-12-13 10:33:33 +00:00
if '/users/' in self.path:
2020-04-02 21:35:06 +00:00
actor = \
self.server.httpPrefix + '://' + \
self.server.domainFull + self.path
nickname = \
2019-12-13 10:33:33 +00:00
getNicknameFromActor(actor)
2020-04-02 21:35:06 +00:00
hashtagStr = \
htmlHashtagSearch(nickname,
self.server.domain, self.server.port,
self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
self.server.baseDir, hashtag, pageNumber,
maxPostsInFeed, self.server.session,
self.server.cachedWebfingers,
self.server.personCache,
self.server.httpPrefix,
2019-08-14 20:12:27 +00:00
self.server.projectVersion)
2019-08-10 10:54:52 +00:00
if hashtagStr:
2020-06-14 19:06:10 +00:00
msg = hashtagStr.encode('utf-8')
2020-04-02 21:35:06 +00:00
self._set_headers('text/html', len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-08-10 10:54:52 +00:00
else:
2020-04-02 21:35:06 +00:00
originPathStr = self.path.split('/tags/')[0]
originPathStrAbsolute = \
self.server.httpPrefix + '://' + \
self.server.domainFull + originPathStr
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
originPathStrAbsolute = 'http://' + \
self.server.onionDomain + originPathStr
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.onionDomain):
2020-06-19 09:50:00 +00:00
originPathStrAbsolute = 'http://' + \
2020-06-03 19:14:24 +00:00
self.server.i2pDomain + originPathStr
2020-04-02 21:35:06 +00:00
self._redirect_headers(originPathStrAbsolute + '/search',
cookie, callingDomain)
self.server.GETbusy = False
2019-08-10 10:54:52 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 28)
2019-11-16 13:25:44 +00:00
2020-05-23 14:23:56 +00:00
# show or hide buttons in the web interface
if htmlGET and '/users/' in self.path and \
self.path.endswith('/minimal') and \
authorized:
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
self._setMinimal(nickname, not self._isMinimal(nickname))
if not (self.server.mediaInstance or
self.server.blogsInstance):
self.path = '/users/' + nickname + '/inbox'
else:
if self.server.blogsInstance:
self.path = '/users/' + nickname + '/tlblogs'
else:
self.path = '/users/' + nickname + '/tlmedia'
2019-11-03 15:27:29 +00:00
# search for a fediverse address, shared item or emoji
# from the web interface by selecting search icon
2019-08-18 20:11:26 +00:00
if htmlGET and '/users/' in self.path:
2020-04-02 21:35:06 +00:00
if self.path.endswith('/search') or \
'/search?' in self.path:
if '?' in self.path:
self.path = self.path.split('?')[0]
# show the search screen
msg = htmlSearch(self.server.translate,
2020-06-10 12:53:16 +00:00
self.server.baseDir, self.path,
2020-06-14 19:06:10 +00:00
self.server.domain).encode('utf-8')
2020-04-02 21:35:06 +00:00
self._set_headers('text/html', len(msg), cookie, callingDomain)
self._write(msg)
self.server.GETbusy = False
return
self._benchmarkGETtimings(GETstartTime, GETtimings, 29)
2019-11-16 13:25:44 +00:00
2019-10-10 14:43:21 +00:00
# Show the calendar for a user
if htmlGET and '/users/' in self.path:
2020-04-02 21:35:06 +00:00
if '/calendar' in self.path:
# show the calendar screen
msg = htmlCalendar(self.server.translate,
self.server.baseDir, self.path,
self.server.httpPrefix,
2020-06-14 19:06:10 +00:00
self.server.domainFull).encode('utf-8')
2020-04-02 21:35:06 +00:00
self._set_headers('text/html', len(msg), cookie, callingDomain)
self._write(msg)
self.server.GETbusy = False
return
2019-07-30 22:34:04 +00:00
2020-02-23 12:20:54 +00:00
# Show confirmation for deleting a calendar event
if htmlGET and '/users/' in self.path:
2020-04-02 21:35:06 +00:00
if '/eventdelete' in self.path and \
'?time=' in self.path and \
'?id=' in self.path:
postId = self.path.split('?id=')[1]
if '?' in postId:
postId = postId.split('?')[0]
postTime = self.path.split('?time=')[1]
if '?' in postTime:
postTime = postTime.split('?')[0]
postYear = self.path.split('?year=')[1]
if '?' in postYear:
postYear = postYear.split('?')[0]
postMonth = self.path.split('?month=')[1]
if '?' in postMonth:
postMonth = postMonth.split('?')[0]
postDay = self.path.split('?day=')[1]
if '?' in postDay:
postDay = postDay.split('?')[0]
# show the confirmation screen screen
msg = htmlCalendarDeleteConfirm(self.server.translate,
self.server.baseDir,
self.path,
self.server.httpPrefix,
self.server.domainFull,
postId, postTime,
postYear, postMonth, postDay)
if not msg:
actor = \
self.server.httpPrefix + '://' + \
self.server.domainFull + \
self.path.split('/eventdelete')[0]
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actor = \
'http://' + self.server.onionDomain + \
self.path.split('/eventdelete')[0]
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
actor = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + \
2020-06-03 19:14:24 +00:00
self.path.split('/eventdelete')[0]
2020-04-02 21:35:06 +00:00
self._redirect_headers(actor + '/calendar',
cookie, callingDomain)
return
2020-06-14 19:06:10 +00:00
msg = msg.encode('utf-8')
2020-04-02 21:35:06 +00:00
self._set_headers('text/html', len(msg),
cookie, callingDomain)
self._write(msg)
self.server.GETbusy = False
return
self._benchmarkGETtimings(GETstartTime, GETtimings, 30)
2019-11-16 13:25:44 +00:00
2019-08-19 20:01:29 +00:00
# search for emoji by name
if htmlGET and '/users/' in self.path:
2020-04-02 21:35:06 +00:00
if self.path.endswith('/searchemoji'):
# show the search screen
msg = htmlSearchEmojiTextEntry(self.server.translate,
self.server.baseDir,
2020-06-14 19:06:10 +00:00
self.path).encode('utf-8')
2020-04-02 21:35:06 +00:00
self._set_headers('text/html', len(msg),
cookie, callingDomain)
self._write(msg)
self.server.GETbusy = False
return
self._benchmarkGETtimings(GETstartTime, GETtimings, 31)
repeatPrivate = False
2020-02-14 17:16:01 +00:00
if htmlGET and '?repeatprivate=' in self.path:
2020-04-02 21:35:06 +00:00
repeatPrivate = True
self.path = self.path.replace('?repeatprivate=', '?repeat=')
2019-08-01 09:05:09 +00:00
# announce/repeat from the web interface
2019-08-18 20:11:26 +00:00
if htmlGET and '?repeat=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumber = 1
repeatUrl = self.path.split('?repeat=')[1]
2019-09-04 11:29:44 +00:00
if '?' in repeatUrl:
2020-04-02 21:35:06 +00:00
repeatUrl = repeatUrl.split('?')[0]
timelineBookmark = ''
2019-11-19 15:27:43 +00:00
if '?bm=' in self.path:
2020-04-02 21:35:06 +00:00
timelineBookmark = self.path.split('?bm=')[1]
2019-11-19 15:27:43 +00:00
if '?' in timelineBookmark:
2020-04-02 21:35:06 +00:00
timelineBookmark = timelineBookmark.split('?')[0]
timelineBookmark = '#' + timelineBookmark
2019-09-04 11:29:44 +00:00
if '?page=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumberStr = self.path.split('?page=')[1]
2019-09-04 11:29:44 +00:00
if '?' in pageNumberStr:
2020-04-02 21:35:06 +00:00
pageNumberStr = pageNumberStr.split('?')[0]
2020-05-22 12:41:47 +00:00
if '#' in pageNumberStr:
pageNumberStr = pageNumberStr.split('#')[0]
2019-09-04 11:29:44 +00:00
if pageNumberStr.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumberStr)
timelineStr = 'inbox'
if '?tl=' in self.path:
2020-04-02 21:35:06 +00:00
timelineStr = self.path.split('?tl=')[1]
if '?' in timelineStr:
2020-04-02 21:35:06 +00:00
timelineStr = timelineStr.split('?')[0]
actor = self.path.split('?repeat=')[0]
self.postToNickname = getNicknameFromActor(actor)
2019-09-02 09:43:43 +00:00
if not self.postToNickname:
2020-04-02 21:35:06 +00:00
print('WARN: unable to find nickname in ' + actor)
self.server.GETbusy = False
actorAbsolute = \
self.server.httpPrefix + '://' + \
self.server.domainFull+actor
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actorAbsolute = 'http://' + self.server.onionDomain + actor
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
actorAbsolute = 'http://' + self.server.i2pDomain + actor
2020-04-02 21:35:06 +00:00
self._redirect_headers(actorAbsolute + '/' + timelineStr +
'?page=' + str(pageNumber), cookie,
2020-03-30 19:09:45 +00:00
callingDomain)
2020-03-22 21:16:02 +00:00
return
2019-07-31 16:47:45 +00:00
if not self.server.session:
2020-06-09 11:03:59 +00:00
self.server.session = createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to create session 7')
self._404()
self.server.GETbusy = False
2020-06-08 17:10:53 +00:00
return
2020-04-02 21:35:06 +00:00
self.server.actorRepeat = self.path.split('?actor=')[1]
announceToStr = \
self.server.httpPrefix + '://' + \
self.server.domain + '/users/' + \
self.postToNickname + '/followers'
2020-02-14 17:16:01 +00:00
if not repeatPrivate:
2020-04-02 21:35:06 +00:00
announceToStr = 'https://www.w3.org/ns/activitystreams#Public'
announceJson = \
createAnnounce(self.server.session,
self.server.baseDir,
self.server.federationList,
self.postToNickname,
self.server.domain, self.server.port,
announceToStr,
None, self.server.httpPrefix,
repeatUrl, False, False,
self.server.sendThreads,
self.server.postLog,
self.server.personCache,
self.server.cachedWebfingers,
self.server.debug,
2019-08-14 20:12:27 +00:00
self.server.projectVersion)
2019-11-14 21:12:09 +00:00
if announceJson:
2019-09-03 17:07:00 +00:00
self._postToOutboxThread(announceJson)
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
actorAbsolute = self.server.httpPrefix + '://' + \
self.server.domainFull + actor
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actorAbsolute = 'http://' + self.server.onionDomain + actor
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
actorAbsolute = 'http://' + self.server.i2pDomain + actor
2020-04-02 21:35:06 +00:00
self._redirect_headers(actorAbsolute + '/' +
timelineStr + '?page=' +
str(pageNumber) +
timelineBookmark, cookie, callingDomain)
2019-07-31 16:47:45 +00:00
return
2019-08-01 09:05:09 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 32)
2019-11-16 13:25:44 +00:00
2020-04-02 21:35:06 +00:00
# unrepeatPrivate = False
2020-02-14 17:16:01 +00:00
if htmlGET and '?unrepeatprivate=' in self.path:
2020-04-02 21:35:06 +00:00
self.path = self.path.replace('?unrepeatprivate=', '?unrepeat=')
# unrepeatPrivate = True
2019-08-01 12:18:22 +00:00
# undo an announce/repeat from the web interface
2019-08-18 20:11:26 +00:00
if htmlGET and '?unrepeat=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumber = 1
repeatUrl = self.path.split('?unrepeat=')[1]
2019-09-04 11:29:44 +00:00
if '?' in repeatUrl:
2020-04-02 21:35:06 +00:00
repeatUrl = repeatUrl.split('?')[0]
timelineBookmark = ''
2019-11-19 15:27:43 +00:00
if '?bm=' in self.path:
2020-04-02 21:35:06 +00:00
timelineBookmark = self.path.split('?bm=')[1]
2019-11-19 15:27:43 +00:00
if '?' in timelineBookmark:
2020-04-02 21:35:06 +00:00
timelineBookmark = timelineBookmark.split('?')[0]
timelineBookmark = '#' + timelineBookmark
2019-09-04 11:29:44 +00:00
if '?page=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumberStr = self.path.split('?page=')[1]
2019-09-04 11:29:44 +00:00
if '?' in pageNumberStr:
2020-04-02 21:35:06 +00:00
pageNumberStr = pageNumberStr.split('?')[0]
2020-05-22 12:41:47 +00:00
if '#' in pageNumberStr:
pageNumberStr = pageNumberStr.split('#')[0]
2019-09-04 11:29:44 +00:00
if pageNumberStr.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumberStr)
timelineStr = 'inbox'
if '?tl=' in self.path:
2020-04-02 21:35:06 +00:00
timelineStr = self.path.split('?tl=')[1]
if '?' in timelineStr:
2020-04-02 21:35:06 +00:00
timelineStr = timelineStr.split('?')[0]
actor = self.path.split('?unrepeat=')[0]
self.postToNickname = getNicknameFromActor(actor)
2019-09-02 09:43:43 +00:00
if not self.postToNickname:
2020-04-02 21:35:06 +00:00
print('WARN: unable to find nickname in ' + actor)
self.server.GETbusy = False
actorAbsolute = self.server.httpPrefix + '://' + \
self.server.domainFull + actor
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
2020-06-03 19:14:24 +00:00
actorAbsolute = 'http://' + self.server.onionDomain + actor
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
actorAbsolute = 'http://' + self.server.i2pDomain + actor
2020-04-02 21:35:06 +00:00
self._redirect_headers(actorAbsolute + '/' +
timelineStr + '?page=' +
str(pageNumber), cookie,
callingDomain)
2020-03-22 21:16:02 +00:00
return
2019-08-01 12:18:22 +00:00
if not self.server.session:
2020-06-09 11:03:59 +00:00
self.server.session = createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to create session 8')
self._404()
self.server.GETbusy = False
2020-06-08 17:10:53 +00:00
return
2020-04-02 21:35:06 +00:00
undoAnnounceActor = \
self.server.httpPrefix + '://' + self.server.domainFull + \
'/users/' + self.postToNickname
unRepeatToStr = 'https://www.w3.org/ns/activitystreams#Public'
newUndoAnnounce = {
2019-08-18 11:07:06 +00:00
"@context": "https://www.w3.org/ns/activitystreams",
2019-08-01 12:18:22 +00:00
'actor': undoAnnounceActor,
'type': 'Undo',
'cc': [undoAnnounceActor+'/followers'],
2020-02-14 17:16:01 +00:00
'to': [unRepeatToStr],
2019-08-01 12:18:22 +00:00
'object': {
'actor': undoAnnounceActor,
'cc': [undoAnnounceActor+'/followers'],
'object': repeatUrl,
2020-02-14 17:16:01 +00:00
'to': [unRepeatToStr],
2019-08-01 12:18:22 +00:00
'type': 'Announce'
}
2020-03-22 21:16:02 +00:00
}
2019-09-03 17:07:00 +00:00
self._postToOutboxThread(newUndoAnnounce)
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
actorAbsolute = self.server.httpPrefix + '://' + \
self.server.domainFull + actor
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actorAbsolute = 'http://' + self.server.onionDomain + actor
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.onionDomain):
2020-06-19 09:50:00 +00:00
actorAbsolute = 'http://' + self.server.i2pDomain + actor
2020-04-02 21:35:06 +00:00
self._redirect_headers(actorAbsolute + '/' +
timelineStr + '?page=' +
str(pageNumber) +
timelineBookmark, cookie, callingDomain)
2019-08-01 12:18:22 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 33)
2019-11-16 13:25:44 +00:00
2019-08-07 11:58:01 +00:00
# send a follow request approval from the web interface
if authorized and '/followapprove=' in self.path and \
2019-11-03 15:27:29 +00:00
self.path.startswith('/users/'):
2020-04-02 21:35:06 +00:00
originPathStr = self.path.split('/followapprove=')[0]
followerNickname = originPathStr.replace('/users/', '')
followingHandle = self.path.split('/followapprove=')[1]
2019-08-07 11:57:14 +00:00
if '@' in followingHandle:
2019-08-07 12:50:48 +00:00
if not self.server.session:
2020-06-09 11:03:59 +00:00
self.server.session = createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to create session 9')
self._404()
self.server.GETbusy = False
2020-06-08 17:10:53 +00:00
return
2020-04-02 21:35:06 +00:00
manualApproveFollowRequest(self.server.session,
self.server.baseDir,
self.server.httpPrefix,
followerNickname,
self.server.domain,
self.server.port,
followingHandle,
self.server.federationList,
self.server.sendThreads,
self.server.postLog,
self.server.cachedWebfingers,
self.server.personCache,
self.server.acceptedCaps,
self.server.debug,
2019-08-14 20:12:27 +00:00
self.server.projectVersion)
2020-04-02 21:35:06 +00:00
originPathStrAbsolute = \
self.server.httpPrefix + '://' + \
self.server.domainFull + originPathStr
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
originPathStrAbsolute = \
'http://' + self.server.onionDomain + originPathStr
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
originPathStrAbsolute = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + originPathStr
2020-04-02 21:35:06 +00:00
self._redirect_headers(originPathStrAbsolute,
cookie, callingDomain)
self.server.GETbusy = False
2019-08-07 11:57:14 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 34)
2019-11-16 13:25:44 +00:00
2019-08-07 11:58:01 +00:00
# deny a follow request from the web interface
if authorized and '/followdeny=' in self.path and \
2019-11-03 15:27:29 +00:00
self.path.startswith('/users/'):
2020-04-02 21:35:06 +00:00
originPathStr = self.path.split('/followdeny=')[0]
followerNickname = originPathStr.replace('/users/', '')
followingHandle = self.path.split('/followdeny=')[1]
2019-08-07 11:57:14 +00:00
if '@' in followingHandle:
2020-04-02 21:35:06 +00:00
manualDenyFollowRequest(self.server.session,
self.server.baseDir,
self.server.httpPrefix,
followerNickname,
self.server.domain,
self.server.port,
followingHandle,
self.server.federationList,
self.server.sendThreads,
self.server.postLog,
self.server.cachedWebfingers,
self.server.personCache,
self.server.debug,
self.server.projectVersion)
2020-04-02 21:35:06 +00:00
originPathStrAbsolute = \
self.server.httpPrefix + '://' + \
self.server.domainFull + originPathStr
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
originPathStrAbsolute = 'http://' + \
self.server.onionDomain + originPathStr
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
originPathStrAbsolute = 'http://' + \
2020-06-03 19:14:24 +00:00
self.server.i2pDomain + originPathStr
2020-04-02 21:35:06 +00:00
self._redirect_headers(originPathStrAbsolute,
cookie, callingDomain)
self.server.GETbusy = False
2019-08-07 11:57:14 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 35)
2019-11-16 13:25:44 +00:00
2019-08-01 09:05:09 +00:00
# like from the web interface icon
2019-11-14 17:58:46 +00:00
if htmlGET and '?like=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumber = 1
likeUrl = self.path.split('?like=')[1]
2019-09-04 11:29:44 +00:00
if '?' in likeUrl:
2020-04-02 21:35:06 +00:00
likeUrl = likeUrl.split('?')[0]
timelineBookmark = ''
2019-11-19 15:27:43 +00:00
if '?bm=' in self.path:
2020-04-02 21:35:06 +00:00
timelineBookmark = self.path.split('?bm=')[1]
2019-11-19 15:27:43 +00:00
if '?' in timelineBookmark:
2020-04-02 21:35:06 +00:00
timelineBookmark = timelineBookmark.split('?')[0]
timelineBookmark = '#' + timelineBookmark
actor = self.path.split('?like=')[0]
2019-09-04 11:29:44 +00:00
if '?page=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumberStr = self.path.split('?page=')[1]
2019-09-04 11:29:44 +00:00
if '?' in pageNumberStr:
2020-04-02 21:35:06 +00:00
pageNumberStr = pageNumberStr.split('?')[0]
2020-05-22 12:41:47 +00:00
if '#' in pageNumberStr:
pageNumberStr = pageNumberStr.split('#')[0]
2019-09-04 11:29:44 +00:00
if pageNumberStr.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumberStr)
timelineStr = 'inbox'
if '?tl=' in self.path:
2020-04-02 21:35:06 +00:00
timelineStr = self.path.split('?tl=')[1]
if '?' in timelineStr:
2020-04-02 21:35:06 +00:00
timelineStr = timelineStr.split('?')[0]
2020-03-22 21:16:02 +00:00
2020-04-02 21:35:06 +00:00
self.postToNickname = getNicknameFromActor(actor)
2019-09-02 09:43:43 +00:00
if not self.postToNickname:
2020-04-02 21:35:06 +00:00
print('WARN: unable to find nickname in ' + actor)
self.server.GETbusy = False
actorAbsolute = \
self.server.httpPrefix + '://' + \
self.server.domainFull+actor
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actorAbsolute = 'http://' + self.server.onionDomain + actor
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
actorAbsolute = 'http://' + self.server.i2pDomain + actor
2020-04-02 21:35:06 +00:00
self._redirect_headers(actorAbsolute + '/' + timelineStr +
'?page=' + str(pageNumber) +
timelineBookmark, cookie,
2020-03-28 17:24:40 +00:00
callingDomain)
2020-03-22 21:16:02 +00:00
return
2019-08-01 09:05:09 +00:00
if not self.server.session:
2020-06-09 11:03:59 +00:00
self.server.session = createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to create session 10')
self._404()
self.server.GETbusy = False
2020-06-08 17:10:53 +00:00
return
2020-04-02 21:35:06 +00:00
likeActor = \
self.server.httpPrefix + '://' + \
self.server.domainFull + '/users/' + self.postToNickname
actorLiked = self.path.split('?actor=')[1]
if '?' in actorLiked:
2020-04-02 21:35:06 +00:00
actorLiked = actorLiked.split('?')[0]
likeJson = {
2019-08-18 11:07:06 +00:00
"@context": "https://www.w3.org/ns/activitystreams",
2019-08-01 09:05:09 +00:00
'type': 'Like',
'actor': likeActor,
'to': [actorLiked],
2019-08-18 16:58:50 +00:00
'object': likeUrl
2020-03-22 21:16:02 +00:00
}
# directly like the post file
likedPostFilename = locatePost(self.server.baseDir,
self.postToNickname,
self.server.domain,
2020-06-07 09:10:10 +00:00
likeUrl)
if likedPostFilename:
2020-06-07 09:18:17 +00:00
if self.server.debug:
print('Updating likes for ' + likedPostFilename)
2020-06-06 18:16:16 +00:00
updateLikesCollection(self.server.recentPostsCache,
self.server.baseDir,
likedPostFilename, likeUrl,
likeActor, self.server.domain,
self.server.debug)
2020-06-07 08:53:34 +00:00
else:
print('WARN: unable to locate file for liked post ' +
likeUrl)
# send out the like to followers
2020-04-02 21:35:06 +00:00
self._postToOutbox(likeJson, self.server.projectVersion)
self.server.GETbusy = False
actorAbsolute = \
self.server.httpPrefix + '://' + \
self.server.domainFull + actor
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actorAbsolute = 'http://' + self.server.onionDomain + actor
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
actorAbsolute = 'http://' + self.server.i2pDomain + actor
2020-04-02 21:35:06 +00:00
self._redirect_headers(actorAbsolute + '/' + timelineStr +
'?page=' + str(pageNumber) +
timelineBookmark, cookie,
2020-03-28 17:24:40 +00:00
callingDomain)
2019-08-01 09:05:09 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 36)
2019-11-16 13:25:44 +00:00
2019-08-01 09:05:09 +00:00
# undo a like from the web interface icon
2019-11-14 17:58:46 +00:00
if htmlGET and '?unlike=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumber = 1
likeUrl = self.path.split('?unlike=')[1]
2019-09-04 11:29:44 +00:00
if '?' in likeUrl:
2020-04-02 21:35:06 +00:00
likeUrl = likeUrl.split('?')[0]
timelineBookmark = ''
2019-11-19 15:27:43 +00:00
if '?bm=' in self.path:
2020-04-02 21:35:06 +00:00
timelineBookmark = self.path.split('?bm=')[1]
2019-11-19 15:27:43 +00:00
if '?' in timelineBookmark:
2020-04-02 21:35:06 +00:00
timelineBookmark = timelineBookmark.split('?')[0]
timelineBookmark = '#' + timelineBookmark
2019-09-04 11:29:44 +00:00
if '?page=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumberStr = self.path.split('?page=')[1]
2019-09-04 11:29:44 +00:00
if '?' in pageNumberStr:
2020-04-02 21:35:06 +00:00
pageNumberStr = pageNumberStr.split('?')[0]
2020-05-22 12:41:47 +00:00
if '#' in pageNumberStr:
pageNumberStr = pageNumberStr.split('#')[0]
2019-09-04 11:29:44 +00:00
if pageNumberStr.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumberStr)
timelineStr = 'inbox'
if '?tl=' in self.path:
2020-04-02 21:35:06 +00:00
timelineStr = self.path.split('?tl=')[1]
if '?' in timelineStr:
2020-04-02 21:35:06 +00:00
timelineStr = timelineStr.split('?')[0]
actor = self.path.split('?unlike=')[0]
self.postToNickname = getNicknameFromActor(actor)
2019-09-02 09:43:43 +00:00
if not self.postToNickname:
2020-04-02 21:35:06 +00:00
print('WARN: unable to find nickname in ' + actor)
self.server.GETbusy = False
actorAbsolute = \
self.server.httpPrefix + '://' + \
self.server.domainFull + actor
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actorAbsolute = 'http://' + self.server.onionDomain + actor
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.onionDomain):
2020-06-19 09:50:00 +00:00
actorAbsolute = 'http://' + self.server.i2pDomain + actor
2020-04-02 21:35:06 +00:00
self._redirect_headers(actorAbsolute + '/' + timelineStr +
'?page=' + str(pageNumber), cookie,
2020-03-28 17:24:40 +00:00
callingDomain)
2020-03-22 21:16:02 +00:00
return
2019-08-01 09:05:09 +00:00
if not self.server.session:
2020-06-09 11:03:59 +00:00
self.server.session = createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to create session 11')
self._404()
self.server.GETbusy = False
2020-06-08 17:10:53 +00:00
return
2020-04-02 21:35:06 +00:00
undoActor = \
self.server.httpPrefix + '://' + \
self.server.domainFull + '/users/' + self.postToNickname
actorLiked = self.path.split('?actor=')[1]
if '?' in actorLiked:
2020-04-02 21:35:06 +00:00
actorLiked = actorLiked.split('?')[0]
undoLikeJson = {
2019-08-18 11:07:06 +00:00
"@context": "https://www.w3.org/ns/activitystreams",
2019-08-01 09:05:09 +00:00
'type': 'Undo',
'actor': undoActor,
'to': [actorLiked],
2019-08-01 09:05:09 +00:00
'object': {
'type': 'Like',
'actor': undoActor,
'to': [actorLiked],
2019-08-18 16:58:50 +00:00
'object': likeUrl
}
2019-08-01 12:49:16 +00:00
}
# directly undo the like within the post file
likedPostFilename = locatePost(self.server.baseDir,
self.postToNickname,
self.server.domain,
2020-06-07 09:10:10 +00:00
likeUrl)
if likedPostFilename:
2020-06-07 09:18:17 +00:00
if self.server.debug:
print('Removing likes for ' + likedPostFilename)
undoLikesCollectionEntry(self.server.recentPostsCache,
self.server.baseDir,
likedPostFilename, likeUrl,
2020-06-07 09:14:20 +00:00
undoActor, self.server.domain,
self.server.debug)
# send out the undo like to followers
2020-04-02 21:35:06 +00:00
self._postToOutbox(undoLikeJson, self.server.projectVersion)
self.server.GETbusy = False
actorAbsolute = self.server.httpPrefix + '://' + \
self.server.domainFull+actor
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actorAbsolute = 'http://' + self.server.onionDomain + actor
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.onionDomain):
2020-06-19 09:50:00 +00:00
actorAbsolute = 'http://' + self.server.i2pDomain + actor
2020-04-02 21:35:06 +00:00
self._redirect_headers(actorAbsolute + '/' + timelineStr +
'?page=' + str(pageNumber) +
timelineBookmark, cookie,
2020-03-28 17:24:40 +00:00
callingDomain)
2019-08-04 18:29:26 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 36)
2019-11-17 14:01:49 +00:00
# bookmark from the web interface icon
if htmlGET and '?bookmark=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumber = 1
bookmarkUrl = self.path.split('?bookmark=')[1]
2019-11-17 14:01:49 +00:00
if '?' in bookmarkUrl:
2020-04-02 21:35:06 +00:00
bookmarkUrl = bookmarkUrl.split('?')[0]
timelineBookmark = ''
2019-11-19 15:27:43 +00:00
if '?bm=' in self.path:
2020-04-02 21:35:06 +00:00
timelineBookmark = self.path.split('?bm=')[1]
2019-11-19 15:27:43 +00:00
if '?' in timelineBookmark:
2020-04-02 21:35:06 +00:00
timelineBookmark = timelineBookmark.split('?')[0]
timelineBookmark = '#' + timelineBookmark
actor = self.path.split('?bookmark=')[0]
2019-11-17 14:01:49 +00:00
if '?page=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumberStr = self.path.split('?page=')[1]
2019-11-17 14:01:49 +00:00
if '?' in pageNumberStr:
2020-04-02 21:35:06 +00:00
pageNumberStr = pageNumberStr.split('?')[0]
2020-05-22 12:41:47 +00:00
if '#' in pageNumberStr:
pageNumberStr = pageNumberStr.split('#')[0]
2019-11-17 14:01:49 +00:00
if pageNumberStr.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumberStr)
timelineStr = 'inbox'
2019-11-17 14:01:49 +00:00
if '?tl=' in self.path:
2020-04-02 21:35:06 +00:00
timelineStr = self.path.split('?tl=')[1]
2019-11-17 14:01:49 +00:00
if '?' in timelineStr:
2020-04-02 21:35:06 +00:00
timelineStr = timelineStr.split('?')[0]
2019-11-17 14:01:49 +00:00
2020-04-02 21:35:06 +00:00
self.postToNickname = getNicknameFromActor(actor)
2019-11-17 14:01:49 +00:00
if not self.postToNickname:
2020-04-02 21:35:06 +00:00
print('WARN: unable to find nickname in ' + actor)
self.server.GETbusy = False
actorAbsolute = \
self.server.httpPrefix + '://' + \
self.server.domainFull+actor
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actorAbsolute = 'http://' + self.server.onionDomain + actor
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
actorAbsolute = 'http://' + self.server.i2pDomain + actor
2020-04-02 21:35:06 +00:00
self._redirect_headers(actorAbsolute + '/' + timelineStr +
'?page=' + str(pageNumber), cookie,
2020-03-28 17:24:40 +00:00
callingDomain)
2020-03-22 21:16:02 +00:00
return
2019-11-17 14:01:49 +00:00
if not self.server.session:
2020-06-09 11:03:59 +00:00
self.server.session = createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to create session 12')
self._404()
self.server.GETbusy = False
2020-06-08 17:10:53 +00:00
return
2020-04-02 21:35:06 +00:00
bookmarkActor = \
self.server.httpPrefix + '://' + \
self.server.domainFull + '/users/' + self.postToNickname
2020-05-21 21:23:34 +00:00
ccList = []
bookmark(self.server.recentPostsCache,
self.server.session,
self.server.baseDir,
self.server.federationList,
self.postToNickname,
self.server.domain, self.server.port,
ccList,
self.server.httpPrefix,
bookmarkUrl, bookmarkActor, False,
self.server.sendThreads,
self.server.postLog,
self.server.personCache,
self.server.cachedWebfingers,
self.server.debug,
self.server.projectVersion)
# self._postToOutbox(bookmarkJson, self.server.projectVersion)
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
actorAbsolute = \
self.server.httpPrefix + '://' + self.server.domainFull + actor
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actorAbsolute = 'http://' + self.server.onionDomain + actor
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
actorAbsolute = 'http://' + self.server.i2pDomain + actor
2020-04-02 21:35:06 +00:00
self._redirect_headers(actorAbsolute + '/' + timelineStr +
'?page=' + str(pageNumber) +
timelineBookmark, cookie,
2020-03-28 17:24:40 +00:00
callingDomain)
2019-11-17 14:01:49 +00:00
return
# undo a bookmark from the web interface icon
if htmlGET and '?unbookmark=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumber = 1
bookmarkUrl = self.path.split('?unbookmark=')[1]
2019-11-17 14:01:49 +00:00
if '?' in bookmarkUrl:
2020-04-02 21:35:06 +00:00
bookmarkUrl = bookmarkUrl.split('?')[0]
timelineBookmark = ''
2019-11-19 15:27:43 +00:00
if '?bm=' in self.path:
2020-04-02 21:35:06 +00:00
timelineBookmark = self.path.split('?bm=')[1]
2019-11-19 15:27:43 +00:00
if '?' in timelineBookmark:
2020-04-02 21:35:06 +00:00
timelineBookmark = timelineBookmark.split('?')[0]
timelineBookmark = '#' + timelineBookmark
2019-11-17 14:01:49 +00:00
if '?page=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumberStr = self.path.split('?page=')[1]
2019-11-17 14:01:49 +00:00
if '?' in pageNumberStr:
2020-04-02 21:35:06 +00:00
pageNumberStr = pageNumberStr.split('?')[0]
2020-05-22 12:41:47 +00:00
if '#' in pageNumberStr:
pageNumberStr = pageNumberStr.split('#')[0]
2019-11-17 14:01:49 +00:00
if pageNumberStr.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumberStr)
timelineStr = 'inbox'
2019-11-17 14:01:49 +00:00
if '?tl=' in self.path:
2020-04-02 21:35:06 +00:00
timelineStr = self.path.split('?tl=')[1]
2019-11-17 14:01:49 +00:00
if '?' in timelineStr:
2020-04-02 21:35:06 +00:00
timelineStr = timelineStr.split('?')[0]
actor = self.path.split('?unbookmark=')[0]
self.postToNickname = getNicknameFromActor(actor)
2019-11-17 14:01:49 +00:00
if not self.postToNickname:
2020-04-02 21:35:06 +00:00
print('WARN: unable to find nickname in ' + actor)
self.server.GETbusy = False
actorAbsolute = \
self.server.httpPrefix + '://' + \
self.server.domainFull + actor
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actorAbsolute = 'http://' + \
self.server.onionDomain + actor
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
actorAbsolute = 'http://' + self.server.i2pDomain + actor
2020-04-02 21:35:06 +00:00
self._redirect_headers(actorAbsolute + '/' + timelineStr +
'?page=' + str(pageNumber), cookie,
2020-03-28 17:24:40 +00:00
callingDomain)
2020-03-22 21:16:02 +00:00
return
2019-11-17 14:01:49 +00:00
if not self.server.session:
2020-06-09 11:03:59 +00:00
self.server.session = createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to create session 13')
self._404()
self.server.GETbusy = False
2020-06-08 17:10:53 +00:00
return
2020-04-02 21:35:06 +00:00
undoActor = \
self.server.httpPrefix + '://' + \
self.server.domainFull + '/users/' + self.postToNickname
2020-05-21 21:23:34 +00:00
ccList = []
undoBookmark(self.server.recentPostsCache,
self.server.session,
self.server.baseDir,
self.server.federationList,
self.postToNickname,
self.server.domain, self.server.port,
ccList,
self.server.httpPrefix,
bookmarkUrl, undoActor, False,
self.server.sendThreads,
self.server.postLog,
self.server.personCache,
self.server.cachedWebfingers,
self.server.debug,
self.server.projectVersion)
# self._postToOutbox(undoBookmarkJson, self.server.projectVersion)
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
actorAbsolute = \
self.server.httpPrefix + '://' + self.server.domainFull + actor
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actorAbsolute = 'http://' + self.server.onionDomain + actor
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
actorAbsolute = 'http://' + self.server.i2pDomain + actor
2020-04-02 21:35:06 +00:00
self._redirect_headers(actorAbsolute + '/' + timelineStr +
'?page=' + str(pageNumber) +
timelineBookmark, cookie,
2020-03-28 17:24:40 +00:00
callingDomain)
2019-11-17 14:01:49 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 37)
2019-11-16 13:25:44 +00:00
2019-08-04 18:29:26 +00:00
# delete a post from the web interface icon
2019-08-18 20:11:26 +00:00
if htmlGET and '?delete=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumber = 1
2019-09-04 11:29:44 +00:00
if '?page=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumberStr = self.path.split('?page=')[1]
2020-03-22 21:16:02 +00:00
if '?' in pageNumberStr:
2020-04-02 21:35:06 +00:00
pageNumberStr = pageNumberStr.split('?')[0]
2020-05-22 12:41:47 +00:00
if '#' in pageNumberStr:
pageNumberStr = pageNumberStr.split('#')[0]
2019-09-04 11:29:44 +00:00
if pageNumberStr.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumberStr)
deleteUrl = self.path.split('?delete=')[1]
2020-03-22 21:16:02 +00:00
if '?' in deleteUrl:
2020-04-02 21:35:06 +00:00
deleteUrl = deleteUrl.split('?')[0]
timelineStr = self.server.defaultTimeline
2019-12-01 13:45:30 +00:00
if '?tl=' in self.path:
2020-04-02 21:35:06 +00:00
timelineStr = self.path.split('?tl=')[1]
2019-12-01 13:45:30 +00:00
if '?' in timelineStr:
2020-04-02 21:35:06 +00:00
timelineStr = timelineStr.split('?')[0]
usersPath = self.path.split('?delete=')[0]
actor = \
self.server.httpPrefix + '://' + \
self.server.domainFull + usersPath
2019-08-12 18:02:29 +00:00
if self.server.allowDeletion or \
deleteUrl.startswith(actor):
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: deleteUrl=' + deleteUrl)
print('DEBUG: actor=' + actor)
2019-08-12 18:02:29 +00:00
if actor not in deleteUrl:
# You can only delete your own posts
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actor = 'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
actor = 'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
self._redirect_headers(actor + '/' + timelineStr,
cookie, callingDomain)
2019-08-12 18:02:29 +00:00
return
2020-04-02 21:35:06 +00:00
self.postToNickname = getNicknameFromActor(actor)
2019-09-02 09:43:43 +00:00
if not self.postToNickname:
2020-04-02 21:35:06 +00:00
print('WARN: unable to find nickname in ' + actor)
self.server.GETbusy = False
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actor = 'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
actor = 'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
self._redirect_headers(actor + '/' + timelineStr,
cookie, callingDomain)
2020-03-22 21:16:02 +00:00
return
2019-08-12 18:02:29 +00:00
if not self.server.session:
2020-06-09 11:03:59 +00:00
self.server.session = createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to create session 14')
self._404()
self.server.GETbusy = False
2020-06-08 17:10:53 +00:00
return
2020-04-02 21:35:06 +00:00
deleteStr = \
htmlDeletePost(self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.translate, pageNumber,
self.server.session, self.server.baseDir,
deleteUrl, self.server.httpPrefix,
__version__, self.server.cachedWebfingers,
2019-08-27 12:47:11 +00:00
self.server.personCache)
if deleteStr:
2020-04-02 21:35:06 +00:00
self._set_headers('text/html', len(deleteStr),
cookie, callingDomain)
2020-06-14 19:06:10 +00:00
self._write(deleteStr.encode('utf-8'))
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-08-27 12:47:11 +00:00
return
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actor = 'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
actor = 'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
self._redirect_headers(actor + '/' + timelineStr,
cookie, callingDomain)
2019-12-01 13:45:30 +00:00
return
# mute a post from the web interface icon
if htmlGET and '?mute=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumber = 1
2019-12-01 13:45:30 +00:00
if '?page=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumberStr = self.path.split('?page=')[1]
2020-03-22 21:16:02 +00:00
if '?' in pageNumberStr:
2020-04-02 21:35:06 +00:00
pageNumberStr = pageNumberStr.split('?')[0]
2020-05-22 12:41:47 +00:00
if '#' in pageNumberStr:
pageNumberStr = pageNumberStr.split('#')[0]
2019-12-01 13:45:30 +00:00
if pageNumberStr.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumberStr)
muteUrl = self.path.split('?mute=')[1]
2020-03-22 21:16:02 +00:00
if '?' in muteUrl:
2020-04-02 21:35:06 +00:00
muteUrl = muteUrl.split('?')[0]
timelineBookmark = ''
2019-12-01 15:28:23 +00:00
if '?bm=' in self.path:
2020-04-02 21:35:06 +00:00
timelineBookmark = self.path.split('?bm=')[1]
2019-12-01 15:28:23 +00:00
if '?' in timelineBookmark:
2020-04-02 21:35:06 +00:00
timelineBookmark = timelineBookmark.split('?')[0]
timelineBookmark = '#' + timelineBookmark
timelineStr = self.server.defaultTimeline
2019-12-01 13:45:30 +00:00
if '?tl=' in self.path:
2020-04-02 21:35:06 +00:00
timelineStr = self.path.split('?tl=')[1]
2019-12-01 13:45:30 +00:00
if '?' in timelineStr:
2020-04-02 21:35:06 +00:00
timelineStr = timelineStr.split('?')[0]
actor = \
self.server.httpPrefix + '://' + \
self.server.domainFull + self.path.split('?mute=')[0]
nickname = getNicknameFromActor(actor)
mutePost(self.server.baseDir, nickname, self.server.domain,
muteUrl, self.server.recentPostsCache)
self.server.GETbusy = False
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actor = \
'http://' + self.server.onionDomain + \
self.path.split('?mute=')[0]
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
actor = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + \
2020-06-03 19:14:24 +00:00
self.path.split('?mute=')[0]
2020-04-02 21:35:06 +00:00
self._redirect_headers(actor + '/' +
timelineStr + timelineBookmark,
cookie, callingDomain)
2019-12-01 13:45:30 +00:00
return
# unmute a post from the web interface icon
if htmlGET and '?unmute=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumber = 1
2019-12-01 13:45:30 +00:00
if '?page=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumberStr = self.path.split('?page=')[1]
2020-03-22 21:16:02 +00:00
if '?' in pageNumberStr:
2020-04-02 21:35:06 +00:00
pageNumberStr = pageNumberStr.split('?')[0]
2020-05-22 12:41:47 +00:00
if '#' in pageNumberStr:
pageNumberStr = pageNumberStr.split('#')[0]
2019-12-01 13:45:30 +00:00
if pageNumberStr.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumberStr)
muteUrl = self.path.split('?unmute=')[1]
2020-03-22 21:16:02 +00:00
if '?' in muteUrl:
2020-04-02 21:35:06 +00:00
muteUrl = muteUrl.split('?')[0]
timelineBookmark = ''
2019-12-01 15:28:23 +00:00
if '?bm=' in self.path:
2020-04-02 21:35:06 +00:00
timelineBookmark = self.path.split('?bm=')[1]
2019-12-01 15:28:23 +00:00
if '?' in timelineBookmark:
2020-04-02 21:35:06 +00:00
timelineBookmark = timelineBookmark.split('?')[0]
timelineBookmark = '#' + timelineBookmark
timelineStr = self.server.defaultTimeline
2019-12-01 13:45:30 +00:00
if '?tl=' in self.path:
2020-04-02 21:35:06 +00:00
timelineStr = self.path.split('?tl=')[1]
2019-12-01 13:45:30 +00:00
if '?' in timelineStr:
2020-04-02 21:35:06 +00:00
timelineStr = timelineStr.split('?')[0]
actor = \
self.server.httpPrefix + '://' + \
self.server.domainFull + self.path.split('?unmute=')[0]
nickname = getNicknameFromActor(actor)
unmutePost(self.server.baseDir,
nickname,
self.server.domain,
muteUrl,
self.server.recentPostsCache)
self.server.GETbusy = False
2020-06-03 19:14:24 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
2020-04-02 21:35:06 +00:00
actor = \
'http://' + \
self.server.onionDomain + self.path.split('?unmute=')[0]
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
actor = \
2020-06-19 09:50:00 +00:00
'http://' + \
2020-06-03 19:14:24 +00:00
self.server.i2pDomain + self.path.split('?unmute=')[0]
2020-04-02 21:35:06 +00:00
self._redirect_headers(actor + '/' + timelineStr +
timelineBookmark,
cookie, callingDomain)
2019-08-01 09:05:09 +00:00
return
# reply from the web interface icon
2020-04-02 21:35:06 +00:00
inReplyToUrl = None
# replyWithDM = False
replyToList = []
replyPageNumber = 1
shareDescription = None
# replytoActor = None
if htmlGET:
# public reply
if '?replyto=' in self.path:
2020-04-02 21:35:06 +00:00
inReplyToUrl = self.path.split('?replyto=')[1]
if '?' in inReplyToUrl:
2020-04-02 21:35:06 +00:00
mentionsList = inReplyToUrl.split('?')
for m in mentionsList:
if m.startswith('mention='):
2020-04-02 21:35:06 +00:00
replyHandle = m.replace('mention=', '')
2019-09-22 17:48:52 +00:00
if replyHandle not in replyToList:
replyToList.append(replyHandle)
2019-09-04 11:29:44 +00:00
if m.startswith('page='):
2020-04-02 21:35:06 +00:00
replyPageStr = m.replace('page=', '')
2019-09-04 11:29:44 +00:00
if replyPageStr.isdigit():
2020-04-02 21:35:06 +00:00
replyPageNumber = int(replyPageStr)
# if m.startswith('actor='):
# replytoActor = m.replace('actor=', '')
inReplyToUrl = mentionsList[0]
self.path = self.path.split('?replyto=')[0] + '/newpost'
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: replyto path ' + self.path)
# reply to followers
if '?replyfollowers=' in self.path:
2020-04-02 21:35:06 +00:00
inReplyToUrl = self.path.split('?replyfollowers=')[1]
if '?' in inReplyToUrl:
2020-04-02 21:35:06 +00:00
mentionsList = inReplyToUrl.split('?')
for m in mentionsList:
if m.startswith('mention='):
2020-04-02 21:35:06 +00:00
replyHandle = m.replace('mention=', '')
if m.replace('mention=', '') not in replyToList:
2019-09-22 17:48:52 +00:00
replyToList.append(replyHandle)
2019-09-04 11:29:44 +00:00
if m.startswith('page='):
2020-04-02 21:35:06 +00:00
replyPageStr = m.replace('page=', '')
2019-09-04 11:29:44 +00:00
if replyPageStr.isdigit():
2020-04-02 21:35:06 +00:00
replyPageNumber = int(replyPageStr)
# if m.startswith('actor='):
# replytoActor = m.replace('actor=', '')
inReplyToUrl = mentionsList[0]
self.path = self.path.split('?replyfollowers=')[0] + \
'/newfollowers'
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: replyfollowers path ' + self.path)
2019-08-02 09:52:12 +00:00
2020-04-02 21:35:06 +00:00
# replying as a direct message,
# for moderation posts or the dm timeline
if '?replydm=' in self.path:
2020-04-02 21:35:06 +00:00
inReplyToUrl = self.path.split('?replydm=')[1]
if '?' in inReplyToUrl:
2020-04-02 21:35:06 +00:00
mentionsList = inReplyToUrl.split('?')
for m in mentionsList:
if m.startswith('mention='):
2020-04-02 21:35:06 +00:00
replyHandle = m.replace('mention=', '')
if m.replace('mention=', '') not in replyToList:
replyToList.append(m.replace('mention=', ''))
2019-09-04 11:29:44 +00:00
if m.startswith('page='):
2020-04-02 21:35:06 +00:00
replyPageStr = m.replace('page=', '')
2019-09-04 11:29:44 +00:00
if replyPageStr.isdigit():
2020-04-02 21:35:06 +00:00
replyPageNumber = int(replyPageStr)
# if m.startswith('actor='):
# replytoActor = m.replace('actor=', '')
inReplyToUrl = mentionsList[0]
if inReplyToUrl.startswith('sharedesc:'):
2020-04-02 21:35:06 +00:00
shareDescription = \
inReplyToUrl.replace('sharedesc:', '')
2020-04-15 10:57:04 +00:00
shareDescription = \
urllib.parse.unquote(shareDescription.strip())
2020-04-02 21:35:06 +00:00
self.path = self.path.split('?replydm=')[0]+'/newdm'
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: replydm path ' + self.path)
2020-03-01 11:05:20 +00:00
# Edit a blog post
2020-03-01 11:15:27 +00:00
if authorized and \
2020-03-01 12:43:20 +00:00
'/tlblogs' in self.path and \
2020-03-01 11:15:27 +00:00
'?editblogpost=' in self.path and \
2020-03-01 11:05:20 +00:00
'?actor=' in self.path:
2020-04-02 21:35:06 +00:00
messageId = self.path.split('?editblogpost=')[1]
2020-03-01 11:05:20 +00:00
if '?' in messageId:
2020-04-02 21:35:06 +00:00
messageId = messageId.split('?')[0]
actor = self.path.split('?actor=')[1]
2020-03-01 11:05:20 +00:00
if '?' in actor:
2020-04-02 21:35:06 +00:00
actor = actor.split('?')[0]
nickname = getNicknameFromActor(self.path)
if nickname == actor:
postUrl = \
self.server.httpPrefix + '://' + \
self.server.domainFull + '/users/' + nickname + \
'/statuses/' + messageId
msg = htmlEditBlog(self.server.mediaInstance,
self.server.translate,
self.server.baseDir,
self.server.httpPrefix,
self.path,
replyPageNumber,
nickname, self.server.domain,
postUrl)
2020-03-01 12:33:20 +00:00
if msg:
2020-06-14 19:06:10 +00:00
msg = msg.encode('utf-8')
2020-04-02 21:35:06 +00:00
self._set_headers('text/html', len(msg),
cookie, callingDomain)
2020-03-01 12:33:20 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2020-03-01 12:33:20 +00:00
return
2020-03-01 11:05:20 +00:00
# edit profile in web interface
if '/users/' in self.path and self.path.endswith('/editprofile'):
2020-04-02 21:35:06 +00:00
msg = htmlEditProfile(self.server.translate,
self.server.baseDir,
self.path, self.server.domain,
self.server.port,
2020-06-14 19:06:10 +00:00
self.server.httpPrefix).encode('utf-8')
2020-05-27 10:30:40 +00:00
if msg:
self._set_headers('text/html', len(msg),
cookie, callingDomain)
self._write(msg)
else:
self._404()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
return
# Various types of new post in the web interface
2020-04-02 21:35:06 +00:00
if ('/users/' in self.path and
(self.path.endswith('/newpost') or
self.path.endswith('/newblog') or
self.path.endswith('/newunlisted') or
self.path.endswith('/newfollowers') or
self.path.endswith('/newdm') or
self.path.endswith('/newreport') or
self.path.endswith('/newquestion') or
self.path.endswith('/newshare'))):
nickname = getNicknameFromActor(self.path)
msg = htmlNewPost(self.server.mediaInstance,
self.server.translate,
self.server.baseDir,
self.server.httpPrefix,
self.path, inReplyToUrl,
replyToList,
shareDescription,
replyPageNumber,
2020-06-14 19:06:10 +00:00
nickname, self.server.domain).encode('utf-8')
2020-04-02 21:35:06 +00:00
self._set_headers('text/html', len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
return
2019-07-24 22:38:42 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 38)
2019-11-16 13:25:44 +00:00
2019-07-06 21:33:46 +00:00
# get an individual post from the path /@nickname/statusnumber
if '/@' in self.path:
2020-04-02 21:35:06 +00:00
namedStatus = self.path.split('/@')[1]
2019-07-19 13:32:58 +00:00
if '/' not in namedStatus:
# show actor
2020-04-02 21:35:06 +00:00
nickname = namedStatus
2019-07-19 13:32:58 +00:00
else:
2020-04-02 21:35:06 +00:00
postSections = namedStatus.split('/')
if len(postSections) == 2:
nickname = postSections[0]
statusNumber = postSections[1]
if len(statusNumber) > 10 and statusNumber.isdigit():
postFilename = \
self.server.baseDir + '/accounts/' + \
nickname + '@' + \
self.server.domain + '/outbox/' + \
self.server.httpPrefix + ':##' + \
self.server.domainFull + '#users#' + \
nickname + '#statuses#' + \
statusNumber + '.json'
2019-07-25 16:03:58 +00:00
if os.path.isfile(postFilename):
2020-04-02 21:35:06 +00:00
postJsonObject = loadJson(postFilename)
loadedPost = False
2019-10-22 11:55:06 +00:00
if postJsonObject:
2020-04-02 21:35:06 +00:00
loadedPost = True
2019-10-22 11:55:06 +00:00
else:
2020-04-02 21:35:06 +00:00
postJsonObject = {}
2020-03-22 21:16:02 +00:00
if loadedPost:
2020-04-02 21:35:06 +00:00
# Only authorized viewers get to see likes
# on posts. Otherwize marketers could gain
# more social graph info
2019-07-25 16:03:58 +00:00
if not authorized:
2020-04-02 21:35:06 +00:00
pjo = postJsonObject
self._removePostInteractions(pjo)
2019-08-24 11:23:12 +00:00
if self._requestHTTP():
2020-04-02 21:35:06 +00:00
recentPostsCache = \
self.server.recentPostsCache
maxRecentPosts = \
self.server.maxRecentPosts
translate = \
self.server.translate
cachedWebfingers = \
self.server.cachedWebfingers
personCache = \
self.server.personCache
httpPrefix = \
self.server.httpPrefix
projectVersion = \
self.server.projectVersion
msg = \
htmlIndividualPost(recentPostsCache,
maxRecentPosts,
translate,
self.server.session,
cachedWebfingers,
personCache,
nickname,
self.server.domain,
self.server.port,
authorized,
postJsonObject,
httpPrefix,
projectVersion)
msg = msg.encode('utf-8')
self._set_headers('text/html', len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-07-25 16:03:58 +00:00
else:
2019-09-25 09:22:10 +00:00
if self._fetchAuthenticated():
2020-04-02 21:35:06 +00:00
msg = json.dumps(postJsonObject,
ensure_ascii=False)
msg = msg.encode('utf-8')
self._set_headers('application/json',
len(msg),
None, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-09-25 09:22:10 +00:00
else:
self._404()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-07-25 16:03:58 +00:00
return
else:
self._404()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-07-25 16:03:58 +00:00
return
2019-09-28 11:29:42 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 39)
2019-11-16 13:25:44 +00:00
2019-07-13 19:28:14 +00:00
# get replies to a post /users/nickname/statuses/number/replies
2019-07-13 20:23:42 +00:00
if self.path.endswith('/replies') or '/replies?page=' in self.path:
2019-07-13 19:28:14 +00:00
if '/statuses/' in self.path and '/users/' in self.path:
2020-04-02 21:35:06 +00:00
namedStatus = self.path.split('/users/')[1]
2019-07-13 19:28:14 +00:00
if '/' in namedStatus:
2020-04-02 21:35:06 +00:00
postSections = namedStatus.split('/')
if len(postSections) >= 4:
2019-07-13 19:28:14 +00:00
if postSections[3].startswith('replies'):
2020-04-02 21:35:06 +00:00
nickname = postSections[0]
statusNumber = postSections[2]
if len(statusNumber) > 10 and \
statusNumber.isdigit():
boxname = 'outbox'
# get the replies file
postDir = \
self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain+'/' + \
boxname
postRepliesFilename = \
postDir + '/' + \
self.server.httpPrefix + ':##' + \
self.server.domainFull + '#users#' + \
nickname + '#statuses#' + \
statusNumber + '.replies'
2019-07-13 19:34:03 +00:00
if not os.path.isfile(postRepliesFilename):
2020-04-02 21:35:06 +00:00
# There are no replies,
# so show empty collection
contextStr = \
'https://www.w3.org/ns/activitystreams'
firstStr = \
self.server.httpPrefix + \
'://' + self.server.domainFull + \
'/users/' + nickname + \
'/statuses/' + statusNumber + \
'/replies?page=true'
idStr = \
self.server.httpPrefix + \
'://' + self.server.domainFull + \
'/users/' + nickname + \
'/statuses/' + statusNumber + \
'/replies'
lastStr = \
self.server.httpPrefix + \
'://' + self.server.domainFull + \
'/users/' + nickname + \
'/statuses/' + statusNumber + \
'/replies?page=true'
repliesJson = {
'@context': contextStr,
'first': firstStr,
'id': idStr,
'last': lastStr,
2019-07-13 19:34:03 +00:00
'totalItems': 0,
2020-04-02 21:35:06 +00:00
'type': 'OrderedCollection'
}
2019-08-24 11:23:12 +00:00
if self._requestHTTP():
2019-08-02 16:49:42 +00:00
if not self.server.session:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: ' +
'creating new session')
2020-06-09 11:03:59 +00:00
proxyType = \
self.server.proxyType
2020-04-02 21:35:06 +00:00
self.server.session = \
2020-06-09 11:03:59 +00:00
createSession(proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to ' +
'create session 15')
self._404()
self.server.GETbusy = False
2020-06-08 17:10:53 +00:00
return
2020-04-02 21:35:06 +00:00
recentPostsCache = \
self.server.recentPostsCache
maxRecentPosts = \
self.server.maxRecentPosts
translate = \
self.server.translate
baseDir = \
self.server.baseDir
session = \
self.server.session
cachedWebfingers = \
self.server.cachedWebfingers
personCache = \
self.server.personCache
httpPrefix = \
self.server.httpPrefix
projectVersion = \
self.server.projectVersion
msg = \
htmlPostReplies(recentPostsCache,
maxRecentPosts,
translate,
baseDir,
session,
cachedWebfingers,
personCache,
nickname,
self.server.domain,
self.server.port,
repliesJson,
httpPrefix,
projectVersion)
msg = msg.encode('utf-8')
self._set_headers('text/html',
len(msg),
cookie,
callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-07-20 21:13:36 +00:00
else:
2019-09-25 09:22:10 +00:00
if self._fetchAuthenticated():
2020-04-02 21:35:06 +00:00
msg = \
json.dumps(repliesJson,
ensure_ascii=False)
msg = msg.encode('utf-8')
protocolStr = 'application/json'
self._set_headers(protocolStr,
len(msg), None,
callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-09-25 09:22:10 +00:00
else:
self._404()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-07-13 19:34:03 +00:00
return
else:
2020-04-02 21:35:06 +00:00
# replies exist. Itterate through the
# text file containing message ids
contextStr = \
'https://www.w3.org/ns/activitystreams'
idStr = \
self.server.httpPrefix + \
'://' + self.server.domainFull + \
'/users/' + nickname + '/statuses/' + \
statusNumber + '?page=true'
partOfStr = \
self.server.httpPrefix + \
'://' + self.server.domainFull + \
'/users/' + nickname + \
'/statuses/' + statusNumber
repliesJson = {
'@context': contextStr,
'id': idStr,
2019-07-13 19:34:03 +00:00
'orderedItems': [
],
2020-04-02 21:35:06 +00:00
'partOf': partOfStr,
'type': 'OrderedCollectionPage'
}
2019-07-25 11:18:35 +00:00
2019-07-13 19:34:03 +00:00
# populate the items list with replies
2020-04-02 21:35:06 +00:00
populateRepliesJson(self.server.baseDir,
nickname,
self.server.domain,
postRepliesFilename,
authorized,
2019-08-02 18:37:23 +00:00
repliesJson)
2019-07-13 19:34:03 +00:00
# send the replies json
2019-08-24 11:23:12 +00:00
if self._requestHTTP():
2019-08-02 16:49:42 +00:00
if not self.server.session:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: ' +
'creating new session')
2020-06-09 11:03:59 +00:00
proxyType = self.server.proxyType
2020-04-02 21:35:06 +00:00
self.server.session = \
2020-06-09 11:03:59 +00:00
createSession(proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to ' +
'create session 16')
self._404()
self.server.GETbusy = False
2020-06-08 17:10:53 +00:00
return
2020-04-02 21:35:06 +00:00
recentPostsCache = \
self.server.recentPostsCache
maxRecentPosts = \
self.server.maxRecentPosts
translate = \
self.server.translate
baseDir = \
self.server.baseDir
session = \
self.server.session
cachedWebfingers = \
self.server.cachedWebfingers
personCache = \
self.server.personCache
httpPrefix = \
self.server.httpPrefix
projectVersion = \
self.server.projectVersion
msg = \
htmlPostReplies(recentPostsCache,
maxRecentPosts,
translate,
baseDir,
session,
cachedWebfingers,
personCache,
nickname,
self.server.domain,
self.server.port,
repliesJson,
httpPrefix,
projectVersion)
msg = msg.encode('utf-8')
self._set_headers('text/html',
len(msg),
cookie,
callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-07-20 21:13:36 +00:00
else:
2019-09-25 09:22:10 +00:00
if self._fetchAuthenticated():
2020-04-02 21:35:06 +00:00
msg = \
json.dumps(repliesJson,
ensure_ascii=False)
msg = msg.encode('utf-8')
protocolStr = 'application/json'
self._set_headers(protocolStr,
len(msg),
None,
callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-09-25 09:22:10 +00:00
else:
self._404()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-07-13 19:34:03 +00:00
return
2019-07-13 19:28:14 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 40)
2019-11-16 13:25:44 +00:00
2019-07-22 17:21:45 +00:00
if self.path.endswith('/roles') and '/users/' in self.path:
2020-04-02 21:35:06 +00:00
namedStatus = self.path.split('/users/')[1]
2019-07-22 17:21:45 +00:00
if '/' in namedStatus:
2020-04-02 21:35:06 +00:00
postSections = namedStatus.split('/')
nickname = postSections[0]
actorFilename = \
self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain + '.json'
2019-07-22 17:21:45 +00:00
if os.path.isfile(actorFilename):
2020-04-02 21:35:06 +00:00
actorJson = loadJson(actorFilename)
2020-03-22 21:16:02 +00:00
if actorJson:
2019-07-22 17:21:45 +00:00
if actorJson.get('roles'):
2019-08-24 11:23:12 +00:00
if self._requestHTTP():
2020-04-02 21:35:06 +00:00
getPerson = \
personLookup(self.server.domain,
self.path.replace('/roles',
''),
2019-07-22 17:21:45 +00:00
self.server.baseDir)
2019-07-22 20:01:46 +00:00
if getPerson:
2020-04-02 21:35:06 +00:00
defaultTimeline = \
self.server.defaultTimeline
recentPostsCache = \
self.server.recentPostsCache
cachedWebfingers = \
self.server.cachedWebfingers
msg = \
htmlProfile(defaultTimeline,
recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
self.server.projectVersion,
self.server.baseDir,
self.server.httpPrefix,
True,
self.server.ocapAlways,
getPerson, 'roles',
self.server.session,
cachedWebfingers,
self.server.personCache,
actorJson['roles'],
None, None)
msg = msg.encode('utf-8')
self._set_headers('text/html', len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-07-22 17:21:45 +00:00
else:
2019-09-25 09:22:10 +00:00
if self._fetchAuthenticated():
2020-04-02 21:35:06 +00:00
msg = json.dumps(actorJson['roles'],
ensure_ascii=False)
msg = msg.encode('utf-8')
self._set_headers('application/json',
len(msg),
None, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-09-25 09:22:10 +00:00
else:
self._404()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-07-22 17:21:45 +00:00
return
2019-07-22 20:01:46 +00:00
2019-08-27 17:33:11 +00:00
# show skills on the profile page
2019-07-22 20:01:46 +00:00
if self.path.endswith('/skills') and '/users/' in self.path:
2020-04-02 21:35:06 +00:00
namedStatus = self.path.split('/users/')[1]
2019-07-22 20:01:46 +00:00
if '/' in namedStatus:
2020-04-02 21:35:06 +00:00
postSections = namedStatus.split('/')
nickname = postSections[0]
actorFilename = \
self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain + '.json'
2019-07-22 20:01:46 +00:00
if os.path.isfile(actorFilename):
2020-04-02 21:35:06 +00:00
actorJson = loadJson(actorFilename)
2020-03-22 21:16:02 +00:00
if actorJson:
2019-07-22 20:01:46 +00:00
if actorJson.get('skills'):
2019-08-24 11:23:12 +00:00
if self._requestHTTP():
2020-04-02 21:35:06 +00:00
getPerson = \
personLookup(self.server.domain,
self.path.replace('/skills',
''),
2019-07-22 20:01:46 +00:00
self.server.baseDir)
if getPerson:
2020-04-02 21:35:06 +00:00
defaultTimeline = \
self.server.defaultTimeline
recentPostsCache = \
self.server.recentPostsCache
cachedWebfingers = \
self.server.cachedWebfingers
msg = \
htmlProfile(defaultTimeline,
recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
self.server.projectVersion,
self.server.baseDir,
self.server.httpPrefix,
True,
self.server.ocapAlways,
getPerson, 'skills',
self.server.session,
cachedWebfingers,
self.server.personCache,
actorJson['skills'],
None, None)
msg = msg.encode('utf-8')
self._set_headers('text/html',
len(msg),
cookie,
callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-07-22 20:01:46 +00:00
else:
2019-09-25 09:22:10 +00:00
if self._fetchAuthenticated():
2020-04-02 21:35:06 +00:00
msg = json.dumps(actorJson['skills'],
ensure_ascii=False)
msg = msg.encode('utf-8')
self._set_headers('application/json',
len(msg),
None,
callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-09-25 09:22:10 +00:00
else:
self._404()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-07-22 20:01:46 +00:00
return
2020-04-02 21:35:06 +00:00
actor = self.path.replace('/skills', '')
actorAbsolute = self.server.httpPrefix + '://' + \
self.server.domainFull + actor
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actorAbsolute = 'http://' + self.server.onionDomain + actor
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
actorAbsolute = 'http://' + self.server.i2pDomain + actor
2020-04-02 21:35:06 +00:00
self._redirect_headers(actorAbsolute, cookie, callingDomain)
self.server.GETbusy = False
2019-08-27 17:33:11 +00:00
return
2019-07-22 20:01:46 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 41)
2019-11-16 13:25:44 +00:00
2020-04-02 21:35:06 +00:00
# get an individual post from the path
# /users/nickname/statuses/number
2019-07-06 21:24:47 +00:00
if '/statuses/' in self.path and '/users/' in self.path:
2020-04-02 21:35:06 +00:00
namedStatus = self.path.split('/users/')[1]
2019-07-06 21:24:47 +00:00
if '/' in namedStatus:
2020-04-02 21:35:06 +00:00
postSections = namedStatus.split('/')
if len(postSections) >= 3:
nickname = postSections[0]
statusNumber = postSections[2]
if len(statusNumber) > 10 and statusNumber.isdigit():
postFilename = \
self.server.baseDir + '/accounts/' + \
nickname + '@' + \
self.server.domain + '/outbox/' + \
self.server.httpPrefix + ':##' + \
self.server.domainFull + '#users#' + \
nickname + '#statuses#' + \
statusNumber + '.json'
2019-07-22 17:21:45 +00:00
if os.path.isfile(postFilename):
2020-04-02 21:35:06 +00:00
postJsonObject = loadJson(postFilename)
2019-10-22 11:55:06 +00:00
if not postJsonObject:
2019-08-31 13:08:01 +00:00
self.send_response(429)
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-08-31 13:08:01 +00:00
return
else:
2020-04-02 21:35:06 +00:00
# Only authorized viewers get to see likes
# on posts
# Otherwize marketers could gain more social
# graph info
2019-07-25 11:18:35 +00:00
if not authorized:
2020-04-02 21:35:06 +00:00
pjo = postJsonObject
self._removePostInteractions(pjo)
2019-11-25 11:19:03 +00:00
2019-08-24 11:23:12 +00:00
if self._requestHTTP():
2020-04-02 21:35:06 +00:00
recentPostsCache = \
self.server.recentPostsCache
maxRecentPosts = \
self.server.maxRecentPosts
translate = \
self.server.translate
cachedWebfingers = \
self.server.cachedWebfingers
personCache = \
self.server.personCache
httpPrefix = \
self.server.httpPrefix
projectVersion = \
self.server.projectVersion
msg = \
htmlIndividualPost(recentPostsCache,
maxRecentPosts,
translate,
self.server.baseDir,
self.server.session,
cachedWebfingers,
personCache,
nickname,
self.server.domain,
self.server.port,
authorized,
postJsonObject,
httpPrefix,
projectVersion)
msg = msg.encode('utf-8')
self._set_headers('text/html',
len(msg),
cookie,
callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-07-22 17:21:45 +00:00
else:
2019-09-25 09:22:10 +00:00
if self._fetchAuthenticated():
2020-04-02 21:35:06 +00:00
msg = json.dumps(postJsonObject,
ensure_ascii=False)
msg = msg.encode('utf-8')
self._set_headers('application/json',
len(msg),
None, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-09-25 09:22:10 +00:00
else:
self._404()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-07-22 17:21:45 +00:00
return
else:
self._404()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-07-22 17:21:45 +00:00
return
2019-08-12 13:22:17 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 42)
2019-11-16 13:25:44 +00:00
2019-07-03 19:32:07 +00:00
# get the inbox for a given person
2019-08-12 13:22:17 +00:00
if self.path.endswith('/inbox') or '/inbox?page=' in self.path:
2019-07-03 19:32:07 +00:00
if '/users/' in self.path:
2019-07-25 11:18:35 +00:00
if authorized:
2020-04-02 21:35:06 +00:00
inboxFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
self.server.baseDir,
self.server.domain,
self.server.port,
self.path,
self.server.httpPrefix,
maxPostsInFeed, 'inbox',
authorized,
self.server.ocapAlways)
2019-07-12 11:05:43 +00:00
if inboxFeed:
2019-08-24 11:23:12 +00:00
if self._requestHTTP():
2020-04-02 21:35:06 +00:00
nickname = self.path.replace('/users/', '')
nickname = nickname.replace('/inbox', '')
pageNumber = 1
2019-07-24 12:02:28 +00:00
if '?page=' in nickname:
2020-04-02 21:35:06 +00:00
pageNumber = nickname.split('?page=')[1]
nickname = nickname.split('?page=')[0]
2019-07-31 20:37:19 +00:00
if pageNumber.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumber)
2019-07-31 20:37:19 +00:00
else:
2020-04-02 21:35:06 +00:00
pageNumber = 1
2019-07-24 11:03:56 +00:00
if 'page=' not in self.path:
# if no page was specified then show the first
2020-04-02 21:35:06 +00:00
inboxFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
self.server.baseDir,
self.server.domain,
self.server.port,
self.path + '?page=1',
self.server.httpPrefix,
maxPostsInFeed, 'inbox',
authorized,
self.server.ocapAlways)
msg = htmlInbox(self.server.defaultTimeline,
self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
pageNumber, maxPostsInFeed,
self.server.session,
self.server.baseDir,
self.server.cachedWebfingers,
self.server.personCache,
nickname,
self.server.domain,
self.server.port,
inboxFeed,
self.server.allowDeletion,
self.server.httpPrefix,
2020-05-23 14:23:56 +00:00
self.server.projectVersion,
self._isMinimal(nickname))
2020-04-02 21:35:06 +00:00
msg = msg.encode('utf-8')
self._set_headers('text/html',
len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-07-20 21:13:36 +00:00
else:
2020-04-02 21:35:06 +00:00
# don't need authenticated fetch here because
# there is already the authorization check
msg = json.dumps(inboxFeed, ensure_ascii=False)
msg = msg.encode('utf-8')
self._set_headers('application/json',
len(msg),
None, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-07-12 11:05:43 +00:00
return
else:
if self.server.debug:
2020-04-02 21:35:06 +00:00
nickname = self.path.replace('/users/', '')
nickname = nickname.replace('/inbox', '')
print('DEBUG: ' + nickname +
' was not authorized to access ' + self.path)
if self.path != '/inbox':
2019-08-05 16:05:08 +00:00
# not the shared inbox
if self.server.debug:
print('DEBUG: GET access to inbox is unauthorized')
self.send_response(405)
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-08-05 16:05:08 +00:00
return
2019-08-25 16:09:56 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 43)
2019-11-16 13:25:44 +00:00
2019-09-23 20:09:11 +00:00
# get the direct messages for a given person
2019-08-25 16:09:56 +00:00
if self.path.endswith('/dm') or '/dm?page=' in self.path:
if '/users/' in self.path:
if authorized:
2020-04-02 21:35:06 +00:00
inboxDMFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
self.server.baseDir,
self.server.domain,
self.server.port,
self.path,
self.server.httpPrefix,
maxPostsInFeed, 'dm',
authorized,
self.server.ocapAlways)
2019-08-25 16:09:56 +00:00
if inboxDMFeed:
if self._requestHTTP():
2020-04-02 21:35:06 +00:00
nickname = self.path.replace('/users/', '')
nickname = nickname.replace('/dm', '')
pageNumber = 1
2019-08-25 16:09:56 +00:00
if '?page=' in nickname:
2020-04-02 21:35:06 +00:00
pageNumber = nickname.split('?page=')[1]
nickname = nickname.split('?page=')[0]
2019-08-25 16:09:56 +00:00
if pageNumber.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumber)
2019-08-25 16:09:56 +00:00
else:
2020-04-02 21:35:06 +00:00
pageNumber = 1
2019-08-25 16:09:56 +00:00
if 'page=' not in self.path:
# if no page was specified then show the first
2020-04-02 21:35:06 +00:00
inboxDMFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
self.server.baseDir,
self.server.domain,
self.server.port,
self.path+'?page=1',
self.server.httpPrefix,
maxPostsInFeed, 'dm',
authorized,
self.server.ocapAlways)
msg = \
htmlInboxDMs(self.server.defaultTimeline,
self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
pageNumber, maxPostsInFeed,
self.server.session,
self.server.baseDir,
self.server.cachedWebfingers,
self.server.personCache,
nickname,
self.server.domain,
self.server.port,
inboxDMFeed,
self.server.allowDeletion,
self.server.httpPrefix,
2020-05-23 14:23:56 +00:00
self.server.projectVersion,
self._isMinimal(nickname))
2020-04-02 21:35:06 +00:00
msg = msg.encode('utf-8')
self._set_headers('text/html',
len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-08-25 16:09:56 +00:00
else:
2020-04-02 21:35:06 +00:00
# don't need authenticated fetch here because
# there is already the authorization check
msg = json.dumps(inboxDMFeed, ensure_ascii=False)
msg = msg.encode('utf-8')
self._set_headers('application/json',
len(msg),
None, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-08-25 16:09:56 +00:00
return
else:
if self.server.debug:
2020-04-02 21:35:06 +00:00
nickname = self.path.replace('/users/', '')
nickname = nickname.replace('/dm', '')
print('DEBUG: ' + nickname +
' was not authorized to access ' + self.path)
if self.path != '/dm':
2019-08-25 16:09:56 +00:00
# not the DM inbox
if self.server.debug:
print('DEBUG: GET access to inbox is unauthorized')
self.send_response(405)
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-08-25 16:09:56 +00:00
return
2019-09-23 20:09:11 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 44)
2019-11-16 13:25:44 +00:00
2019-09-23 20:09:11 +00:00
# get the replies for a given person
2019-09-23 20:43:18 +00:00
if self.path.endswith('/tlreplies') or '/tlreplies?page=' in self.path:
2019-09-23 20:09:11 +00:00
if '/users/' in self.path:
if authorized:
2020-04-02 21:35:06 +00:00
inboxRepliesFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
self.server.baseDir,
self.server.domain,
self.server.port,
self.path,
self.server.httpPrefix,
maxPostsInFeed, 'tlreplies',
True, self.server.ocapAlways)
2019-09-23 21:02:06 +00:00
if not inboxRepliesFeed:
2020-04-02 21:35:06 +00:00
inboxRepliesFeed = []
2019-09-23 21:02:06 +00:00
if self._requestHTTP():
2020-04-02 21:35:06 +00:00
nickname = self.path.replace('/users/', '')
nickname = nickname.replace('/tlreplies', '')
pageNumber = 1
2019-09-23 21:02:06 +00:00
if '?page=' in nickname:
2020-04-02 21:35:06 +00:00
pageNumber = nickname.split('?page=')[1]
nickname = nickname.split('?page=')[0]
2019-09-23 21:02:06 +00:00
if pageNumber.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumber)
2019-09-23 21:02:06 +00:00
else:
2020-04-02 21:35:06 +00:00
pageNumber = 1
2019-09-23 21:02:06 +00:00
if 'page=' not in self.path:
# if no page was specified then show the first
2020-04-02 21:35:06 +00:00
inboxRepliesFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
self.server.baseDir,
self.server.domain,
self.server.port,
self.path + '?page=1',
self.server.httpPrefix,
maxPostsInFeed, 'tlreplies',
True, self.server.ocapAlways)
msg = \
htmlInboxReplies(self.server.defaultTimeline,
self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
pageNumber, maxPostsInFeed,
self.server.session,
self.server.baseDir,
self.server.cachedWebfingers,
self.server.personCache,
nickname,
self.server.domain,
self.server.port,
inboxRepliesFeed,
self.server.allowDeletion,
self.server.httpPrefix,
2020-05-23 14:23:56 +00:00
self.server.projectVersion,
self._isMinimal(nickname))
2020-04-02 21:35:06 +00:00
msg = msg.encode('utf-8')
self._set_headers('text/html',
len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-09-23 21:02:06 +00:00
else:
# don't need authenticated fetch here because there is
# already the authorization check
2020-04-02 21:35:06 +00:00
msg = json.dumps(inboxRepliesFeed,
ensure_ascii=False)
msg = msg.encode('utf-8')
self._set_headers('application/json',
len(msg),
None, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-09-23 21:02:06 +00:00
return
2019-09-23 20:09:11 +00:00
else:
if self.server.debug:
2020-04-02 21:35:06 +00:00
nickname = self.path.replace('/users/', '')
nickname = nickname.replace('/tlreplies', '')
print('DEBUG: ' + nickname +
' was not authorized to access ' + self.path)
if self.path != '/tlreplies':
2019-09-23 20:09:11 +00:00
# not the replies inbox
if self.server.debug:
print('DEBUG: GET access to inbox is unauthorized')
self.send_response(405)
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-09-28 11:29:42 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 45)
2019-11-16 13:25:44 +00:00
2019-09-28 11:29:42 +00:00
# get the media for a given person
if self.path.endswith('/tlmedia') or '/tlmedia?page=' in self.path:
if '/users/' in self.path:
if authorized:
2020-04-02 21:35:06 +00:00
inboxMediaFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
self.server.baseDir,
self.server.domain,
self.server.port,
self.path,
self.server.httpPrefix,
maxPostsInMediaFeed, 'tlmedia',
True, self.server.ocapAlways)
2019-09-28 11:29:42 +00:00
if not inboxMediaFeed:
2020-04-02 21:35:06 +00:00
inboxMediaFeed = []
2019-09-28 11:29:42 +00:00
if self._requestHTTP():
2020-04-02 21:35:06 +00:00
nickname = self.path.replace('/users/', '')
nickname = nickname.replace('/tlmedia', '')
pageNumber = 1
2019-09-28 11:29:42 +00:00
if '?page=' in nickname:
2020-04-02 21:35:06 +00:00
pageNumber = nickname.split('?page=')[1]
nickname = nickname.split('?page=')[0]
2019-09-28 11:29:42 +00:00
if pageNumber.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumber)
2019-09-28 11:29:42 +00:00
else:
2020-04-02 21:35:06 +00:00
pageNumber = 1
2019-09-28 11:29:42 +00:00
if 'page=' not in self.path:
# if no page was specified then show the first
2020-04-02 21:35:06 +00:00
inboxMediaFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
self.server.baseDir,
self.server.domain,
self.server.port,
self.path + '?page=1',
self.server.httpPrefix,
maxPostsInMediaFeed, 'tlmedia',
True, self.server.ocapAlways)
msg = \
htmlInboxMedia(self.server.defaultTimeline,
self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
pageNumber, maxPostsInMediaFeed,
self.server.session,
self.server.baseDir,
self.server.cachedWebfingers,
self.server.personCache,
nickname,
self.server.domain,
self.server.port,
inboxMediaFeed,
self.server.allowDeletion,
self.server.httpPrefix,
2020-05-23 14:23:56 +00:00
self.server.projectVersion,
2020-05-23 15:13:14 +00:00
self._isMinimal(nickname))
2020-04-02 21:35:06 +00:00
msg = msg.encode('utf-8')
self._set_headers('text/html',
len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-09-28 11:29:42 +00:00
else:
# don't need authenticated fetch here because there is
# already the authorization check
2020-04-02 21:35:06 +00:00
msg = json.dumps(inboxMediaFeed,
ensure_ascii=False)
msg = msg.encode('utf-8')
self._set_headers('application/json',
len(msg),
None, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-09-28 11:29:42 +00:00
return
else:
if self.server.debug:
2020-04-02 21:35:06 +00:00
nickname = self.path.replace('/users/', '')
nickname = nickname.replace('/tlmedia', '')
print('DEBUG: ' + nickname +
' was not authorized to access ' + self.path)
if self.path != '/tlmedia':
2019-09-28 11:29:42 +00:00
# not the media inbox
if self.server.debug:
print('DEBUG: GET access to inbox is unauthorized')
self.send_response(405)
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-09-23 20:09:11 +00:00
return
2019-08-25 16:09:56 +00:00
2020-02-24 14:39:25 +00:00
# get the blogs for a given person
if self.path.endswith('/tlblogs') or '/tlblogs?page=' in self.path:
if '/users/' in self.path:
if authorized:
2020-04-02 21:35:06 +00:00
inboxBlogsFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
self.server.baseDir,
self.server.domain,
self.server.port,
self.path,
self.server.httpPrefix,
maxPostsInBlogsFeed, 'tlblogs',
True, self.server.ocapAlways)
2020-02-24 14:39:25 +00:00
if not inboxBlogsFeed:
2020-04-02 21:35:06 +00:00
inboxBlogsFeed = []
2020-02-24 14:39:25 +00:00
if self._requestHTTP():
2020-04-02 21:35:06 +00:00
nickname = self.path.replace('/users/', '')
nickname = nickname.replace('/tlblogs', '')
pageNumber = 1
2020-02-24 14:39:25 +00:00
if '?page=' in nickname:
2020-04-02 21:35:06 +00:00
pageNumber = nickname.split('?page=')[1]
nickname = nickname.split('?page=')[0]
2020-02-24 14:39:25 +00:00
if pageNumber.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumber)
2020-02-24 14:39:25 +00:00
else:
2020-04-02 21:35:06 +00:00
pageNumber = 1
2020-02-24 14:39:25 +00:00
if 'page=' not in self.path:
# if no page was specified then show the first
2020-04-02 21:35:06 +00:00
inboxBlogsFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
self.server.baseDir,
self.server.domain,
self.server.port,
self.path + '?page=1',
self.server.httpPrefix,
maxPostsInBlogsFeed, 'tlblogs',
True, self.server.ocapAlways)
msg = \
htmlInboxBlogs(self.server.defaultTimeline,
self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
pageNumber, maxPostsInBlogsFeed,
self.server.session,
self.server.baseDir,
self.server.cachedWebfingers,
self.server.personCache,
nickname,
self.server.domain,
self.server.port,
inboxBlogsFeed,
self.server.allowDeletion,
self.server.httpPrefix,
2020-05-23 14:23:56 +00:00
self.server.projectVersion,
self._isMinimal(nickname))
2020-04-02 21:35:06 +00:00
msg = msg.encode('utf-8')
self._set_headers('text/html',
len(msg),
cookie, callingDomain)
2020-02-24 14:39:25 +00:00
self._write(msg)
else:
# don't need authenticated fetch here because there is
# already the authorization check
2020-04-02 21:35:06 +00:00
msg = json.dumps(inboxBlogsFeed,
ensure_ascii=False)
msg = msg.encode('utf-8')
self._set_headers('application/json',
len(msg),
None, callingDomain)
2020-02-24 14:39:25 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2020-02-24 14:39:25 +00:00
return
else:
if self.server.debug:
2020-04-02 21:35:06 +00:00
nickname = self.path.replace('/users/', '')
nickname = nickname.replace('/tlblogs', '')
print('DEBUG: ' + nickname +
' was not authorized to access ' + self.path)
if self.path != '/tlblogs':
2020-02-24 14:39:25 +00:00
# not the blogs inbox
if self.server.debug:
print('DEBUG: GET access to blogs is unauthorized')
self.send_response(405)
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2020-02-24 14:39:25 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 46)
2019-11-16 13:25:44 +00:00
2019-11-02 14:31:39 +00:00
# get the shared items timeline for a given person
if self.path.endswith('/tlshares') or '/tlshares?page=' in self.path:
if '/users/' in self.path:
if authorized:
if self._requestHTTP():
2020-04-02 21:35:06 +00:00
nickname = self.path.replace('/users/', '')
nickname = nickname.replace('/tlshares', '')
pageNumber = 1
2019-11-02 14:31:39 +00:00
if '?page=' in nickname:
2020-04-02 21:35:06 +00:00
pageNumber = nickname.split('?page=')[1]
nickname = nickname.split('?page=')[0]
2019-11-02 14:31:39 +00:00
if pageNumber.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumber)
2019-11-02 14:31:39 +00:00
else:
2020-04-02 21:35:06 +00:00
pageNumber = 1
msg = \
htmlShares(self.server.defaultTimeline,
self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
pageNumber, maxPostsInFeed,
self.server.session,
self.server.baseDir,
self.server.cachedWebfingers,
self.server.personCache,
nickname,
self.server.domain,
self.server.port,
self.server.allowDeletion,
self.server.httpPrefix,
self.server.projectVersion)
msg = msg.encode('utf-8')
self._set_headers('text/html',
len(msg),
cookie, callingDomain)
2019-11-02 14:31:39 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-11-02 14:31:39 +00:00
return
# not the shares timeline
if self.server.debug:
print('DEBUG: GET access to shares timeline is unauthorized')
self.send_response(405)
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-11-02 14:31:39 +00:00
return
2019-11-17 14:01:49 +00:00
# get the bookmarks for a given person
2020-04-02 21:35:06 +00:00
if self.path.endswith('/tlbookmarks') or \
2020-05-21 19:58:21 +00:00
'/tlbookmarks?page=' in self.path or \
self.path.endswith('/bookmarks') or \
'/bookmarks?page=' in self.path:
2019-11-17 14:01:49 +00:00
if '/users/' in self.path:
if authorized:
2020-04-02 21:35:06 +00:00
bookmarksFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
self.server.baseDir,
self.server.domain,
self.server.port,
self.path,
self.server.httpPrefix,
maxPostsInFeed, 'tlbookmarks',
authorized, self.server.ocapAlways)
2019-11-17 14:01:49 +00:00
if bookmarksFeed:
if self._requestHTTP():
2020-04-02 21:35:06 +00:00
nickname = self.path.replace('/users/', '')
nickname = nickname.replace('/tlbookmarks', '')
2020-05-21 19:58:21 +00:00
nickname = nickname.replace('/bookmarks', '')
2020-04-02 21:35:06 +00:00
pageNumber = 1
2019-11-17 14:01:49 +00:00
if '?page=' in nickname:
2020-04-02 21:35:06 +00:00
pageNumber = nickname.split('?page=')[1]
nickname = nickname.split('?page=')[0]
2019-11-17 14:01:49 +00:00
if pageNumber.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumber)
2019-11-17 14:01:49 +00:00
else:
2020-04-02 21:35:06 +00:00
pageNumber = 1
2019-11-17 14:01:49 +00:00
if 'page=' not in self.path:
# if no page was specified then show the first
2020-04-02 21:35:06 +00:00
bookmarksFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
self.server.baseDir,
self.server.domain,
self.server.port,
self.path + '?page=1',
self.server.httpPrefix,
maxPostsInFeed,
'tlbookmarks',
authorized,
self.server.ocapAlways)
msg = \
htmlBookmarks(self.server.defaultTimeline,
self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
pageNumber, maxPostsInFeed,
self.server.session,
self.server.baseDir,
self.server.cachedWebfingers,
self.server.personCache,
nickname,
self.server.domain,
self.server.port,
bookmarksFeed,
self.server.allowDeletion,
self.server.httpPrefix,
2020-05-23 15:14:59 +00:00
self.server.projectVersion,
self._isMinimal(nickname))
2020-04-02 21:35:06 +00:00
msg = msg.encode('utf-8')
self._set_headers('text/html',
len(msg),
cookie, callingDomain)
2019-11-17 14:01:49 +00:00
self._write(msg)
else:
2020-04-02 21:35:06 +00:00
# don't need authenticated fetch here because
# there is already the authorization check
msg = json.dumps(inboxFeed,
ensure_ascii=False)
msg = msg.encode('utf-8')
self._set_headers('application/json',
len(msg),
None, callingDomain)
2019-11-17 14:01:49 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-11-17 14:01:49 +00:00
return
else:
if self.server.debug:
2020-04-02 21:35:06 +00:00
nickname = self.path.replace('/users/', '')
nickname = nickname.replace('/tlbookmarks', '')
2020-05-21 19:58:21 +00:00
nickname = nickname.replace('/bookmarks', '')
2020-04-02 21:35:06 +00:00
print('DEBUG: ' + nickname +
' was not authorized to access ' + self.path)
2019-11-17 14:01:49 +00:00
if self.server.debug:
print('DEBUG: GET access to bookmarks is unauthorized')
self.send_response(405)
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-11-17 14:01:49 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 47)
2019-11-16 13:25:44 +00:00
2019-06-29 14:35:26 +00:00
# get outbox feed for a person
2020-04-02 21:35:06 +00:00
outboxFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
self.server.baseDir, self.server.domain,
self.server.port, self.path,
self.server.httpPrefix,
maxPostsInFeed, 'outbox',
authorized,
2019-11-17 17:20:08 +00:00
self.server.ocapAlways)
2019-06-29 14:41:23 +00:00
if outboxFeed:
2019-08-24 11:23:12 +00:00
if self._requestHTTP():
2020-04-02 21:35:06 +00:00
nickname = \
self.path.replace('/users/', '').replace('/outbox', '')
pageNumber = 1
2019-07-24 12:02:28 +00:00
if '?page=' in nickname:
2020-04-02 21:35:06 +00:00
pageNumber = nickname.split('?page=')[1]
nickname = nickname.split('?page=')[0]
2019-07-31 20:37:19 +00:00
if pageNumber.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumber)
2019-07-31 20:37:19 +00:00
else:
2020-04-02 21:35:06 +00:00
pageNumber = 1
2019-07-24 11:03:56 +00:00
if 'page=' not in self.path:
# if a page wasn't specified then show the first one
2020-04-02 21:35:06 +00:00
outboxFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
self.server.baseDir,
self.server.domain,
self.server.port,
self.path + '?page=1',
self.server.httpPrefix,
maxPostsInFeed, 'outbox',
authorized,
2019-11-17 17:20:08 +00:00
self.server.ocapAlways)
2020-04-02 21:35:06 +00:00
msg = \
htmlOutbox(self.server.defaultTimeline,
self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
pageNumber, maxPostsInFeed,
self.server.session,
self.server.baseDir,
self.server.cachedWebfingers,
self.server.personCache,
nickname,
self.server.domain,
self.server.port,
outboxFeed,
self.server.allowDeletion,
self.server.httpPrefix,
2020-05-23 14:23:56 +00:00
self.server.projectVersion,
self._isMinimal(nickname))
2020-04-02 21:35:06 +00:00
msg = msg.encode('utf-8')
self._set_headers('text/html',
len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-07-20 21:13:36 +00:00
else:
2019-09-25 09:22:10 +00:00
if self._fetchAuthenticated():
2020-04-02 21:35:06 +00:00
msg = json.dumps(outboxFeed,
ensure_ascii=False)
msg = msg.encode('utf-8')
self._set_headers('application/json',
len(msg),
None, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-09-25 09:22:10 +00:00
else:
self._404()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-06-29 20:21:37 +00:00
return
2019-07-23 12:33:09 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 48)
2019-11-16 13:25:44 +00:00
2019-08-12 13:22:17 +00:00
# get the moderation feed for a moderator
2019-11-03 15:27:29 +00:00
if self.path.endswith('/moderation') or \
'/moderation?page=' in self.path:
2019-08-12 13:22:17 +00:00
if '/users/' in self.path:
if authorized:
2020-04-02 21:35:06 +00:00
moderationFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
self.server.baseDir,
self.server.domain,
self.server.port,
self.path,
self.server.httpPrefix,
maxPostsInFeed, 'moderation',
True, self.server.ocapAlways)
2019-08-12 13:22:17 +00:00
if moderationFeed:
2019-08-24 11:23:12 +00:00
if self._requestHTTP():
2020-04-02 21:35:06 +00:00
nickname = self.path.replace('/users/', '')
nickname = nickname.replace('/moderation', '')
pageNumber = 1
2019-08-12 13:22:17 +00:00
if '?page=' in nickname:
2020-04-02 21:35:06 +00:00
pageNumber = nickname.split('?page=')[1]
nickname = nickname.split('?page=')[0]
2019-08-12 13:22:17 +00:00
if pageNumber.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumber)
2019-08-12 13:22:17 +00:00
else:
2020-04-02 21:35:06 +00:00
pageNumber = 1
2019-08-12 13:22:17 +00:00
if 'page=' not in self.path:
# if no page was specified then show the first
2020-04-02 21:35:06 +00:00
moderationFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
self.server.baseDir,
self.server.domain,
self.server.port,
self.path + '?page=1',
self.server.httpPrefix,
maxPostsInFeed, 'moderation',
True, self.server.ocapAlways)
msg = \
htmlModeration(self.server.defaultTimeline,
self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
pageNumber, maxPostsInFeed,
self.server.session,
self.server.baseDir,
self.server.cachedWebfingers,
self.server.personCache,
nickname,
self.server.domain,
self.server.port,
moderationFeed,
True,
self.server.httpPrefix,
self.server.projectVersion)
msg = msg.encode('utf-8')
self._set_headers('text/html',
len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-08-12 13:22:17 +00:00
else:
2020-04-02 21:35:06 +00:00
# don't need authenticated fetch here because
# there is already the authorization check
msg = json.dumps(moderationFeed,
ensure_ascii=False)
msg = msg.encode('utf-8')
self._set_headers('application/json',
len(msg),
None, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-08-12 13:22:17 +00:00
return
else:
if self.server.debug:
2020-04-02 21:35:06 +00:00
nickname = self.path.replace('/users/', '')
nickname = nickname.replace('/moderation', '')
print('DEBUG: ' + nickname +
' was not authorized to access ' + self.path)
2019-08-12 13:22:17 +00:00
if self.server.debug:
print('DEBUG: GET access to moderation feed is unauthorized')
self.send_response(405)
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-08-12 13:22:17 +00:00
return
2020-03-22 21:16:02 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 49)
2019-11-16 13:25:44 +00:00
2020-04-02 21:35:06 +00:00
shares = \
getSharesFeedForPerson(self.server.baseDir,
self.server.domain,
self.server.port, self.path,
self.server.httpPrefix,
2019-11-17 17:20:08 +00:00
sharesPerPage)
2019-07-23 12:33:09 +00:00
if shares:
2019-08-24 11:23:12 +00:00
if self._requestHTTP():
2020-04-02 21:35:06 +00:00
pageNumber = 1
2019-09-05 15:11:21 +00:00
if '?page=' not in self.path:
2020-04-02 21:35:06 +00:00
searchPath = self.path
2019-07-23 12:33:09 +00:00
# get a page of shares, not the summary
2020-04-02 21:35:06 +00:00
shares = \
getSharesFeedForPerson(self.server.baseDir,
self.server.domain,
self.server.port,
self.path + '?page=true',
self.server.httpPrefix,
2019-11-03 15:27:29 +00:00
sharesPerPage)
2019-09-05 15:11:21 +00:00
else:
2020-04-02 21:35:06 +00:00
pageNumberStr = self.path.split('?page=')[1]
2020-05-22 12:41:47 +00:00
if '#' in pageNumberStr:
pageNumberStr = pageNumberStr.split('#')[0]
if pageNumberStr.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumberStr)
searchPath = self.path.split('?page=')[0]
getPerson = \
personLookup(self.server.domain,
searchPath.replace('/shares', ''),
2019-11-03 15:27:29 +00:00
self.server.baseDir)
2019-07-23 12:33:09 +00:00
if getPerson:
if not self.server.session:
if self.server.debug:
print('DEBUG: creating new session')
2020-04-02 21:35:06 +00:00
self.server.session = \
2020-06-09 11:03:59 +00:00
createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to create session 17')
self._404()
self.server.GETbusy = False
2020-06-08 17:10:53 +00:00
return
2020-04-02 21:35:06 +00:00
msg = \
htmlProfile(self.server.defaultTimeline,
self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
self.server.projectVersion,
self.server.baseDir,
self.server.httpPrefix,
authorized,
self.server.ocapAlways,
getPerson, 'shares',
self.server.session,
self.server.cachedWebfingers,
self.server.personCache,
shares,
pageNumber, sharesPerPage)
msg = msg.encode('utf-8')
self._set_headers('text/html',
len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-07-23 12:33:09 +00:00
return
else:
2019-09-25 09:22:10 +00:00
if self._fetchAuthenticated():
2020-04-02 21:35:06 +00:00
msg = json.dumps(shares,
ensure_ascii=False)
msg = msg.encode('utf-8')
self._set_headers('application/json',
len(msg),
None, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-09-25 09:22:10 +00:00
else:
self._404()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-07-23 12:33:09 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 50)
2019-11-16 13:25:44 +00:00
2020-04-02 21:35:06 +00:00
following = \
getFollowingFeed(self.server.baseDir, self.server.domain,
self.server.port, self.path,
self.server.httpPrefix,
authorized, followsPerPage)
2019-06-29 20:21:37 +00:00
if following:
2019-08-24 11:23:12 +00:00
if self._requestHTTP():
2020-04-02 21:35:06 +00:00
pageNumber = 1
2019-09-05 15:07:58 +00:00
if '?page=' not in self.path:
2020-04-02 21:35:06 +00:00
searchPath = self.path
2019-07-22 09:38:02 +00:00
# get a page of following, not the summary
2020-04-02 21:35:06 +00:00
following = \
getFollowingFeed(self.server.baseDir,
self.server.domain,
self.server.port,
self.path + '?page=true',
self.server.httpPrefix,
authorized, followsPerPage)
2019-09-05 15:07:58 +00:00
else:
2020-04-02 21:35:06 +00:00
pageNumberStr = self.path.split('?page=')[1]
2020-05-22 12:41:47 +00:00
if '#' in pageNumberStr:
pageNumberStr = pageNumberStr.split('#')[0]
if pageNumberStr.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumberStr)
searchPath = self.path.split('?page=')[0]
getPerson = \
personLookup(self.server.domain,
searchPath.replace('/following', ''),
2020-03-02 16:11:34 +00:00
self.server.baseDir)
2019-07-22 09:38:02 +00:00
if getPerson:
2019-07-22 14:09:21 +00:00
if not self.server.session:
if self.server.debug:
print('DEBUG: creating new session')
2020-04-02 21:35:06 +00:00
self.server.session = \
2020-06-09 11:03:59 +00:00
createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to create session 18')
self._404()
self.server.GETbusy = False
2020-06-08 17:10:53 +00:00
return
2019-08-15 13:20:09 +00:00
2020-04-02 21:35:06 +00:00
msg = \
htmlProfile(self.server.defaultTimeline,
self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
self.server.projectVersion,
self.server.baseDir,
self.server.httpPrefix,
authorized,
self.server.ocapAlways,
getPerson, 'following',
self.server.session,
self.server.cachedWebfingers,
self.server.personCache,
following,
pageNumber,
followsPerPage).encode('utf-8')
self._set_headers('text/html',
len(msg), cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-07-22 09:38:02 +00:00
return
2019-07-20 21:13:36 +00:00
else:
2019-09-25 09:22:10 +00:00
if self._fetchAuthenticated():
2020-04-02 21:35:06 +00:00
msg = json.dumps(following,
ensure_ascii=False).encode('utf-8')
self._set_headers('application/json',
len(msg),
None, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-09-25 09:22:10 +00:00
else:
self._404()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
return
2019-11-16 13:25:44 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 51)
2019-11-16 13:25:44 +00:00
2020-04-02 21:35:06 +00:00
followers = \
getFollowingFeed(self.server.baseDir, self.server.domain,
self.server.port, self.path,
self.server.httpPrefix,
authorized, followsPerPage, 'followers')
2019-06-29 20:21:37 +00:00
if followers:
2019-08-24 11:23:12 +00:00
if self._requestHTTP():
2020-04-02 21:35:06 +00:00
pageNumber = 1
2019-09-05 15:07:58 +00:00
if '?page=' not in self.path:
2020-04-02 21:35:06 +00:00
searchPath = self.path
2019-07-22 09:38:02 +00:00
# get a page of followers, not the summary
2020-04-02 21:35:06 +00:00
followers = \
getFollowingFeed(self.server.baseDir,
self.server.domain,
self.server.port,
self.path + '?page=1',
self.server.httpPrefix,
authorized, followsPerPage,
'followers')
2019-09-05 15:07:58 +00:00
else:
2020-04-02 21:35:06 +00:00
pageNumberStr = self.path.split('?page=')[1]
2020-05-22 12:41:47 +00:00
if '#' in pageNumberStr:
pageNumberStr = pageNumberStr.split('#')[0]
if pageNumberStr.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumberStr)
searchPath = self.path.split('?page=')[0]
getPerson = \
personLookup(self.server.domain,
searchPath.replace('/followers', ''),
2019-11-03 15:27:29 +00:00
self.server.baseDir)
2019-07-22 09:38:02 +00:00
if getPerson:
2019-07-22 14:09:21 +00:00
if not self.server.session:
if self.server.debug:
print('DEBUG: creating new session')
2020-04-02 21:35:06 +00:00
self.server.session = \
2020-06-09 11:03:59 +00:00
createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to create session 19')
self._404()
self.server.GETbusy = False
2020-06-08 17:10:53 +00:00
return
2020-04-02 21:35:06 +00:00
msg = \
htmlProfile(self.server.defaultTimeline,
self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
self.server.projectVersion,
self.server.baseDir,
self.server.httpPrefix,
authorized,
self.server.ocapAlways,
getPerson, 'followers',
self.server.session,
self.server.cachedWebfingers,
self.server.personCache,
followers,
pageNumber,
followsPerPage).encode('utf-8')
self._set_headers('text/html',
len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-07-22 09:38:02 +00:00
return
2019-07-20 21:13:36 +00:00
else:
2019-09-25 09:22:10 +00:00
if self._fetchAuthenticated():
2020-04-02 21:35:06 +00:00
msg = json.dumps(followers,
ensure_ascii=False).encode('utf-8')
self._set_headers('application/json',
len(msg),
None, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-09-25 09:22:10 +00:00
else:
self._404()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-07-04 14:36:29 +00:00
return
2019-11-16 13:25:44 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 52)
2019-11-16 13:25:44 +00:00
2019-06-28 18:55:29 +00:00
# look up a person
2020-04-02 21:35:06 +00:00
getPerson = \
personLookup(self.server.domain, self.path,
2020-03-02 16:11:34 +00:00
self.server.baseDir)
2019-06-28 18:55:29 +00:00
if getPerson:
2019-08-24 11:23:12 +00:00
if self._requestHTTP():
2019-07-22 14:09:21 +00:00
if not self.server.session:
if self.server.debug:
print('DEBUG: creating new session')
2020-04-02 21:35:06 +00:00
self.server.session = \
2020-06-09 11:03:59 +00:00
createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: GET failed to create session 20')
self._404()
self.server.GETbusy = False
2020-06-08 17:10:53 +00:00
return
2020-04-02 21:35:06 +00:00
msg = \
htmlProfile(self.server.defaultTimeline,
self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
self.server.projectVersion,
self.server.baseDir,
self.server.httpPrefix,
authorized,
self.server.ocapAlways,
getPerson, 'posts',
self.server.session,
self.server.cachedWebfingers,
self.server.personCache,
None, None).encode('utf-8')
self._set_headers('text/html',
len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-07-20 21:13:36 +00:00
else:
2019-09-25 09:22:10 +00:00
if self._fetchAuthenticated():
2020-04-02 21:35:06 +00:00
msg = json.dumps(getPerson,
ensure_ascii=False).encode('utf-8')
self._set_headers('application/json',
len(msg),
None, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-09-25 09:22:10 +00:00
else:
self._404()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-06-28 18:55:29 +00:00
return
2019-11-16 13:25:44 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 53)
2019-11-16 13:25:44 +00:00
2019-06-28 18:55:29 +00:00
# check that a json file was requested
if not self.path.endswith('.json'):
2019-07-03 16:14:45 +00:00
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: GET Not json: ' + self.path +
' ' + self.server.baseDir)
2019-06-28 18:55:29 +00:00
self._404()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-06-28 18:55:29 +00:00
return
2019-09-25 09:22:10 +00:00
if not self._fetchAuthenticated():
if self.server.debug:
print('WARN: Unauthenticated GET')
self._404()
2020-04-16 11:48:00 +00:00
self.server.GETbusy = False
2019-11-15 18:59:15 +00:00
return
2020-03-22 21:16:02 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 54)
2019-11-16 13:25:44 +00:00
2019-06-28 18:55:29 +00:00
# check that the file exists
2020-04-02 21:35:06 +00:00
filename = self.server.baseDir + self.path
2019-06-28 18:55:29 +00:00
if os.path.isfile(filename):
2019-07-03 19:15:42 +00:00
with open(filename, 'r', encoding='utf-8') as File:
2020-04-02 21:35:06 +00:00
content = File.read()
contentJson = json.loads(content)
msg = json.dumps(contentJson,
ensure_ascii=False).encode('utf-8')
self._set_headers('application/json',
len(msg),
None, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2019-06-28 18:55:29 +00:00
else:
2019-07-03 16:14:45 +00:00
if self.server.debug:
print('DEBUG: GET Unknown file')
2019-06-28 18:55:29 +00:00
self._404()
2020-04-02 21:35:06 +00:00
self.server.GETbusy = False
2019-06-28 18:55:29 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkGETtimings(GETstartTime, GETtimings, 55)
2019-11-16 13:25:44 +00:00
2019-06-28 18:55:29 +00:00
def do_HEAD(self):
2020-04-02 21:35:06 +00:00
callingDomain = self.server.domainFull
2020-03-28 17:24:40 +00:00
if self.headers.get('Host'):
2020-04-02 21:35:06 +00:00
callingDomain = self.headers['Host']
2020-03-28 17:24:40 +00:00
if self.server.onionDomain:
if callingDomain != self.server.domain and \
callingDomain != self.server.domainFull and \
callingDomain != self.server.onionDomain:
2020-04-02 21:35:06 +00:00
print('HEAD domain blocked: ' + callingDomain)
2020-03-28 17:24:40 +00:00
self._400()
return
else:
if callingDomain != self.server.domain and \
callingDomain != self.server.domainFull:
2020-04-02 21:35:06 +00:00
print('HEAD domain blocked: ' + callingDomain)
2020-03-28 17:24:40 +00:00
self._400()
return
2020-04-02 21:35:06 +00:00
checkPath = self.path
etag = None
fileLength = -1
2019-12-04 13:54:59 +00:00
if '/media/' in self.path:
2020-06-19 20:04:22 +00:00
if self._pathIsImage() or \
2020-06-19 20:08:06 +00:00
self._pathIsVideo() or \
self._pathIsAudio():
2020-04-02 21:35:06 +00:00
mediaStr = self.path.split('/media/')[1]
mediaFilename = \
self.server.baseDir + '/media/' + mediaStr
2019-12-04 13:54:59 +00:00
if os.path.isfile(mediaFilename):
2020-04-02 21:35:06 +00:00
checkPath = mediaFilename
fileLength = os.path.getsize(mediaFilename)
mediaTagFilename = mediaFilename + '.etag'
if os.path.isfile(mediaTagFilename):
2019-12-04 13:54:59 +00:00
try:
2020-04-02 21:35:06 +00:00
with open(mediaTagFilename, 'r') as etagFile:
etag = etagFile.read()
except BaseException:
2019-12-04 13:54:59 +00:00
pass
else:
with open(mediaFilename, 'rb') as avFile:
2020-04-02 21:35:06 +00:00
mediaBinary = avFile.read()
etag = sha1(mediaBinary).hexdigest()
2019-12-04 13:54:59 +00:00
try:
2020-04-02 21:35:06 +00:00
with open(mediaTagFilename, 'w') as etagFile:
2019-12-04 13:54:59 +00:00
etagFile.write(etag)
2020-04-02 21:35:06 +00:00
except BaseException:
2019-12-04 13:54:59 +00:00
pass
2020-04-02 21:35:06 +00:00
mediaFileType = 'application/json'
2019-12-04 13:54:59 +00:00
if checkPath.endswith('.png'):
2020-04-02 21:35:06 +00:00
mediaFileType = 'image/png'
2019-12-04 13:54:59 +00:00
elif checkPath.endswith('.jpg'):
2020-04-02 21:35:06 +00:00
mediaFileType = 'image/jpeg'
2019-12-04 13:54:59 +00:00
elif checkPath.endswith('.gif'):
2020-04-02 21:35:06 +00:00
mediaFileType = 'image/gif'
2019-12-04 13:54:59 +00:00
elif checkPath.endswith('.webp'):
2020-04-02 21:35:06 +00:00
mediaFileType = 'image/webp'
2019-12-04 13:54:59 +00:00
elif checkPath.endswith('.mp4'):
2020-04-02 21:35:06 +00:00
mediaFileType = 'video/mp4'
2019-12-04 13:54:59 +00:00
elif checkPath.endswith('.ogv'):
2020-04-02 21:35:06 +00:00
mediaFileType = 'video/ogv'
2019-12-04 13:54:59 +00:00
elif checkPath.endswith('.mp3'):
2020-04-02 21:35:06 +00:00
mediaFileType = 'audio/mpeg'
2019-12-04 13:54:59 +00:00
elif checkPath.endswith('.ogg'):
2020-04-02 21:35:06 +00:00
mediaFileType = 'audio/ogg'
2019-12-04 13:54:59 +00:00
2020-04-02 21:35:06 +00:00
self._set_headers_head(mediaFileType, fileLength,
etag, callingDomain)
2019-07-01 21:01:43 +00:00
2020-04-16 09:01:33 +00:00
def _receiveNewPostProcess(self, postType: str, path: str, headers: {},
2020-04-02 21:35:06 +00:00
length: int, postBytes, boundary: str) -> int:
2019-11-01 21:23:59 +00:00
# Note: this needs to happen synchronously
2020-03-22 20:36:19 +00:00
# 0=this is not a new post
# 1=new post success
# -1=new post failed
# 2=new post canceled
2019-11-10 12:00:05 +00:00
if self.server.debug:
print('DEBUG: receiving POST')
2019-09-29 14:16:09 +00:00
if ' boundary=' in headers['Content-Type']:
2019-11-10 12:00:05 +00:00
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: receiving POST headers ' +
headers['Content-Type'])
nickname = None
nicknameStr = path.split('/users/')[1]
2019-09-29 13:30:24 +00:00
if '/' in nicknameStr:
2020-04-02 21:35:06 +00:00
nickname = nicknameStr.split('/')[0]
2019-09-29 13:30:24 +00:00
else:
return -1
2020-04-02 21:35:06 +00:00
length = int(headers['Content-Length'])
if length > self.server.maxPostLength:
2019-09-29 13:30:24 +00:00
print('POST size too large')
return -1
2020-04-02 21:35:06 +00:00
boundary = headers['Content-Type'].split('boundary=')[1]
2019-09-29 13:30:24 +00:00
if ';' in boundary:
2020-04-02 21:35:06 +00:00
boundary = boundary.split(';')[0]
2019-09-29 13:30:24 +00:00
# Note: we don't use cgi here because it's due to be deprecated
# in Python 3.8/3.10
# Instead we use the multipart mime parser from the email module
2019-11-10 11:54:45 +00:00
if self.server.debug:
print('DEBUG: extracting media from POST')
2020-04-02 21:35:06 +00:00
mediaBytes, postBytes = \
extractMediaInFormPOST(postBytes, boundary, 'attachpic')
2019-11-10 11:54:45 +00:00
if self.server.debug:
if mediaBytes:
2020-04-02 21:35:06 +00:00
print('DEBUG: media was found. ' +
str(len(mediaBytes)) + ' bytes')
2019-11-10 11:54:45 +00:00
else:
print('DEBUG: no media was found in POST')
2019-11-10 13:31:55 +00:00
# Note: a .temp extension is used here so that at no time is
# an image with metadata publicly exposed, even for a few mS
2020-04-02 21:35:06 +00:00
filenameBase = \
self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain + '/upload.temp'
2020-03-22 21:16:02 +00:00
2020-04-02 21:35:06 +00:00
filename, attachmentMediaType = \
saveMediaInFormPOST(mediaBytes, self.server.debug,
filenameBase)
2019-11-10 11:54:45 +00:00
if self.server.debug:
if filename:
2020-04-02 21:35:06 +00:00
print('DEBUG: POST media filename is ' + filename)
2019-11-10 11:54:45 +00:00
else:
print('DEBUG: no media filename in POST')
2019-11-10 13:31:55 +00:00
if filename:
if filename.endswith('.png') or \
filename.endswith('.jpg') or \
2019-12-04 18:52:27 +00:00
filename.endswith('.webp') or \
2019-11-10 13:31:55 +00:00
filename.endswith('.gif'):
if self.server.debug:
print('DEBUG: POST media removing metadata')
2020-04-02 21:35:06 +00:00
postImageFilename = filename.replace('.temp', '')
removeMetaData(filename, postImageFilename)
2019-11-10 13:31:55 +00:00
if os.path.isfile(postImageFilename):
2020-04-02 21:35:06 +00:00
print('POST media saved to ' + postImageFilename)
2019-11-10 13:31:55 +00:00
else:
2020-04-02 21:35:06 +00:00
print('ERROR: POST media could not be saved to ' +
2020-03-29 10:31:59 +00:00
postImageFilename)
2019-11-10 13:31:55 +00:00
else:
if os.path.isfile(filename):
2020-04-02 21:35:06 +00:00
os.rename(filename, filename.replace('.temp', ''))
2019-11-10 13:31:55 +00:00
2020-04-02 21:35:06 +00:00
fields = \
extractTextFieldsInPOST(postBytes, boundary,
self.server.debug)
2019-11-10 11:54:45 +00:00
if self.server.debug:
if fields:
2020-04-02 21:35:06 +00:00
print('DEBUG: text field extracted from POST ' +
str(fields))
2019-11-10 11:54:45 +00:00
else:
print('WARN: no text fields could be extracted from POST')
2019-11-10 11:37:24 +00:00
# process the received text fields from the POST
2020-04-02 21:35:06 +00:00
if not fields.get('message') and \
not fields.get('imageDescription'):
2019-09-29 13:30:24 +00:00
return -1
if fields.get('submitPost'):
2020-04-02 21:35:06 +00:00
if fields['submitPost'] != 'Submit':
2019-09-29 13:30:24 +00:00
return -1
else:
return 2
2019-07-27 20:30:58 +00:00
2019-09-29 13:30:24 +00:00
if not fields.get('imageDescription'):
2020-04-02 21:35:06 +00:00
fields['imageDescription'] = None
2019-09-29 13:30:24 +00:00
if not fields.get('subject'):
2020-04-02 21:35:06 +00:00
fields['subject'] = None
2019-09-29 13:30:24 +00:00
if not fields.get('replyTo'):
2020-04-02 21:35:06 +00:00
fields['replyTo'] = None
if not fields.get('schedulePost'):
2020-04-02 21:35:06 +00:00
fields['schedulePost'] = False
2020-01-12 20:35:39 +00:00
else:
2020-04-02 21:35:06 +00:00
fields['schedulePost'] = True
print('DEBUG: shedulePost ' + str(fields['schedulePost']))
2019-10-10 13:12:13 +00:00
if not fields.get('eventDate'):
2020-04-02 21:35:06 +00:00
fields['eventDate'] = None
2019-10-10 13:12:13 +00:00
if not fields.get('eventTime'):
2020-04-02 21:35:06 +00:00
fields['eventTime'] = None
2019-10-10 13:12:13 +00:00
if not fields.get('location'):
2020-04-02 21:35:06 +00:00
fields['location'] = None
2019-07-27 22:48:34 +00:00
2019-11-13 15:32:46 +00:00
# Store a file which contains the time in seconds
# since epoch when an attempt to post something was made.
# This is then used for active monthly users counts
2020-04-02 21:35:06 +00:00
lastUsedFilename = \
self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain + '/.lastUsed'
2019-11-13 15:32:46 +00:00
try:
2020-04-02 21:35:06 +00:00
lastUsedFile = open(lastUsedFilename, 'w')
2019-11-13 15:32:46 +00:00
if lastUsedFile:
lastUsedFile.write(str(int(time.time())))
lastUsedFile.close()
2020-04-02 21:35:06 +00:00
except BaseException:
2019-11-13 15:32:46 +00:00
pass
2020-04-02 21:35:06 +00:00
if postType == 'newpost':
messageJson = \
createPublicPost(self.server.baseDir,
nickname,
self.server.domain,
self.server.port,
self.server.httpPrefix,
fields['message'],
False, False, False,
filename, attachmentMediaType,
fields['imageDescription'],
self.server.useBlurHash,
fields['replyTo'], fields['replyTo'],
fields['subject'], fields['schedulePost'],
fields['eventDate'], fields['eventTime'],
2019-11-03 15:27:29 +00:00
fields['location'])
2019-09-29 13:30:24 +00:00
if messageJson:
2020-02-24 13:32:19 +00:00
if fields['schedulePost']:
return 1
2020-04-02 21:35:06 +00:00
if self._postToOutbox(messageJson, __version__, nickname):
populateReplies(self.server.baseDir,
self.server.httpPrefix,
self.server.domainFull,
messageJson,
self.server.maxReplies,
2020-02-24 13:32:19 +00:00
self.server.debug)
return 1
else:
2020-03-22 21:16:02 +00:00
return -1
2020-04-02 21:35:06 +00:00
elif postType == 'newblog':
messageJson = \
createBlogPost(self.server.baseDir, nickname,
self.server.domain, self.server.port,
self.server.httpPrefix,
fields['message'],
False, False, False,
filename, attachmentMediaType,
fields['imageDescription'],
self.server.useBlurHash,
fields['replyTo'], fields['replyTo'],
fields['subject'], fields['schedulePost'],
fields['eventDate'], fields['eventTime'],
2020-02-24 13:32:19 +00:00
fields['location'])
if messageJson:
2020-01-12 13:02:39 +00:00
if fields['schedulePost']:
return 1
2020-04-02 21:35:06 +00:00
if self._postToOutbox(messageJson, __version__, nickname):
populateReplies(self.server.baseDir,
self.server.httpPrefix,
self.server.domainFull,
messageJson,
self.server.maxReplies,
2019-09-29 13:30:24 +00:00
self.server.debug)
return 1
else:
2020-03-22 21:16:02 +00:00
return -1
2020-04-02 21:35:06 +00:00
elif postType == 'editblogpost':
2020-03-01 19:44:40 +00:00
print('Edited blog post received')
2020-04-02 21:35:06 +00:00
postFilename = \
locatePost(self.server.baseDir,
nickname, self.server.domain,
2020-03-01 19:44:40 +00:00
fields['postUrl'])
if os.path.isfile(postFilename):
2020-04-02 21:35:06 +00:00
postJsonObject = loadJson(postFilename)
2020-03-01 19:44:40 +00:00
if postJsonObject:
2020-04-02 21:35:06 +00:00
cachedFilename = \
self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain + \
'/postcache/' + \
fields['postUrl'].replace('/', '#') + '.html'
2020-03-01 19:44:40 +00:00
if os.path.isfile(cachedFilename):
print('Edited blog post, removing cached html')
try:
os.remove(cachedFilename)
2020-04-02 21:35:06 +00:00
except BaseException:
2020-03-01 19:44:40 +00:00
pass
2020-03-01 20:15:07 +00:00
# remove from memory cache
2020-04-02 21:35:06 +00:00
removePostFromCache(postJsonObject,
2020-03-01 20:15:07 +00:00
self.server.recentPostsCache)
# change the blog post title
2020-04-02 21:35:06 +00:00
postJsonObject['object']['summary'] = fields['subject']
2020-03-01 20:15:07 +00:00
# format message
2020-04-02 21:35:06 +00:00
tags = []
hashtagsDict = {}
mentionedRecipients = []
fields['message'] = \
addHtmlTags(self.server.baseDir,
self.server.httpPrefix,
nickname, self.server.domain,
fields['message'],
mentionedRecipients,
hashtagsDict, True)
2020-03-01 20:15:07 +00:00
# replace emoji with unicode
2020-04-02 21:35:06 +00:00
tags = []
for tagName, tag in hashtagsDict.items():
2020-03-01 20:15:07 +00:00
tags.append(tag)
# get list of tags
2020-04-02 21:35:06 +00:00
fields['message'] = \
replaceEmojiFromTags(fields['message'],
tags, 'content')
2020-03-22 21:16:02 +00:00
2020-04-02 21:35:06 +00:00
postJsonObject['object']['content'] = fields['message']
2020-03-01 20:57:53 +00:00
2020-04-02 21:35:06 +00:00
imgDescription = ''
2020-03-01 20:57:53 +00:00
if fields.get('imageDescription'):
2020-04-02 21:35:06 +00:00
imgDescription = fields['imageDescription']
2020-03-01 20:57:53 +00:00
if filename:
2020-04-02 21:35:06 +00:00
postJsonObject['object'] = \
attachMedia(self.server.baseDir,
self.server.httpPrefix,
self.server.domain,
self.server.port,
postJsonObject['object'],
filename,
attachmentMediaType,
imgDescription,
2020-03-01 21:14:57 +00:00
self.server.useBlurHash)
2020-03-01 20:57:53 +00:00
2020-03-01 21:11:01 +00:00
replaceYouTube(postJsonObject)
2020-04-02 21:35:06 +00:00
saveJson(postJsonObject, postFilename)
print('Edited blog post, resaved ' + postFilename)
2020-03-01 19:44:40 +00:00
return 1
else:
2020-04-02 21:35:06 +00:00
print('Edited blog post, unable to load json for ' +
2020-03-29 10:31:59 +00:00
postFilename)
2020-03-01 19:44:40 +00:00
else:
2020-04-02 21:35:06 +00:00
print('Edited blog post not found ' +
str(fields['postUrl']))
2020-03-22 21:16:02 +00:00
return -1
2020-04-02 21:35:06 +00:00
elif postType == 'newunlisted':
messageJson = \
createUnlistedPost(self.server.baseDir,
nickname,
self.server.domain, self.server.port,
self.server.httpPrefix,
fields['message'],
False, False, False,
filename, attachmentMediaType,
fields['imageDescription'],
self.server.useBlurHash,
fields['replyTo'],
fields['replyTo'],
fields['subject'],
fields['schedulePost'],
fields['eventDate'],
fields['eventTime'],
2019-11-03 15:27:29 +00:00
fields['location'])
2019-09-29 13:30:24 +00:00
if messageJson:
2020-01-12 13:02:39 +00:00
if fields['schedulePost']:
return 1
2020-04-02 21:35:06 +00:00
if self._postToOutbox(messageJson, __version__, nickname):
populateReplies(self.server.baseDir,
self.server.httpPrefix,
self.server.domain,
messageJson,
self.server.maxReplies,
2019-09-29 13:30:24 +00:00
self.server.debug)
return 1
else:
return -1
2020-04-02 21:35:06 +00:00
elif postType == 'newfollowers':
messageJson = \
createFollowersOnlyPost(self.server.baseDir,
nickname,
self.server.domain,
self.server.port,
self.server.httpPrefix,
fields['message'],
True, False, False,
filename, attachmentMediaType,
fields['imageDescription'],
self.server.useBlurHash,
fields['replyTo'],
fields['replyTo'],
fields['subject'],
fields['schedulePost'],
fields['eventDate'],
fields['eventTime'],
2019-11-03 15:27:29 +00:00
fields['location'])
2019-09-29 13:30:24 +00:00
if messageJson:
2020-01-12 13:02:39 +00:00
if fields['schedulePost']:
return 1
2020-04-02 21:35:06 +00:00
if self._postToOutbox(messageJson, __version__, nickname):
populateReplies(self.server.baseDir,
self.server.httpPrefix,
self.server.domain,
messageJson,
self.server.maxReplies,
2019-09-29 13:30:24 +00:00
self.server.debug)
return 1
else:
return -1
2020-04-02 21:35:06 +00:00
elif postType == 'newdm':
messageJson = None
2020-02-21 13:03:35 +00:00
print('A DM was posted')
2019-09-29 13:30:24 +00:00
if '@' in fields['message']:
2020-04-02 21:35:06 +00:00
messageJson = \
createDirectMessagePost(self.server.baseDir,
nickname,
self.server.domain,
self.server.port,
self.server.httpPrefix,
fields['message'],
True, False, False,
filename, attachmentMediaType,
fields['imageDescription'],
self.server.useBlurHash,
fields['replyTo'],
fields['replyTo'],
fields['subject'],
True, fields['schedulePost'],
fields['eventDate'],
fields['eventTime'],
2020-03-22 21:16:02 +00:00
fields['location'])
2019-09-29 13:30:24 +00:00
if messageJson:
2020-01-12 13:02:39 +00:00
if fields['schedulePost']:
return 1
2020-04-02 21:35:06 +00:00
# if self.server.debug:
print('DEBUG: new DM to ' +
str(messageJson['object']['to']))
if self._postToOutbox(messageJson, __version__, nickname):
populateReplies(self.server.baseDir,
self.server.httpPrefix,
self.server.domain,
messageJson,
self.server.maxReplies,
2019-09-29 13:30:24 +00:00
self.server.debug)
return 1
else:
return -1
2020-04-02 21:35:06 +00:00
elif postType == 'newreport':
2019-09-29 13:30:24 +00:00
if attachmentMediaType:
2020-04-02 21:35:06 +00:00
if attachmentMediaType != 'image':
2019-09-29 13:30:24 +00:00
return -1
# So as to be sure that this only goes to moderators
# and not accounts being reported we disable any
# included fediverse addresses by replacing '@' with '-at-'
2020-04-02 21:35:06 +00:00
fields['message'] = fields['message'].replace('@', '-at-')
messageJson = \
createReportPost(self.server.baseDir,
nickname,
self.server.domain, self.server.port,
self.server.httpPrefix,
fields['message'],
True, False, False,
filename, attachmentMediaType,
fields['imageDescription'],
self.server.useBlurHash,
self.server.debug, fields['subject'])
2019-09-29 13:30:24 +00:00
if messageJson:
2020-04-02 21:35:06 +00:00
if self._postToOutbox(messageJson, __version__, nickname):
2019-09-29 13:30:24 +00:00
return 1
else:
return -1
2020-04-02 21:35:06 +00:00
elif postType == 'newquestion':
2019-11-25 22:34:26 +00:00
if not fields.get('duration'):
return -1
if not fields.get('message'):
return -1
2020-04-02 21:35:06 +00:00
# questionStr = fields['message']
qOptions = []
2019-11-26 12:17:52 +00:00
for questionCtr in range(8):
2020-04-02 21:35:06 +00:00
if fields.get('questionOption' + str(questionCtr)):
qOptions.append(fields['questionOption' +
str(questionCtr)])
2019-11-25 22:34:26 +00:00
if not qOptions:
return -1
2020-04-02 21:35:06 +00:00
messageJson = \
createQuestionPost(self.server.baseDir,
nickname,
self.server.domain,
self.server.port,
self.server.httpPrefix,
fields['message'], qOptions,
False, False, False,
filename, attachmentMediaType,
fields['imageDescription'],
self.server.useBlurHash,
fields['subject'],
int(fields['duration']))
2019-11-25 22:34:26 +00:00
if messageJson:
if self.server.debug:
print('DEBUG: new Question')
2020-04-02 21:35:06 +00:00
if self._postToOutbox(messageJson, __version__, nickname):
2019-11-25 22:34:26 +00:00
return 1
return -1
2020-04-02 21:35:06 +00:00
elif postType == 'newshare':
2019-09-29 13:30:24 +00:00
if not fields.get('itemType'):
return -1
if not fields.get('category'):
return -1
if not fields.get('location'):
return -1
if not fields.get('duration'):
return -1
if attachmentMediaType:
2020-04-02 21:35:06 +00:00
if attachmentMediaType != 'image':
2019-09-29 13:30:24 +00:00
return -1
2020-04-02 21:35:06 +00:00
durationStr = fields['duration']
2019-11-02 10:46:56 +00:00
if durationStr:
if ' ' not in durationStr:
2020-04-02 21:35:06 +00:00
durationStr = durationStr + ' days'
addShare(self.server.baseDir,
self.server.httpPrefix,
nickname,
self.server.domain, self.server.port,
fields['subject'],
fields['message'],
filename,
fields['itemType'],
fields['category'],
fields['location'],
2019-11-02 10:46:56 +00:00
durationStr,
2019-09-29 13:30:24 +00:00
self.server.debug)
if filename:
if os.path.isfile(filename):
os.remove(filename)
2020-04-02 21:35:06 +00:00
self.postToNickname = nickname
2019-09-29 13:30:24 +00:00
return 1
return -1
2020-04-16 08:55:53 +00:00
def _receiveNewPost(self, postType: str, path: str) -> int:
2019-09-29 13:30:24 +00:00
"""A new post has been created
This creates a thread to send the new post
"""
2020-04-02 21:35:06 +00:00
pageNumber = 1
2019-11-12 13:53:14 +00:00
if '/users/' not in path:
2020-04-02 21:35:06 +00:00
print('Not receiving new post for ' + path +
2020-03-29 10:31:59 +00:00
' because /users/ not in path')
2019-11-12 13:53:14 +00:00
return None
2020-04-02 21:35:06 +00:00
if '?' + postType + '?' not in path:
print('Not receiving new post for ' + path +
' because ?' + postType + '? not in path')
2019-09-29 13:58:05 +00:00
return None
2020-04-02 21:35:06 +00:00
print('New post begins: ' + postType + ' ' + path)
2019-09-29 14:06:53 +00:00
2019-09-29 13:58:05 +00:00
if '?page=' in path:
2020-04-02 21:35:06 +00:00
pageNumberStr = path.split('?page=')[1]
2019-09-29 13:58:05 +00:00
if '?' in pageNumberStr:
2020-04-02 21:35:06 +00:00
pageNumberStr = pageNumberStr.split('?')[0]
2020-05-22 12:41:47 +00:00
if '#' in pageNumberStr:
pageNumberStr = pageNumberStr.split('#')[0]
2019-09-29 13:58:05 +00:00
if pageNumberStr.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumberStr)
path = path.split('?page=')[0]
2019-09-29 13:58:05 +00:00
2020-02-21 12:29:51 +00:00
# get the username who posted
2020-04-02 21:35:06 +00:00
newPostThreadName = None
2020-02-21 12:29:51 +00:00
if '/users/' in path:
2020-04-02 21:35:06 +00:00
newPostThreadName = path.split('/users/')[1]
2020-02-21 12:48:08 +00:00
if '/' in newPostThreadName:
2020-04-02 21:35:06 +00:00
newPostThreadName = newPostThreadName.split('/')[0]
2019-09-29 13:58:05 +00:00
if not newPostThreadName:
2020-04-02 21:35:06 +00:00
newPostThreadName = '*'
2020-02-21 12:29:51 +00:00
2019-09-29 13:58:05 +00:00
if self.server.newPostThread.get(newPostThreadName):
print('Waiting for previous new post thread to end')
2020-04-02 21:35:06 +00:00
waitCtr = 0
while (self.server.newPostThread[newPostThreadName].isAlive() and
waitCtr < 8):
2019-09-29 13:58:05 +00:00
time.sleep(1)
2020-04-02 21:35:06 +00:00
waitCtr += 1
if waitCtr >= 8:
print('Killing previous new post thread for ' +
newPostThreadName)
2019-09-29 13:58:05 +00:00
self.server.newPostThread[newPostThreadName].kill()
# make a copy of self.headers
2020-04-02 21:35:06 +00:00
headers = {}
headersWithoutCookie = {}
for dictEntryName, headerLine in self.headers.items():
headers[dictEntryName] = headerLine
if dictEntryName.lower() != 'cookie':
headersWithoutCookie[dictEntryName] = headerLine
print('New post headers: ' + str(headersWithoutCookie))
length = int(headers['Content-Length'])
if length > self.server.maxPostLength:
2019-11-16 11:29:57 +00:00
print('POST size too large')
2019-11-16 12:49:34 +00:00
return None
2019-11-16 11:29:57 +00:00
2019-11-22 18:37:07 +00:00
if not headers.get('Content-Type'):
if headers.get('Content-type'):
2020-04-02 21:35:06 +00:00
headers['Content-Type'] = headers['Content-type']
2019-11-22 18:37:07 +00:00
elif headers.get('content-type'):
2020-04-02 21:35:06 +00:00
headers['Content-Type'] = headers['content-type']
2019-11-22 18:37:07 +00:00
if headers.get('Content-Type'):
if ' boundary=' in headers['Content-Type']:
2020-04-02 21:35:06 +00:00
boundary = headers['Content-Type'].split('boundary=')[1]
2019-11-22 18:37:07 +00:00
if ';' in boundary:
2020-04-02 21:35:06 +00:00
boundary = boundary.split(';')[0]
2019-11-22 18:37:07 +00:00
2020-06-08 18:52:18 +00:00
try:
postBytes = self.rfile.read(length)
except BaseException:
print('ERROR: POST postBytes rfile.read failed')
return None
2020-03-22 21:16:02 +00:00
2019-11-22 18:37:07 +00:00
# second length check from the bytes received
# since Content-Length could be untruthful
2020-04-02 21:35:06 +00:00
length = len(postBytes)
if length > self.server.maxPostLength:
2019-11-22 18:37:07 +00:00
print('POST size too large')
return None
2020-03-22 21:16:02 +00:00
2020-03-29 10:31:59 +00:00
# Note sending new posts needs to be synchronous,
# otherwise any attachments can get mangled if
# other events happen during their decoding
2020-04-02 21:35:06 +00:00
print('Creating new post from: ' + newPostThreadName)
2020-04-16 09:01:33 +00:00
self._receiveNewPostProcess(postType,
2020-04-02 21:35:06 +00:00
path, headers, length,
postBytes, boundary)
2019-09-29 13:58:05 +00:00
return pageNumber
2020-03-22 21:16:02 +00:00
2019-06-28 18:55:29 +00:00
def do_POST(self):
2020-04-02 21:35:06 +00:00
POSTstartTime = time.time()
POSTtimings = []
2019-11-15 18:59:15 +00:00
2019-08-20 11:30:41 +00:00
if not self.server.session:
2019-11-15 19:44:20 +00:00
print('Starting new session from POST')
2020-04-02 21:35:06 +00:00
self.server.session = \
2020-06-09 11:03:59 +00:00
createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: POST failed to create session 1')
self._404()
return
2019-08-20 11:30:41 +00:00
2019-07-04 17:56:25 +00:00
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: POST to ' + self.server.baseDir +
' path: ' + self.path + ' busy: ' +
2019-07-06 17:00:22 +00:00
str(self.server.POSTbusy))
2019-07-01 14:30:48 +00:00
if self.server.POSTbusy:
2020-04-02 21:35:06 +00:00
currTimePOST = int(time.time())
if currTimePOST - self.server.lastPOST == 0:
2019-06-29 17:28:43 +00:00
self.send_response(429)
2019-06-29 17:27:32 +00:00
self.end_headers()
2020-03-22 21:16:02 +00:00
return
2020-04-02 21:35:06 +00:00
self.server.lastPOST = currTimePOST
2020-03-22 21:16:02 +00:00
2020-04-02 21:35:06 +00:00
callingDomain = self.server.domainFull
2020-03-27 11:46:36 +00:00
if self.headers.get('Host'):
2020-04-02 21:35:06 +00:00
callingDomain = self.headers['Host']
2020-03-28 15:42:27 +00:00
if self.server.onionDomain:
if callingDomain != self.server.domain and \
callingDomain != self.server.domainFull and \
callingDomain != self.server.onionDomain:
2020-04-02 21:35:06 +00:00
print('POST domain blocked: ' + callingDomain)
2020-03-28 15:42:27 +00:00
self._400()
return
else:
if callingDomain != self.server.domain and \
callingDomain != self.server.domainFull:
2020-04-02 21:35:06 +00:00
print('POST domain blocked: ' + callingDomain)
2020-03-28 15:42:27 +00:00
self._400()
return
2020-03-28 10:33:04 +00:00
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = True
2019-07-01 11:48:54 +00:00
if not self.headers.get('Content-type'):
2019-07-03 16:14:45 +00:00
print('Content-type header missing')
2020-04-16 11:48:00 +00:00
self._400()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-07-01 11:48:54 +00:00
return
2019-07-03 16:14:45 +00:00
2019-07-03 21:37:46 +00:00
# remove any trailing slashes from the path
if not self.path.endswith('confirm'):
2020-04-02 21:35:06 +00:00
self.path = self.path.replace('/outbox/', '/outbox')
self.path = self.path.replace('/tlblogs/', '/tlblogs')
self.path = self.path.replace('/inbox/', '/inbox')
self.path = self.path.replace('/shares/', '/shares')
self.path = self.path.replace('/sharedInbox/', '/sharedInbox')
2019-07-03 21:37:46 +00:00
2020-04-02 21:35:06 +00:00
if self.path == '/inbox':
2019-11-15 21:43:20 +00:00
if not self.server.enableSharedInbox:
self._503()
2020-04-16 11:48:00 +00:00
self.server.POSTbusy = False
2019-11-15 21:43:20 +00:00
return
2020-04-02 21:35:06 +00:00
cookie = None
2019-07-29 16:13:48 +00:00
if self.headers.get('Cookie'):
2020-04-02 21:35:06 +00:00
cookie = self.headers['Cookie']
2019-07-29 16:13:48 +00:00
2019-07-27 20:30:58 +00:00
# check authorization
2020-04-02 21:35:06 +00:00
authorized = self._isAuthorized()
2020-02-21 11:24:35 +00:00
if self.server.debug:
if authorized:
2019-07-27 20:30:58 +00:00
print('POST Authorization granted')
2020-02-21 11:24:35 +00:00
else:
2019-07-27 20:30:58 +00:00
print('POST Not authorized')
2019-07-29 16:13:48 +00:00
print(str(self.headers))
2019-07-27 20:30:58 +00:00
2020-02-24 22:50:55 +00:00
# if this is a POST to the outbox then check authentication
2020-04-02 21:35:06 +00:00
self.outboxAuthenticated = False
self.postToNickname = None
2019-07-24 22:38:42 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 1)
2020-03-22 21:16:02 +00:00
2019-07-24 22:38:42 +00:00
if self.path.startswith('/login'):
2019-07-25 10:56:24 +00:00
# get the contents of POST containing login credentials
2020-04-02 21:35:06 +00:00
length = int(self.headers['Content-length'])
if length > 512:
2019-07-25 10:56:24 +00:00
print('Login failed - credentials too long')
self.send_response(401)
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2020-03-22 21:16:02 +00:00
return
2020-06-08 18:52:18 +00:00
try:
loginParams = self.rfile.read(length).decode('utf-8')
except BaseException:
print('ERROR: POST rfile.read failed')
self.send_response(400)
self.end_headers()
self.server.POSTbusy = False
return
2020-04-02 21:35:06 +00:00
loginNickname, loginPassword, register = \
htmlGetLoginCredentials(loginParams, self.server.lastLoginTime)
2019-07-24 22:38:42 +00:00
if loginNickname:
2020-04-02 21:35:06 +00:00
self.server.lastLoginTime = int(time.time())
2019-08-08 13:38:33 +00:00
if register:
2020-04-02 21:35:06 +00:00
if not registerAccount(self.server.baseDir,
self.server.httpPrefix,
self.server.domain,
self.server.port,
loginNickname, loginPassword):
self.server.POSTbusy = False
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
2020-04-02 21:35:06 +00:00
self._redirect_headers('http://' +
self.server.onionDomain +
'/login',
cookie, callingDomain)
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
self._redirect_headers('http://' +
2020-06-03 19:14:24 +00:00
self.server.i2pDomain +
'/login',
cookie, callingDomain)
else:
2020-04-02 21:35:06 +00:00
self._redirect_headers(self.server.httpPrefix +
'://' +
self.server.domainFull +
'/login',
cookie, callingDomain)
2019-08-08 13:38:33 +00:00
return
2020-04-02 21:35:06 +00:00
authHeader = createBasicAuthHeader(loginNickname,
loginPassword)
if not authorizeBasic(self.server.baseDir, '/users/' +
loginNickname + '/outbox',
authHeader, False):
print('Login failed: ' + loginNickname)
2020-06-19 09:40:35 +00:00
self._clearLoginDetails(loginNickname, callingDomain)
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-07-24 22:38:42 +00:00
return
2019-07-25 10:56:24 +00:00
else:
2020-04-02 21:35:06 +00:00
if isSuspended(self.server.baseDir, loginNickname):
msg = \
htmlSuspended(self.server.baseDir).encode('utf-8')
self._login_headers('text/html',
len(msg), callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2020-03-22 21:16:02 +00:00
return
2019-07-25 10:56:24 +00:00
# login success - redirect with authorization
2020-04-02 21:35:06 +00:00
print('Login success: ' + loginNickname)
# re-activate account if needed
2020-04-02 21:35:06 +00:00
activateAccount(self.server.baseDir, loginNickname,
2020-03-29 10:31:59 +00:00
self.server.domain)
2020-04-02 21:35:06 +00:00
# This produces a deterministic token based
# on nick+password+salt
saltFilename = \
self.server.baseDir+'/accounts/' + \
loginNickname + '@' + self.server.domain + '/.salt'
salt = createPassword(32)
2019-10-25 12:52:07 +00:00
if os.path.isfile(saltFilename):
2019-10-25 16:48:53 +00:00
try:
with open(saltFilename, 'r') as fp:
2020-04-02 21:35:06 +00:00
salt = fp.read()
2019-10-25 16:48:53 +00:00
except Exception as e:
2020-04-02 21:35:06 +00:00
print('WARN: Unable to read salt for ' +
loginNickname + ' ' + str(e))
2019-10-25 12:52:07 +00:00
else:
2019-10-25 16:48:53 +00:00
try:
with open(saltFilename, 'w') as fp:
fp.write(salt)
except Exception as e:
2020-04-02 21:35:06 +00:00
print('WARN: Unable to save salt for ' +
loginNickname + ' ' + str(e))
tokenText = loginNickname + loginPassword + salt
token = sha256(tokenText.encode('utf-8')).hexdigest()
self.server.tokens[loginNickname] = token
loginHandle = loginNickname + '@' + self.server.domain
tokenFilename = \
self.server.baseDir+'/accounts/' + \
loginHandle + '/.token'
2019-10-25 16:48:53 +00:00
try:
with open(tokenFilename, 'w') as fp:
fp.write(token)
except Exception as e:
2020-04-02 21:35:06 +00:00
print('WARN: Unable to save token for ' +
loginNickname + ' ' + str(e))
personUpgradeActor(self.server.baseDir, None, loginHandle,
self.server.baseDir + '/accounts/' +
loginHandle + '.json')
index = self.server.tokens[loginNickname]
self.server.tokensLookup[index] = loginNickname
2020-06-19 09:40:35 +00:00
cookieStr = 'SET:epicyon=' + \
self.server.tokens[loginNickname] + '; SameSite=Strict'
2020-06-03 19:14:24 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
2020-06-19 09:40:35 +00:00
self._redirect_headers('http://' +
self.server.onionDomain +
'/users/' +
loginNickname + '/' +
self.server.defaultTimeline,
cookieStr, callingDomain)
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:40:35 +00:00
self._redirect_headers('http://' +
self.server.i2pDomain +
'/users/' +
loginNickname + '/' +
self.server.defaultTimeline,
cookieStr, callingDomain)
2020-03-27 14:09:48 +00:00
else:
2020-06-19 09:40:35 +00:00
self._redirect_headers(self.server.httpPrefix+'://' +
self.server.domainFull +
'/users/' +
loginNickname + '/' +
self.server.defaultTimeline,
cookieStr, callingDomain)
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-07-25 10:56:24 +00:00
return
2020-04-16 11:49:17 +00:00
self._200()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-11-15 18:59:15 +00:00
return
2019-07-24 22:38:42 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 2)
2019-11-16 10:40:35 +00:00
2019-08-02 09:52:12 +00:00
# update of profile/avatar from web interface
if authorized and self.path.endswith('/profiledata'):
2020-04-02 21:35:06 +00:00
usersPath = self.path.replace('/profiledata', '')
usersPath = usersPath.replace('/editprofile', '')
actorStr = \
self.server.httpPrefix + '://' + \
self.server.domainFull + usersPath
2019-08-02 09:52:12 +00:00
if ' boundary=' in self.headers['Content-type']:
2020-04-02 21:35:06 +00:00
boundary = self.headers['Content-type'].split('boundary=')[1]
2019-08-02 09:52:12 +00:00
if ';' in boundary:
2020-04-02 21:35:06 +00:00
boundary = boundary.split(';')[0]
2019-08-02 09:52:12 +00:00
2020-04-02 21:35:06 +00:00
nickname = getNicknameFromActor(actorStr)
2019-08-02 09:52:12 +00:00
if not nickname:
2020-03-29 10:31:59 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
2020-04-02 21:35:06 +00:00
actorStr = \
'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
actorStr = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
print('WARN: nickname not found in ' + actorStr)
self._redirect_headers(actorStr, cookie, callingDomain)
self.server.POSTbusy = False
2019-08-02 09:52:12 +00:00
return
2020-04-02 21:35:06 +00:00
length = int(self.headers['Content-length'])
if length > self.server.maxPostLength:
2020-03-29 10:31:59 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
2020-04-02 21:35:06 +00:00
actorStr = \
'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
actorStr = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
print('Maximum profile data length exceeded ' +
str(length))
self._redirect_headers(actorStr, cookie, callingDomain)
self.server.POSTbusy = False
2019-08-26 19:43:52 +00:00
return
2019-08-26 21:15:33 +00:00
try:
# read the bytes of the http form POST
postBytes = self.rfile.read(length)
except BaseException:
print('ERROR: failed to read bytes for POST')
2020-06-08 18:52:18 +00:00
self.send_response(400)
self.end_headers()
self.server.POSTbusy = False
return
2019-08-26 21:19:01 +00:00
2019-11-10 13:31:55 +00:00
# extract each image type
2020-04-02 21:35:06 +00:00
actorChanged = True
profileMediaTypes = ('avatar', 'image',
2020-06-10 16:56:23 +00:00
'banner', 'search_banner',
'instanceLogo')
2020-04-02 21:35:06 +00:00
profileMediaTypesUploaded = {}
2019-11-10 13:31:55 +00:00
for mType in profileMediaTypes:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: profile update extracting ' + mType +
2020-05-26 16:08:30 +00:00
' image or font from POST')
2020-04-02 21:35:06 +00:00
mediaBytes, postBytes = \
extractMediaInFormPOST(postBytes, boundary, mType)
2019-11-10 13:31:55 +00:00
if mediaBytes:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: profile update ' + mType +
2020-05-26 16:08:30 +00:00
' image or font was found. ' +
2020-04-02 21:35:06 +00:00
str(len(mediaBytes)) + ' bytes')
2019-11-10 13:31:55 +00:00
else:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: profile update, no ' + mType +
2020-05-26 16:08:30 +00:00
' image or font was found in POST')
2019-08-02 09:52:12 +00:00
continue
2019-11-10 13:31:55 +00:00
2020-04-02 21:35:06 +00:00
# Note: a .temp extension is used here so that at no
# time is an image with metadata publicly exposed,
# even for a few mS
2020-05-26 14:28:58 +00:00
if mType == 'instanceLogo':
filenameBase = \
self.server.baseDir + '/accounts/login.temp'
else:
2020-04-02 21:35:06 +00:00
filenameBase = \
self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain + \
'/' + mType + '.temp'
2019-11-10 13:31:55 +00:00
2020-05-28 08:52:25 +00:00
filename, attachmentMediaType = \
saveMediaInFormPOST(mediaBytes, self.server.debug,
filenameBase)
2019-11-10 13:31:55 +00:00
if filename:
2020-05-26 14:28:58 +00:00
print('Profile update POST ' + mType +
2020-05-26 16:08:30 +00:00
' media or font filename is ' + filename)
2019-11-10 13:31:55 +00:00
else:
2020-05-26 14:28:58 +00:00
print('Profile update, no ' + mType +
2020-05-26 16:08:30 +00:00
' media or font filename in POST')
2019-11-10 13:31:55 +00:00
continue
2020-04-02 21:35:06 +00:00
postImageFilename = filename.replace('.temp', '')
2020-05-28 08:52:25 +00:00
if self.server.debug:
print('DEBUG: POST ' + mType +
' media removing metadata')
# remove existing etag
if os.path.isfile(postImageFilename + '.etag'):
try:
os.remove(postImageFilename + '.etag')
except BaseException:
pass
2020-05-28 08:52:25 +00:00
removeMetaData(filename, postImageFilename)
2019-11-10 13:31:55 +00:00
if os.path.isfile(postImageFilename):
2020-04-02 21:35:06 +00:00
print('profile update POST ' + mType +
2020-05-26 14:28:58 +00:00
' image or font saved to ' + postImageFilename)
2020-05-28 08:52:25 +00:00
if mType != 'instanceLogo':
2020-04-02 21:35:06 +00:00
lastPartOfImageFilename = \
2020-03-29 10:31:59 +00:00
postImageFilename.split('/')[-1]
2020-04-02 21:35:06 +00:00
profileMediaTypesUploaded[mType] = \
2020-03-29 10:31:59 +00:00
lastPartOfImageFilename
2020-04-02 21:35:06 +00:00
actorChanged = True
2019-11-10 13:31:55 +00:00
else:
2020-04-02 21:35:06 +00:00
print('ERROR: profile update POST ' + mType +
2020-05-26 14:28:58 +00:00
' image or font could not be saved to ' +
2020-04-02 21:35:06 +00:00
postImageFilename)
2019-11-10 13:31:55 +00:00
2020-04-02 21:35:06 +00:00
fields = \
extractTextFieldsInPOST(postBytes, boundary,
2020-03-29 10:31:59 +00:00
self.server.debug)
2019-11-10 13:31:55 +00:00
if self.server.debug:
if fields:
2020-04-02 21:35:06 +00:00
print('DEBUG: profile update text ' +
'field extracted from POST ' + str(fields))
2019-11-10 13:31:55 +00:00
else:
2020-04-02 21:35:06 +00:00
print('WARN: profile update, no text ' +
2020-03-29 10:31:59 +00:00
'fields could be extracted from POST')
2019-11-10 13:31:55 +00:00
2020-04-02 21:35:06 +00:00
actorFilename = \
self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain + '.json'
2019-08-02 09:52:12 +00:00
if os.path.isfile(actorFilename):
2020-04-02 21:35:06 +00:00
actorJson = loadJson(actorFilename)
2019-11-14 13:30:54 +00:00
if actorJson:
# update the avatar/image url file extension
2020-04-02 21:35:06 +00:00
uploads = profileMediaTypesUploaded.items()
for mType, lastPart in uploads:
repStr = '/' + lastPart
if mType == 'avatar':
lastPartOfUrl = \
2020-03-29 10:31:59 +00:00
actorJson['icon']['url'].split('/')[-1]
2020-04-02 21:35:06 +00:00
srchStr = '/' + lastPartOfUrl
actorJson['icon']['url'] = \
actorJson['icon']['url'].replace(srchStr,
repStr)
elif mType == 'image':
lastPartOfUrl = \
actorJson['image']['url'].split('/')[-1]
srchStr = '/' + lastPartOfUrl
actorJson['image']['url'] = \
actorJson['image']['url'].replace(srchStr,
repStr)
skillCtr = 1
newSkills = {}
while skillCtr < 10:
skillName = \
fields.get('skillName' + str(skillCtr))
2019-08-09 08:46:38 +00:00
if not skillName:
2020-04-02 21:35:06 +00:00
skillCtr += 1
2019-08-09 08:46:38 +00:00
continue
2020-04-02 21:35:06 +00:00
skillValue = \
fields.get('skillValue' + str(skillCtr))
2019-08-09 08:46:38 +00:00
if not skillValue:
2020-04-02 21:35:06 +00:00
skillCtr += 1
2019-08-09 08:46:38 +00:00
continue
if not actorJson['skills'].get(skillName):
2020-04-02 21:35:06 +00:00
actorChanged = True
2019-08-09 08:46:38 +00:00
else:
2020-04-02 21:35:06 +00:00
if actorJson['skills'][skillName] != \
int(skillValue):
actorChanged = True
newSkills[skillName] = int(skillValue)
skillCtr += 1
if len(actorJson['skills'].items()) != \
len(newSkills.items()):
actorChanged = True
actorJson['skills'] = newSkills
2019-11-12 10:59:17 +00:00
if fields.get('password'):
if fields.get('passwordconfirm'):
2020-04-02 21:35:06 +00:00
if actorJson['password'] == \
fields['passwordconfirm']:
if len(actorJson['password']) > 2:
2019-11-12 10:59:17 +00:00
# set password
2020-04-02 21:35:06 +00:00
baseDir = self.server.baseDir
pwd = actorJson['password']
storeBasicCredentials(baseDir,
nickname,
pwd)
if fields.get('displayNickname'):
2020-04-02 21:35:06 +00:00
if fields['displayNickname'] != actorJson['name']:
actorJson['name'] = fields['displayNickname']
actorChanged = True
2019-11-23 14:13:25 +00:00
if fields.get('themeDropdown'):
2020-04-02 21:35:06 +00:00
setTheme(self.server.baseDir,
fields['themeDropdown'])
# self.server.iconsCache={}
2020-05-04 11:28:43 +00:00
2020-04-02 21:35:06 +00:00
currentEmailAddress = getEmailAddress(actorJson)
if fields.get('email'):
2020-04-02 21:35:06 +00:00
if fields['email'] != currentEmailAddress:
setEmailAddress(actorJson, fields['email'])
actorChanged = True
2019-12-17 23:21:59 +00:00
else:
if currentEmailAddress:
2020-04-02 21:35:06 +00:00
setEmailAddress(actorJson, '')
actorChanged = True
2020-05-04 11:28:43 +00:00
2020-04-02 21:35:06 +00:00
currentXmppAddress = getXmppAddress(actorJson)
2019-12-17 14:57:16 +00:00
if fields.get('xmppAddress'):
2020-04-02 21:35:06 +00:00
if fields['xmppAddress'] != currentXmppAddress:
setXmppAddress(actorJson,
fields['xmppAddress'])
actorChanged = True
2019-12-17 23:21:59 +00:00
else:
if currentXmppAddress:
2020-04-02 21:35:06 +00:00
setXmppAddress(actorJson, '')
actorChanged = True
2020-05-04 11:28:43 +00:00
2020-04-02 21:35:06 +00:00
currentMatrixAddress = getMatrixAddress(actorJson)
2019-12-17 15:25:34 +00:00
if fields.get('matrixAddress'):
2020-04-02 21:35:06 +00:00
if fields['matrixAddress'] != currentMatrixAddress:
setMatrixAddress(actorJson,
fields['matrixAddress'])
actorChanged = True
2019-12-17 23:21:59 +00:00
else:
if currentMatrixAddress:
2020-04-02 21:35:06 +00:00
setMatrixAddress(actorJson, '')
actorChanged = True
2020-05-04 11:28:43 +00:00
2020-04-02 21:35:06 +00:00
currentSSBAddress = getSSBAddress(actorJson)
2020-02-26 14:35:17 +00:00
if fields.get('ssbAddress'):
2020-04-02 21:35:06 +00:00
if fields['ssbAddress'] != currentSSBAddress:
setSSBAddress(actorJson,
fields['ssbAddress'])
actorChanged = True
2020-02-26 14:35:17 +00:00
else:
if currentSSBAddress:
2020-04-02 21:35:06 +00:00
setSSBAddress(actorJson, '')
actorChanged = True
2020-05-04 11:28:43 +00:00
currentBlogAddress = getBlogAddress(actorJson)
if fields.get('blogAddress'):
if fields['blogAddress'] != currentBlogAddress:
setBlogAddress(actorJson,
fields['blogAddress'])
actorChanged = True
else:
if currentBlogAddress:
setBlogAddress(actorJson, '')
actorChanged = True
2020-04-02 21:35:06 +00:00
currentToxAddress = getToxAddress(actorJson)
2020-03-22 14:42:26 +00:00
if fields.get('toxAddress'):
2020-04-02 21:35:06 +00:00
if fields['toxAddress'] != currentToxAddress:
setToxAddress(actorJson,
fields['toxAddress'])
actorChanged = True
2020-03-22 14:42:26 +00:00
else:
if currentToxAddress:
2020-04-02 21:35:06 +00:00
setToxAddress(actorJson, '')
actorChanged = True
2020-05-04 11:28:43 +00:00
2020-04-02 21:35:06 +00:00
currentPGPpubKey = getPGPpubKey(actorJson)
if fields.get('pgp'):
2020-04-02 21:35:06 +00:00
if fields['pgp'] != currentPGPpubKey:
setPGPpubKey(actorJson,
fields['pgp'])
actorChanged = True
2019-12-17 23:21:59 +00:00
else:
if currentPGPpubKey:
2020-04-02 21:35:06 +00:00
setPGPpubKey(actorJson, '')
actorChanged = True
2020-05-04 11:28:43 +00:00
2020-04-02 21:35:06 +00:00
currentDonateUrl = getDonationUrl(actorJson)
2019-11-06 22:56:55 +00:00
if fields.get('donateUrl'):
2020-04-02 21:35:06 +00:00
if fields['donateUrl'] != currentDonateUrl:
setDonationUrl(actorJson,
fields['donateUrl'])
actorChanged = True
2019-12-17 23:21:59 +00:00
else:
if currentDonateUrl:
2020-04-02 21:35:06 +00:00
setDonationUrl(actorJson, '')
actorChanged = True
2020-05-04 11:28:43 +00:00
if fields.get('instanceTitle'):
2020-04-02 21:35:06 +00:00
currInstanceTitle = \
getConfigParam(self.server.baseDir,
'instanceTitle')
if fields['instanceTitle'] != currInstanceTitle:
setConfigParam(self.server.baseDir,
'instanceTitle',
2020-03-29 10:31:59 +00:00
fields['instanceTitle'])
2020-04-02 21:35:06 +00:00
currInstanceDescriptionShort = \
getConfigParam(self.server.baseDir,
2020-03-29 10:31:59 +00:00
'instanceDescriptionShort')
if fields.get('instanceDescriptionShort'):
2020-04-02 21:35:06 +00:00
if fields['instanceDescriptionShort'] != \
currInstanceDescriptionShort:
iDesc = fields['instanceDescriptionShort']
setConfigParam(self.server.baseDir,
'instanceDescriptionShort',
iDesc)
2019-12-17 23:21:59 +00:00
else:
if currInstanceDescriptionShort:
2020-04-02 21:35:06 +00:00
setConfigParam(self.server.baseDir,
'instanceDescriptionShort', '')
currInstanceDescription = \
getConfigParam(self.server.baseDir,
2020-03-29 10:31:59 +00:00
'instanceDescription')
if fields.get('instanceDescription'):
2020-04-02 21:35:06 +00:00
if fields['instanceDescription'] != \
currInstanceDescription:
setConfigParam(self.server.baseDir,
'instanceDescription',
2020-03-29 10:31:59 +00:00
fields['instanceDescription'])
2019-12-17 23:21:59 +00:00
else:
if currInstanceDescription:
2020-04-02 21:35:06 +00:00
setConfigParam(self.server.baseDir,
'instanceDescription', '')
2019-08-02 09:52:12 +00:00
if fields.get('bio'):
2020-04-02 21:35:06 +00:00
if fields['bio'] != actorJson['summary']:
actorTags = {}
actorJson['summary'] = \
addHtmlTags(self.server.baseDir,
self.server.httpPrefix,
nickname,
self.server.domainFull,
fields['bio'], [], actorTags)
2019-08-09 16:18:00 +00:00
if actorTags:
2020-04-02 21:35:06 +00:00
actorJson['tag'] = []
for tagName, tag in actorTags.items():
2019-08-09 16:18:00 +00:00
actorJson['tag'].append(tag)
2020-04-02 21:35:06 +00:00
actorChanged = True
2019-12-17 23:21:59 +00:00
else:
if actorJson['summary']:
2020-04-02 21:35:06 +00:00
actorJson['summary'] = ''
actorChanged = True
2019-08-12 21:20:47 +00:00
if fields.get('moderators'):
2020-04-02 21:35:06 +00:00
adminNickname = \
getConfigParam(self.server.baseDir, 'admin')
if self.path.startswith('/users/' +
adminNickname + '/'):
moderatorsFile = \
self.server.baseDir + \
'/accounts/moderators.txt'
2019-08-12 21:20:47 +00:00
clearModeratorStatus(self.server.baseDir)
if ',' in fields['moderators']:
# if the list was given as comma separated
2020-04-02 21:35:06 +00:00
modFile = open(moderatorsFile, "w+")
mods = fields['moderators'].split(',')
for modNick in mods:
modNick = modNick.strip()
modDir = self.server.baseDir + \
'/accounts/' + modNick + \
'@' + self.server.domain
if os.path.isdir(modDir):
modFile.write(modNick + '\n')
2019-08-12 21:20:47 +00:00
modFile.close()
2020-04-02 21:35:06 +00:00
mods = fields['moderators'].split(',')
for modNick in mods:
modNick = modNick.strip()
modDir = self.server.baseDir + \
'/accounts/' + modNick + \
'@' + self.server.domain
if os.path.isdir(modDir):
setRole(self.server.baseDir,
modNick,
self.server.domain,
'instance', 'moderator')
2019-08-12 21:20:47 +00:00
else:
# nicknames on separate lines
2020-04-02 21:35:06 +00:00
modFile = open(moderatorsFile, "w+")
mods = fields['moderators'].split('\n')
for modNick in mods:
modNick = modNick.strip()
modDir = \
self.server.baseDir + \
'/accounts/' + modNick + \
'@' + self.server.domain
if os.path.isdir(modDir):
modFile.write(modNick + '\n')
2019-08-12 21:20:47 +00:00
modFile.close()
2020-04-02 21:35:06 +00:00
mods = fields['moderators'].split('\n')
for modNick in mods:
modNick = modNick.strip()
modDir = \
self.server.baseDir + \
'/accounts/' + \
modNick + '@' + \
self.server.domain
if os.path.isdir(modDir):
setRole(self.server.baseDir,
modNick,
self.server.domain,
'instance',
'moderator')
2020-03-22 21:16:02 +00:00
2020-01-14 10:23:17 +00:00
if fields.get('removeScheduledPosts'):
2020-04-02 21:35:06 +00:00
if fields['removeScheduledPosts'] == 'on':
removeScheduledPosts(self.server.baseDir,
nickname,
2020-03-29 10:31:59 +00:00
self.server.domain)
2020-05-26 20:47:52 +00:00
2020-04-02 21:35:06 +00:00
approveFollowers = False
2019-08-02 09:52:12 +00:00
if fields.get('approveFollowers'):
2020-04-02 21:35:06 +00:00
if fields['approveFollowers'] == 'on':
approveFollowers = True
if approveFollowers != \
actorJson['manuallyApprovesFollowers']:
actorJson['manuallyApprovesFollowers'] = \
2020-03-29 10:31:59 +00:00
approveFollowers
2020-04-02 21:35:06 +00:00
actorChanged = True
2020-05-26 20:47:52 +00:00
if fields.get('removeCustomFont'):
if fields['removeCustomFont'] == 'on':
2020-05-26 21:02:06 +00:00
fontExt = ('woff', 'woff2', 'otf', 'ttf')
for ext in fontExt:
if os.path.isfile(self.server.baseDir +
'/fonts/custom.' + ext):
os.remove(self.server.baseDir +
'/fonts/custom.' + ext)
if os.path.isfile(self.server.baseDir +
'/fonts/custom.' + ext +
'.etag'):
os.remove(self.server.baseDir +
'/fonts/custom.' + ext +
'.etag')
currTheme = getTheme(self.server.baseDir)
if currTheme:
setTheme(self.server.baseDir, currTheme)
2020-05-26 20:47:52 +00:00
2019-11-28 17:03:57 +00:00
if fields.get('mediaInstance'):
2020-04-02 21:35:06 +00:00
self.server.mediaInstance = False
self.server.defaultTimeline = 'inbox'
if fields['mediaInstance'] == 'on':
self.server.mediaInstance = True
self.server.defaultTimeline = 'tlmedia'
setConfigParam(self.server.baseDir,
"mediaInstance",
2019-11-28 17:21:38 +00:00
self.server.mediaInstance)
else:
if self.server.mediaInstance:
2020-04-02 21:35:06 +00:00
self.server.mediaInstance = False
self.server.defaultTimeline = 'inbox'
setConfigParam(self.server.baseDir,
"mediaInstance",
2019-11-28 17:21:38 +00:00
self.server.mediaInstance)
2020-02-24 14:39:25 +00:00
if fields.get('blogsInstance'):
2020-04-02 21:35:06 +00:00
self.server.blogsInstance = False
self.server.defaultTimeline = 'inbox'
if fields['blogsInstance'] == 'on':
self.server.blogsInstance = True
self.server.defaultTimeline = 'tlblogs'
setConfigParam(self.server.baseDir,
"blogsInstance",
2020-02-24 14:39:25 +00:00
self.server.blogsInstance)
else:
if self.server.blogsInstance:
2020-04-02 21:35:06 +00:00
self.server.blogsInstance = False
self.server.defaultTimeline = 'inbox'
setConfigParam(self.server.baseDir,
"blogsInstance",
2020-02-24 14:39:25 +00:00
self.server.blogsInstance)
# only receive DMs from accounts you follow
2020-04-02 21:35:06 +00:00
followDMsFilename = \
self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain + \
'/.followDMs'
followDMsActive = False
if fields.get('followDMs'):
2020-04-02 21:35:06 +00:00
if fields['followDMs'] == 'on':
followDMsActive = True
with open(followDMsFilename, "w") as fFile:
fFile.write('\n')
if not followDMsActive:
if os.path.isfile(followDMsFilename):
os.remove(followDMsFilename)
2020-02-05 14:57:10 +00:00
# remove Twitter retweets
2020-04-02 21:35:06 +00:00
removeTwitterFilename = \
self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain + \
'/.removeTwitter'
removeTwitterActive = False
2020-02-05 14:57:10 +00:00
if fields.get('removeTwitter'):
2020-04-02 21:35:06 +00:00
if fields['removeTwitter'] == 'on':
removeTwitterActive = True
with open(removeTwitterFilename, "w") as rFile:
rFile.write('\n')
2020-02-05 14:57:10 +00:00
if not removeTwitterActive:
if os.path.isfile(removeTwitterFilename):
2020-03-22 21:16:02 +00:00
os.remove(removeTwitterFilename)
# this account is a bot
2019-08-07 20:13:44 +00:00
if fields.get('isBot'):
2020-04-02 21:35:06 +00:00
if fields['isBot'] == 'on':
if actorJson['type'] != 'Service':
actorJson['type'] = 'Service'
actorChanged = True
2019-08-07 20:13:44 +00:00
else:
# this account is a group
2019-10-04 09:23:38 +00:00
if fields.get('isGroup'):
2020-04-02 21:35:06 +00:00
if fields['isGroup'] == 'on':
if actorJson['type'] != 'Group':
actorJson['type'] = 'Group'
actorChanged = True
2019-10-04 09:23:38 +00:00
else:
# this account is a person (default)
2020-04-02 21:35:06 +00:00
if actorJson['type'] != 'Person':
actorJson['type'] = 'Person'
actorChanged = True
2019-08-02 11:43:14 +00:00
# save filtered words list
2020-04-02 21:35:06 +00:00
filterFilename = \
self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain + \
'/filters.txt'
2019-08-02 11:43:14 +00:00
if fields.get('filteredWords'):
with open(filterFilename, "w") as filterfile:
filterfile.write(fields['filteredWords'])
else:
if os.path.isfile(filterFilename):
os.remove(filterFilename)
2020-02-19 19:06:23 +00:00
# word replacements
2020-04-02 21:35:06 +00:00
switchFilename = \
self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain + \
'/replacewords.txt'
2020-02-19 19:06:23 +00:00
if fields.get('switchWords'):
with open(switchFilename, "w") as switchfile:
switchfile.write(fields['switchWords'])
else:
if os.path.isfile(switchFilename):
os.remove(switchFilename)
2019-08-02 11:43:14 +00:00
# save blocked accounts list
2020-04-02 21:35:06 +00:00
blockedFilename = \
self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain + \
'/blocking.txt'
2019-08-02 11:43:14 +00:00
if fields.get('blocked'):
with open(blockedFilename, "w") as blockedfile:
blockedfile.write(fields['blocked'])
else:
if os.path.isfile(blockedFilename):
os.remove(blockedFilename)
# save allowed instances list
2020-04-02 21:35:06 +00:00
allowedInstancesFilename = \
self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain + \
2020-03-29 10:31:59 +00:00
'/allowedinstances.txt'
if fields.get('allowedInstances'):
2020-04-02 21:35:06 +00:00
with open(allowedInstancesFilename, "w") as aFile:
aFile.write(fields['allowedInstances'])
else:
if os.path.isfile(allowedInstancesFilename):
os.remove(allowedInstancesFilename)
# save git project names list
gitProjectsFilename = \
self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain + \
'/gitprojects.txt'
if fields.get('gitProjects'):
with open(gitProjectsFilename, "w") as aFile:
aFile.write(fields['gitProjects'].lower())
else:
if os.path.isfile(gitProjectsFilename):
os.remove(gitProjectsFilename)
2019-08-02 11:43:14 +00:00
# save actor json file within accounts
2019-08-02 09:52:12 +00:00
if actorChanged:
randomizeActorImages(actorJson)
2020-04-02 21:35:06 +00:00
saveJson(actorJson, actorFilename)
webfingerUpdate(self.server.baseDir,
nickname,
self.server.domain,
self.server.onionDomain,
self.server.cachedWebfingers)
2020-04-02 21:35:06 +00:00
# also copy to the actors cache and
# personCache in memory
storePersonInCache(self.server.baseDir,
actorJson['id'], actorJson,
2019-11-03 15:27:29 +00:00
self.server.personCache)
2020-01-20 17:54:07 +00:00
# clear any cached images for this actor
2020-04-02 21:35:06 +00:00
idStr = actorJson['id'].replace('/', '-')
removeAvatarFromCache(self.server.baseDir, idStr)
2020-01-20 17:54:07 +00:00
# save the actor to the cache
2020-04-02 21:35:06 +00:00
actorCacheFilename = \
self.server.baseDir + '/cache/actors/' + \
actorJson['id'].replace('/', '#') + '.json'
saveJson(actorJson, actorCacheFilename)
2020-01-20 12:40:59 +00:00
# send profile update to followers
2020-04-02 21:35:06 +00:00
ccStr = 'https://www.w3.org/ns/' + \
'activitystreams#Public'
updateActorJson = {
2019-08-22 16:15:02 +00:00
'type': 'Update',
'actor': actorJson['id'],
2020-04-02 21:35:06 +00:00
'to': [actorJson['id'] + '/followers'],
'cc': [ccStr],
2019-08-22 16:15:02 +00:00
'object': actorJson
}
2020-04-02 21:35:06 +00:00
self._postToOutbox(updateActorJson,
__version__, nickname)
if fields.get('deactivateThisAccount'):
2020-04-02 21:35:06 +00:00
if fields['deactivateThisAccount'] == 'on':
deactivateAccount(self.server.baseDir,
nickname,
2020-03-29 10:31:59 +00:00
self.server.domain)
2020-06-19 09:40:35 +00:00
self._clearLoginDetails(nickname,
callingDomain)
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
return
2020-04-02 21:35:06 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actorStr = \
'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
actorStr = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
self._redirect_headers(actorStr, cookie, callingDomain)
self.server.POSTbusy = False
2019-08-02 09:52:12 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 3)
2019-11-16 10:40:35 +00:00
2019-08-13 10:48:16 +00:00
# moderator action buttons
if authorized and '/users/' in self.path and \
2019-08-13 11:59:38 +00:00
self.path.endswith('/moderationaction'):
2020-04-02 21:35:06 +00:00
usersPath = self.path.replace('/moderationaction', '')
actorStr = \
self.server.httpPrefix + '://' + \
self.server.domainFull + usersPath
length = int(self.headers['Content-length'])
2020-06-08 18:52:18 +00:00
try:
moderationParams = self.rfile.read(length).decode('utf-8')
except BaseException:
print('ERROR: POST moderationParams rfile.read failed')
self.send_response(400)
self.end_headers()
self.server.POSTbusy = False
return
2019-08-13 11:59:38 +00:00
if '&' in moderationParams:
2020-04-02 21:35:06 +00:00
moderationText = None
moderationButton = None
2019-08-13 11:59:38 +00:00
for moderationStr in moderationParams.split('&'):
2019-08-13 13:58:48 +00:00
if moderationStr.startswith('moderationAction'):
2019-08-13 11:59:38 +00:00
if '=' in moderationStr:
2020-04-02 21:35:06 +00:00
moderationText = \
2019-11-03 15:27:29 +00:00
moderationStr.split('=')[1].strip()
2020-04-02 21:35:06 +00:00
moderationText = moderationText.replace('+', ' ')
2020-04-15 10:57:04 +00:00
moderationText = \
urllib.parse.unquote(moderationText.strip())
2019-08-13 17:25:39 +00:00
elif moderationStr.startswith('submitInfo'):
2020-04-02 21:35:06 +00:00
msg = htmlModerationInfo(self.server.translate,
self.server.baseDir,
self.server.httpPrefix)
msg = msg.encode('utf-8')
self._login_headers('text/html',
len(msg), callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2020-03-22 21:16:02 +00:00
return
2019-08-13 11:59:38 +00:00
elif moderationStr.startswith('submitBlock'):
2020-04-02 21:35:06 +00:00
moderationButton = 'block'
2019-08-13 11:59:38 +00:00
elif moderationStr.startswith('submitUnblock'):
2020-04-02 21:35:06 +00:00
moderationButton = 'unblock'
2019-08-13 11:59:38 +00:00
elif moderationStr.startswith('submitSuspend'):
2020-04-02 21:35:06 +00:00
moderationButton = 'suspend'
2019-08-13 11:59:38 +00:00
elif moderationStr.startswith('submitUnsuspend'):
2020-04-02 21:35:06 +00:00
moderationButton = 'unsuspend'
2019-08-13 11:59:38 +00:00
elif moderationStr.startswith('submitRemove'):
2020-04-02 21:35:06 +00:00
moderationButton = 'remove'
2019-08-13 11:59:38 +00:00
if moderationButton and moderationText:
2019-08-13 13:58:48 +00:00
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('moderationButton: ' + moderationButton)
print('moderationText: ' + moderationText)
nickname = moderationText
2019-08-13 11:59:38 +00:00
if nickname.startswith('http') or \
nickname.startswith('dat'):
2020-04-02 21:35:06 +00:00
nickname = getNicknameFromActor(nickname)
2019-08-13 11:59:38 +00:00
if '@' in nickname:
2020-04-02 21:35:06 +00:00
nickname = nickname.split('@')[0]
if moderationButton == 'suspend':
suspendAccount(self.server.baseDir, nickname,
2019-11-03 15:27:29 +00:00
self.server.domain)
2020-04-02 21:35:06 +00:00
if moderationButton == 'unsuspend':
unsuspendAccount(self.server.baseDir, nickname)
if moderationButton == 'block':
fullBlockDomain = None
2019-08-13 16:39:43 +00:00
if moderationText.startswith('http') or \
moderationText.startswith('dat'):
2020-04-02 21:35:06 +00:00
blockDomain, blockPort = \
2019-11-03 15:27:29 +00:00
getDomainFromActor(moderationText)
2020-04-02 21:35:06 +00:00
fullBlockDomain = blockDomain
2019-08-13 16:39:43 +00:00
if blockPort:
2020-04-02 21:35:06 +00:00
if blockPort != 80 and blockPort != 443:
if ':' not in blockDomain:
2020-04-02 21:35:06 +00:00
fullBlockDomain = \
blockDomain + ':' + str(blockPort)
2019-08-13 16:39:43 +00:00
if '@' in moderationText:
2020-04-02 21:35:06 +00:00
fullBlockDomain = moderationText.split('@')[1]
2019-08-14 10:32:15 +00:00
if fullBlockDomain or nickname.startswith('#'):
2020-04-02 21:35:06 +00:00
addGlobalBlock(self.server.baseDir,
nickname, fullBlockDomain)
if moderationButton == 'unblock':
fullBlockDomain = None
2019-08-13 16:39:43 +00:00
if moderationText.startswith('http') or \
moderationText.startswith('dat'):
2020-04-02 21:35:06 +00:00
blockDomain, blockPort = \
2019-11-03 15:27:29 +00:00
getDomainFromActor(moderationText)
2020-04-02 21:35:06 +00:00
fullBlockDomain = blockDomain
2019-08-13 16:39:43 +00:00
if blockPort:
2020-04-02 21:35:06 +00:00
if blockPort != 80 and blockPort != 443:
if ':' not in blockDomain:
2020-04-02 21:35:06 +00:00
fullBlockDomain = \
blockDomain + ':' + str(blockPort)
2019-08-13 16:39:43 +00:00
if '@' in moderationText:
2020-04-02 21:35:06 +00:00
fullBlockDomain = moderationText.split('@')[1]
2019-08-14 10:32:15 +00:00
if fullBlockDomain or nickname.startswith('#'):
2020-04-02 21:35:06 +00:00
removeGlobalBlock(self.server.baseDir,
nickname, fullBlockDomain)
if moderationButton == 'remove':
2019-08-13 13:58:48 +00:00
if '/statuses/' not in moderationText:
2020-04-02 21:35:06 +00:00
removeAccount(self.server.baseDir,
nickname,
self.server.domain,
2019-08-13 13:58:48 +00:00
self.server.port)
else:
2020-03-22 21:16:02 +00:00
# remove a post or thread
2020-04-02 21:35:06 +00:00
postFilename = \
locatePost(self.server.baseDir,
nickname, self.server.domain,
2019-08-13 13:58:48 +00:00
moderationText)
if postFilename:
2020-04-02 21:35:06 +00:00
if canRemovePost(self.server.baseDir,
nickname,
self.server.domain,
self.server.port,
2020-03-22 21:16:02 +00:00
moderationText):
2020-04-02 21:35:06 +00:00
deletePost(self.server.baseDir,
self.server.httpPrefix,
nickname, self.server.domain,
postFilename,
self.server.debug)
2020-04-02 21:35:06 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actorStr = \
'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
actorStr = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
self._redirect_headers(actorStr + '/moderation',
cookie, callingDomain)
self.server.POSTbusy = False
2019-08-13 10:48:16 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 4)
2019-11-16 10:40:35 +00:00
2020-04-02 21:35:06 +00:00
searchForEmoji = False
2019-08-19 20:11:38 +00:00
if self.path.endswith('/searchhandleemoji'):
2020-04-02 21:35:06 +00:00
searchForEmoji = True
self.path = self.path.replace('/searchhandleemoji',
'/searchhandle')
2019-08-19 20:11:38 +00:00
if self.server.debug:
print('DEBUG: searching for emoji')
2020-04-02 21:35:06 +00:00
print('authorized: ' + str(authorized))
2019-08-19 20:01:29 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 5)
2019-11-16 10:40:35 +00:00
2019-09-13 19:01:33 +00:00
# a vote/question/poll is posted
2020-04-02 21:35:06 +00:00
if (authorized and
(self.path.endswith('/question') or
'/question?page=' in self.path)):
pageNumber = 1
2019-09-13 19:01:33 +00:00
if '?page=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumberStr = self.path.split('?page=')[1]
2020-05-22 12:41:47 +00:00
if '#' in pageNumberStr:
pageNumberStr = pageNumberStr.split('#')[0]
2019-09-13 19:01:33 +00:00
if pageNumberStr.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumberStr)
self.path = self.path.split('?page=')[0]
2019-09-13 19:01:33 +00:00
# the actor who votes
2020-04-02 21:35:06 +00:00
usersPath = self.path.replace('/question', '')
actor = \
self.server.httpPrefix + '://' + \
self.server.domainFull + usersPath
nickname = getNicknameFromActor(actor)
2019-09-13 19:01:33 +00:00
if not nickname:
2020-04-02 21:35:06 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actor = 'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
actor = 'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
self._redirect_headers(actor + '/' +
self.server.defaultTimeline +
'?page=' + str(pageNumber),
cookie, callingDomain)
self.server.POSTbusy = False
2019-09-13 19:01:33 +00:00
return
# get the parameters
2020-04-02 21:35:06 +00:00
length = int(self.headers['Content-length'])
2020-06-08 18:52:18 +00:00
try:
questionParams = self.rfile.read(length).decode('utf-8')
except BaseException:
print('ERROR: POST questionParams rfile.read failed')
self.send_response(400)
self.end_headers()
self.server.POSTbusy = False
return
2020-04-02 21:35:06 +00:00
questionParams = questionParams.replace('+', ' ')
2020-04-15 10:57:04 +00:00
questionParams = questionParams.replace('%3F', '')
questionParams = \
urllib.parse.unquote(questionParams.strip())
2019-09-13 19:01:33 +00:00
# post being voted on
2020-04-02 21:35:06 +00:00
messageId = None
2019-09-13 19:01:33 +00:00
if 'messageId=' in questionParams:
2020-04-02 21:35:06 +00:00
messageId = questionParams.split('messageId=')[1]
2019-09-13 19:01:33 +00:00
if '&' in messageId:
2020-04-02 21:35:06 +00:00
messageId = messageId.split('&')[0]
answer = None
2019-09-13 19:01:33 +00:00
if 'answer=' in questionParams:
2020-04-02 21:35:06 +00:00
answer = questionParams.split('answer=')[1]
2019-09-13 19:01:33 +00:00
if '&' in answer:
2020-04-02 21:35:06 +00:00
answer = answer.split('&')[0]
self._sendReplyToQuestion(nickname, messageId, answer)
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actor = 'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
actor = 'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
self._redirect_headers(actor + '/' +
self.server.defaultTimeline +
'?page=' + str(pageNumber), cookie,
2020-03-28 17:24:40 +00:00
callingDomain)
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2020-03-22 21:16:02 +00:00
return
2019-09-13 19:01:33 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 6)
2019-11-16 10:40:35 +00:00
2019-08-14 09:45:51 +00:00
# a search was made
2020-04-02 21:35:06 +00:00
if ((authorized or searchForEmoji) and
(self.path.endswith('/searchhandle') or
'/searchhandle?page=' in self.path)):
2019-08-14 09:45:51 +00:00
# get the page number
2020-04-02 21:35:06 +00:00
pageNumber = 1
2019-08-14 09:45:51 +00:00
if '/searchhandle?page=' in self.path:
2020-04-02 21:35:06 +00:00
pageNumberStr = self.path.split('/searchhandle?page=')[1]
2020-05-22 12:41:47 +00:00
if '#' in pageNumberStr:
pageNumberStr = pageNumberStr.split('#')[0]
2019-08-14 09:45:51 +00:00
if pageNumberStr.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumberStr)
self.path = self.path.split('?page=')[0]
usersPath = self.path.replace('/searchhandle', '')
actorStr = \
self.server.httpPrefix + '://' + \
self.server.domainFull + usersPath
length = int(self.headers['Content-length'])
2020-06-08 18:52:18 +00:00
try:
searchParams = self.rfile.read(length).decode('utf-8')
except BaseException:
print('ERROR: POST searchParams rfile.read failed')
self.send_response(400)
self.end_headers()
self.server.POSTbusy = False
return
2019-12-17 13:09:08 +00:00
if 'submitBack=' in searchParams:
# go back on search screen
2020-04-02 21:35:06 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actorStr = 'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
actorStr = 'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
self._redirect_headers(actorStr + '/' +
self.server.defaultTimeline,
cookie, callingDomain)
self.server.POSTbusy = False
2020-03-22 21:16:02 +00:00
return
2019-07-30 22:34:04 +00:00
if 'searchtext=' in searchParams:
2020-04-02 21:35:06 +00:00
searchStr = searchParams.split('searchtext=')[1]
2019-07-30 22:34:04 +00:00
if '&' in searchStr:
2020-04-02 21:35:06 +00:00
searchStr = searchStr.split('&')[0]
2020-04-15 10:57:04 +00:00
searchStr = searchStr.replace('+', ' ')
searchStr = \
urllib.parse.unquote(searchStr.strip())
2020-05-22 11:32:38 +00:00
searchStr2 = searchStr.lower().strip('\n').strip('\r')
print('searchStr: ' + searchStr)
2019-08-19 20:01:29 +00:00
if searchForEmoji:
2020-04-02 21:35:06 +00:00
searchStr = ':' + searchStr + ':'
2020-03-22 21:16:02 +00:00
if searchStr.startswith('#'):
2020-04-02 21:35:06 +00:00
nickname = getNicknameFromActor(actorStr)
2019-08-10 10:54:52 +00:00
# hashtag search
2020-04-02 21:35:06 +00:00
hashtagStr = \
htmlHashtagSearch(nickname,
self.server.domain,
self.server.port,
self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
self.server.baseDir,
searchStr[1:], 1,
maxPostsInFeed,
self.server.session,
self.server.cachedWebfingers,
self.server.personCache,
self.server.httpPrefix,
2019-08-14 20:12:27 +00:00
self.server.projectVersion)
2019-08-10 10:54:52 +00:00
if hashtagStr:
2020-04-02 21:35:06 +00:00
msg = hashtagStr.encode('utf-8')
self._login_headers('text/html',
len(msg), callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-08-10 10:54:52 +00:00
return
2020-03-22 21:16:02 +00:00
elif searchStr.startswith('*'):
2019-08-27 22:50:40 +00:00
# skill search
2020-04-02 21:35:06 +00:00
searchStr = searchStr.replace('*', '').strip()
skillStr = \
htmlSkillsSearch(self.server.translate,
self.server.baseDir,
self.server.httpPrefix,
searchStr,
self.server.instanceOnlySkillsSearch,
2019-08-28 10:11:06 +00:00
64)
2019-08-27 22:50:40 +00:00
if skillStr:
2020-04-02 21:35:06 +00:00
msg = skillStr.encode('utf-8')
self._login_headers('text/html',
2020-04-11 12:37:20 +00:00
len(msg), callingDomain)
self._write(msg)
self.server.POSTbusy = False
return
elif searchStr.startswith('!'):
# your post history search
nickname = getNicknameFromActor(actorStr)
searchStr = searchStr.replace('!', '').strip()
historyStr = \
htmlHistorySearch(self.server.translate,
self.server.baseDir,
self.server.httpPrefix,
nickname,
self.server.domain,
searchStr,
maxPostsInFeed,
pageNumber,
self.server.projectVersion,
self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.session,
self.server.cachedWebfingers,
self.server.personCache,
self.server.port)
if historyStr:
msg = historyStr.encode('utf-8')
self._login_headers('text/html',
2020-04-02 21:35:06 +00:00
len(msg), callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-08-27 22:50:40 +00:00
return
2019-08-13 21:32:18 +00:00
elif '@' in searchStr:
2019-08-10 10:54:52 +00:00
# profile search
2020-04-02 21:35:06 +00:00
nickname = getNicknameFromActor(actorStr)
2019-07-30 22:34:04 +00:00
if not self.server.session:
2020-04-02 21:35:06 +00:00
self.server.session = \
2020-06-09 11:03:59 +00:00
createSession(self.server.proxyType)
2020-06-08 17:10:53 +00:00
if not self.server.session:
print('ERROR: POST failed to create session 2')
self._404()
self.server.POSTbusy = False
2020-06-08 17:10:53 +00:00
return
2020-04-02 21:35:06 +00:00
profilePathStr = self.path.replace('/searchhandle', '')
profileStr = \
htmlProfileAfterSearch(self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
self.server.baseDir,
profilePathStr,
self.server.httpPrefix,
nickname,
self.server.domain,
self.server.port,
searchStr,
self.server.session,
self.server.cachedWebfingers,
self.server.personCache,
self.server.debug,
2019-08-14 20:12:27 +00:00
self.server.projectVersion)
2019-07-30 22:34:04 +00:00
if profileStr:
2020-04-02 21:35:06 +00:00
msg = profileStr.encode('utf-8')
self._login_headers('text/html',
len(msg), callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-08-19 19:02:28 +00:00
return
2019-09-01 12:33:05 +00:00
else:
2020-04-02 21:35:06 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
actorStr = 'http://' + self.server.onionDomain + \
usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
actorStr = 'http://' + self.server.i2pDomain + \
2020-06-03 19:14:24 +00:00
usersPath
2020-04-02 21:35:06 +00:00
self._redirect_headers(actorStr + '/search',
cookie, callingDomain)
self.server.POSTbusy = False
2020-03-22 21:16:02 +00:00
return
2020-04-02 21:35:06 +00:00
elif (searchStr.startswith(':') or
2020-05-22 11:32:38 +00:00
searchStr2.endswith(' emoji')):
2019-08-19 19:02:28 +00:00
# eg. "cat emoji"
2020-05-22 11:32:38 +00:00
if searchStr2.endswith(' emoji'):
2020-04-02 21:35:06 +00:00
searchStr = \
2020-05-22 11:32:38 +00:00
searchStr2.replace(' emoji', '')
2019-08-19 19:02:28 +00:00
# emoji search
2020-04-02 21:35:06 +00:00
emojiStr = \
htmlSearchEmoji(self.server.translate,
self.server.baseDir,
self.server.httpPrefix,
2019-12-10 14:48:08 +00:00
searchStr)
2019-08-19 19:02:28 +00:00
if emojiStr:
2020-04-02 21:35:06 +00:00
msg = emojiStr.encode('utf-8')
self._login_headers('text/html',
len(msg), callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-07-30 22:34:04 +00:00
return
2019-08-13 21:32:18 +00:00
else:
# shared items search
2020-04-02 21:35:06 +00:00
sharedItemsStr = \
htmlSearchSharedItems(self.server.translate,
self.server.baseDir,
searchStr, pageNumber,
maxPostsInFeed,
self.server.httpPrefix,
self.server.domainFull,
2019-08-25 21:59:42 +00:00
actorStr)
2019-08-13 21:32:18 +00:00
if sharedItemsStr:
2020-04-02 21:35:06 +00:00
msg = sharedItemsStr.encode('utf-8')
self._login_headers('text/html',
len(msg), callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-08-13 21:32:18 +00:00
return
if callingDomain.endswith('.onion') and self.server.onionDomain:
2020-04-02 21:35:06 +00:00
actorStr = 'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif callingDomain.endswith('.i2p') and self.server.i2pDomain:
2020-06-19 09:50:00 +00:00
actorStr = 'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
self._redirect_headers(actorStr + '/' +
self.server.defaultTimeline,
cookie, callingDomain)
self.server.POSTbusy = False
2019-07-30 22:34:04 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 7)
2019-11-16 10:40:35 +00:00
2019-08-26 09:31:45 +00:00
# removes a shared item
if authorized and self.path.endswith('/rmshare'):
2020-04-02 21:35:06 +00:00
usersPath = self.path.split('/rmshare')[0]
originPathStr = \
self.server.httpPrefix + '://' + \
self.server.domainFull + usersPath
length = int(self.headers['Content-length'])
2020-06-08 18:52:18 +00:00
try:
removeShareConfirmParams = \
self.rfile.read(length).decode('utf-8')
except BaseException:
print('ERROR: POST removeShareConfirmParams rfile.read failed')
self.send_response(400)
self.end_headers()
self.server.POSTbusy = False
return
2019-08-26 09:31:45 +00:00
if '&submitYes=' in removeShareConfirmParams:
2020-04-02 21:35:06 +00:00
removeShareConfirmParams = \
removeShareConfirmParams.replace('+', ' ').strip()
2020-04-15 10:57:04 +00:00
removeShareConfirmParams = \
urllib.parse.unquote(removeShareConfirmParams)
2020-04-02 21:35:06 +00:00
shareActor = removeShareConfirmParams.split('actor=')[1]
2019-08-26 09:31:45 +00:00
if '&' in shareActor:
2020-04-02 21:35:06 +00:00
shareActor = shareActor.split('&')[0]
shareName = removeShareConfirmParams.split('shareName=')[1]
2019-08-26 09:31:45 +00:00
if '&' in shareName:
2020-04-02 21:35:06 +00:00
shareName = shareName.split('&')[0]
shareNickname = getNicknameFromActor(shareActor)
2019-09-02 09:43:43 +00:00
if shareNickname:
2020-04-02 21:35:06 +00:00
shareDomain, sharePort = getDomainFromActor(shareActor)
removeShare(self.server.baseDir,
shareNickname, shareDomain, shareName)
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
originPathStr = \
'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
originPathStr = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
self._redirect_headers(originPathStr + '/tlshares',
cookie, callingDomain)
self.server.POSTbusy = False
2019-08-26 09:31:45 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 8)
2019-11-16 10:40:35 +00:00
2019-08-27 12:47:11 +00:00
# removes a post
if authorized and self.path.endswith('/rmpost'):
2020-04-02 21:35:06 +00:00
pageNumber = 1
usersPath = self.path.split('/rmpost')[0]
originPathStr = \
self.server.httpPrefix + '://' + \
self.server.domainFull + usersPath
length = int(self.headers['Content-length'])
2020-06-08 18:52:18 +00:00
try:
removePostConfirmParams = \
self.rfile.read(length).decode('utf-8')
except BaseException:
print('ERROR: POST removePostConfirmParams rfile.read failed')
self.send_response(400)
self.end_headers()
self.server.POSTbusy = False
return
2019-08-27 12:47:11 +00:00
if '&submitYes=' in removePostConfirmParams:
2020-04-02 21:35:06 +00:00
removePostConfirmParams = \
2020-04-15 10:57:04 +00:00
urllib.parse.unquote(removePostConfirmParams)
2020-04-02 21:35:06 +00:00
removeMessageId = \
2019-11-03 15:27:29 +00:00
removePostConfirmParams.split('messageId=')[1]
2019-08-27 12:47:11 +00:00
if '&' in removeMessageId:
2020-04-02 21:35:06 +00:00
removeMessageId = removeMessageId.split('&')[0]
2019-09-04 11:29:44 +00:00
if 'pageNumber=' in removePostConfirmParams:
2020-04-02 21:35:06 +00:00
pageNumberStr = \
removePostConfirmParams.split('pageNumber=')[1]
2019-09-04 11:29:44 +00:00
if '&' in pageNumberStr:
2020-04-02 21:35:06 +00:00
pageNumberStr = pageNumberStr.split('&')[0]
2019-09-04 11:29:44 +00:00
if pageNumberStr.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumberStr)
yearStr = None
2020-02-23 13:28:27 +00:00
if 'year=' in removePostConfirmParams:
2020-04-02 21:35:06 +00:00
yearStr = removePostConfirmParams.split('year=')[1]
2020-02-23 13:28:27 +00:00
if '&' in yearStr:
2020-04-02 21:35:06 +00:00
yearStr = yearStr.split('&')[0]
monthStr = None
2020-02-23 13:28:27 +00:00
if 'month=' in removePostConfirmParams:
2020-04-02 21:35:06 +00:00
monthStr = removePostConfirmParams.split('month=')[1]
2020-02-23 13:28:27 +00:00
if '&' in monthStr:
2020-04-02 21:35:06 +00:00
monthStr = monthStr.split('&')[0]
2019-08-27 12:47:11 +00:00
if '/statuses/' in removeMessageId:
2020-04-02 21:35:06 +00:00
removePostActor = removeMessageId.split('/statuses/')[0]
if originPathStr in removePostActor:
2020-04-02 21:35:06 +00:00
toList = ['https://www.w3.org/ns/activitystreams#Public',
removePostActor]
deleteJson = {
"@context": "https://www.w3.org/ns/activitystreams",
'actor': removePostActor,
'object': removeMessageId,
2020-04-02 21:35:06 +00:00
'to': toList,
'cc': [removePostActor+'/followers'],
'type': 'Delete'
}
2020-04-02 21:35:06 +00:00
self.postToNickname = getNicknameFromActor(removePostActor)
2019-09-02 09:43:43 +00:00
if self.postToNickname:
2020-02-23 13:28:27 +00:00
if monthStr and yearStr:
if monthStr.isdigit() and yearStr.isdigit():
2020-04-02 21:35:06 +00:00
removeCalendarEvent(self.server.baseDir,
self.postToNickname,
self.server.domain,
int(yearStr),
int(monthStr),
2020-02-23 13:28:27 +00:00
removeMessageId)
2019-09-04 11:29:44 +00:00
self._postToOutboxThread(deleteJson)
2020-04-02 21:35:06 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
originPathStr = 'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
originPathStr = 'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
if pageNumber == 1:
self._redirect_headers(originPathStr + '/outbox', cookie,
2020-03-29 10:31:59 +00:00
callingDomain)
2019-09-04 11:29:44 +00:00
else:
2020-04-02 21:35:06 +00:00
self._redirect_headers(originPathStr + '/outbox?page=' +
str(pageNumber),
cookie, callingDomain)
self.server.POSTbusy = False
2019-08-27 12:47:11 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 9)
2019-11-16 10:40:35 +00:00
2019-07-29 16:13:48 +00:00
# decision to follow in the web interface is confirmed
2019-07-29 20:36:26 +00:00
if authorized and self.path.endswith('/followconfirm'):
2020-04-02 21:35:06 +00:00
usersPath = self.path.split('/followconfirm')[0]
originPathStr = self.server.httpPrefix + '://' + \
self.server.domainFull + usersPath
followerNickname = getNicknameFromActor(originPathStr)
length = int(self.headers['Content-length'])
2020-06-08 18:52:18 +00:00
try:
followConfirmParams = self.rfile.read(length).decode('utf-8')
except BaseException:
print('ERROR: POST followConfirmParams rfile.read failed')
self.send_response(400)
self.end_headers()
self.server.POSTbusy = False
return
2019-10-23 15:48:06 +00:00
if '&submitView=' in followConfirmParams:
2020-04-15 10:57:04 +00:00
followingActor = \
urllib.parse.unquote(followConfirmParams)
2020-04-02 21:35:06 +00:00
followingActor = followingActor.split('actor=')[1]
2019-10-23 15:48:06 +00:00
if '&' in followingActor:
2020-04-02 21:35:06 +00:00
followingActor = followingActor.split('&')[0]
self._redirect_headers(followingActor, cookie, callingDomain)
self.server.POSTbusy = False
2019-10-23 15:48:06 +00:00
return
2019-07-29 16:13:48 +00:00
if '&submitYes=' in followConfirmParams:
2020-04-15 10:57:04 +00:00
followingActor = \
urllib.parse.unquote(followConfirmParams)
2020-04-02 21:35:06 +00:00
followingActor = followingActor.split('actor=')[1]
2019-07-29 16:13:48 +00:00
if '&' in followingActor:
2020-04-02 21:35:06 +00:00
followingActor = followingActor.split('&')[0]
followingNickname = getNicknameFromActor(followingActor)
followingDomain, followingPort = \
getDomainFromActor(followingActor)
if followerNickname == followingNickname and \
followingDomain == self.server.domain and \
followingPort == self.server.port:
2019-07-29 16:13:48 +00:00
if self.server.debug:
print('You cannot follow yourself!')
else:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('Sending follow request from ' +
followerNickname + ' to ' + followingActor)
sendFollowRequest(self.server.session,
self.server.baseDir,
followerNickname,
self.server.domain, self.server.port,
self.server.httpPrefix,
followingNickname,
followingDomain,
followingPort, self.server.httpPrefix,
False, self.server.federationList,
self.server.sendThreads,
self.server.postLog,
self.server.cachedWebfingers,
self.server.personCache,
self.server.debug,
2019-08-14 20:12:27 +00:00
self.server.projectVersion)
2020-04-02 21:35:06 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
originPathStr = \
'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
originPathStr = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
self._redirect_headers(originPathStr, cookie, callingDomain)
self.server.POSTbusy = False
2019-07-29 20:36:26 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 10)
2019-11-16 10:40:35 +00:00
2019-07-29 20:36:26 +00:00
# decision to unfollow in the web interface is confirmed
if authorized and self.path.endswith('/unfollowconfirm'):
2020-04-02 21:35:06 +00:00
usersPath = self.path.split('/unfollowconfirm')[0]
originPathStr = self.server.httpPrefix + '://' + \
self.server.domainFull + usersPath
followerNickname = getNicknameFromActor(originPathStr)
length = int(self.headers['Content-length'])
2020-06-08 18:52:18 +00:00
try:
followConfirmParams = self.rfile.read(length).decode('utf-8')
except BaseException:
print('ERROR: POST followConfirmParams rfile.read failed')
self.send_response(400)
self.end_headers()
self.server.POSTbusy = False
return
2019-07-29 20:36:26 +00:00
if '&submitYes=' in followConfirmParams:
2020-04-15 10:57:04 +00:00
followingActor = \
urllib.parse.unquote(followConfirmParams)
2020-04-02 21:35:06 +00:00
followingActor = followingActor.split('actor=')[1]
2019-07-29 20:36:26 +00:00
if '&' in followingActor:
2020-04-02 21:35:06 +00:00
followingActor = followingActor.split('&')[0]
followingNickname = getNicknameFromActor(followingActor)
followingDomain, followingPort = \
getDomainFromActor(followingActor)
if followerNickname == followingNickname and \
followingDomain == self.server.domain and \
followingPort == self.server.port:
2019-07-29 20:36:26 +00:00
if self.server.debug:
print('You cannot unfollow yourself!')
else:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print(followerNickname + ' stops following ' +
2020-03-29 10:31:59 +00:00
followingActor)
2020-04-02 21:35:06 +00:00
followActor = \
self.server.httpPrefix + '://' + \
self.server.domainFull + \
'/users/' + followerNickname
statusNumber, published = getStatusNumber()
followId = followActor + '/statuses/' + str(statusNumber)
unfollowJson = {
2019-08-16 21:52:11 +00:00
'@context': 'https://www.w3.org/ns/activitystreams',
'id': followId+'/undo',
2019-07-29 20:36:26 +00:00
'type': 'Undo',
'actor': followActor,
'object': {
2019-08-16 21:52:11 +00:00
'id': followId,
2019-07-29 20:36:26 +00:00
'type': 'Follow',
'actor': followActor,
2019-08-16 21:52:11 +00:00
'object': followingActor
2019-07-29 20:36:26 +00:00
}
}
2020-04-02 21:35:06 +00:00
pathUsersSection = self.path.split('/users/')[1]
self.postToNickname = pathUsersSection.split('/')[0]
2019-09-03 17:07:00 +00:00
self._postToOutboxThread(unfollowJson)
2020-04-02 21:35:06 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
originPathStr = \
'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
originPathStr = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
self._redirect_headers(originPathStr, cookie, callingDomain)
self.server.POSTbusy = False
2019-07-29 16:13:48 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 11)
2019-11-16 10:40:35 +00:00
# decision to unblock in the web interface is confirmed
if authorized and self.path.endswith('/unblockconfirm'):
2020-04-02 21:35:06 +00:00
usersPath = self.path.split('/unblockconfirm')[0]
originPathStr = \
self.server.httpPrefix + '://' + \
self.server.domainFull + usersPath
blockerNickname = getNicknameFromActor(originPathStr)
2019-09-02 09:43:43 +00:00
if not blockerNickname:
2020-04-02 21:35:06 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
originPathStr = \
'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
originPathStr = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
print('WARN: unable to find nickname in ' + originPathStr)
self._redirect_headers(originPathStr,
cookie, callingDomain)
self.server.POSTbusy = False
2020-03-22 21:16:02 +00:00
return
2020-04-02 21:35:06 +00:00
length = int(self.headers['Content-length'])
2020-06-08 18:52:18 +00:00
try:
blockConfirmParams = self.rfile.read(length).decode('utf-8')
except BaseException:
print('ERROR: POST blockConfirmParams rfile.read failed')
self.send_response(400)
self.end_headers()
self.server.POSTbusy = False
return
if '&submitYes=' in blockConfirmParams:
2020-04-15 10:57:04 +00:00
blockingActor = \
urllib.parse.unquote(blockConfirmParams)
2020-04-02 21:35:06 +00:00
blockingActor = blockingActor.split('actor=')[1]
if '&' in blockingActor:
2020-04-02 21:35:06 +00:00
blockingActor = blockingActor.split('&')[0]
blockingNickname = getNicknameFromActor(blockingActor)
2019-09-02 09:43:43 +00:00
if not blockingNickname:
2020-04-02 21:35:06 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
originPathStr = \
'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
originPathStr = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
print('WARN: unable to find nickname in ' + blockingActor)
self._redirect_headers(originPathStr,
cookie, callingDomain)
self.server.POSTbusy = False
2020-03-22 21:16:02 +00:00
return
2020-04-02 21:35:06 +00:00
blockingDomain, blockingPort = \
getDomainFromActor(blockingActor)
blockingDomainFull = blockingDomain
if blockingPort:
2020-04-02 21:35:06 +00:00
if blockingPort != 80 and blockingPort != 443:
if ':' not in blockingDomain:
2020-04-02 21:35:06 +00:00
blockingDomainFull = \
blockingDomain + ':' + str(blockingPort)
if blockerNickname == blockingNickname and \
blockingDomain == self.server.domain and \
blockingPort == self.server.port:
if self.server.debug:
print('You cannot unblock yourself!')
else:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print(blockerNickname + ' stops blocking ' +
blockingActor)
removeBlock(self.server.baseDir,
blockerNickname, self.server.domain,
blockingNickname, blockingDomainFull)
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
originPathStr = \
'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
originPathStr = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
self._redirect_headers(originPathStr,
cookie, callingDomain)
self.server.POSTbusy = False
return
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 12)
2019-11-16 10:40:35 +00:00
# decision to block in the web interface is confirmed
if authorized and self.path.endswith('/blockconfirm'):
2020-04-02 21:35:06 +00:00
usersPath = self.path.split('/blockconfirm')[0]
originPathStr = \
self.server.httpPrefix + '://' + \
self.server.domainFull + usersPath
blockerNickname = getNicknameFromActor(originPathStr)
2019-09-02 09:43:43 +00:00
if not blockerNickname:
2020-04-02 21:35:06 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
originPathStr = \
'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
originPathStr = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
print('WARN: unable to find nickname in ' + originPathStr)
self._redirect_headers(originPathStr,
cookie, callingDomain)
self.server.POSTbusy = False
2020-03-22 21:16:02 +00:00
return
2020-04-02 21:35:06 +00:00
length = int(self.headers['Content-length'])
2020-06-08 18:52:18 +00:00
try:
blockConfirmParams = self.rfile.read(length).decode('utf-8')
except BaseException:
print('ERROR: POST blockConfirmParams rfile.read failed')
self.send_response(400)
self.end_headers()
self.server.POSTbusy = False
return
if '&submitYes=' in blockConfirmParams:
2020-04-15 10:57:04 +00:00
blockingActor = \
urllib.parse.unquote(blockConfirmParams)
2020-04-02 21:35:06 +00:00
blockingActor = blockingActor.split('actor=')[1]
if '&' in blockingActor:
2020-04-02 21:35:06 +00:00
blockingActor = blockingActor.split('&')[0]
blockingNickname = getNicknameFromActor(blockingActor)
2019-09-02 09:43:43 +00:00
if not blockingNickname:
2020-04-02 21:35:06 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
originPathStr = \
'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
originPathStr = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
print('WARN: unable to find nickname in ' + blockingActor)
self._redirect_headers(originPathStr,
cookie, callingDomain)
self.server.POSTbusy = False
2020-03-22 21:16:02 +00:00
return
2020-04-02 21:35:06 +00:00
blockingDomain, blockingPort = \
2019-11-03 15:27:29 +00:00
getDomainFromActor(blockingActor)
2020-04-02 21:35:06 +00:00
blockingDomainFull = blockingDomain
if blockingPort:
2020-04-02 21:35:06 +00:00
if blockingPort != 80 and blockingPort != 443:
if ':' not in blockingDomain:
2020-04-02 21:35:06 +00:00
blockingDomainFull = \
blockingDomain + ':' + str(blockingPort)
if blockerNickname == blockingNickname and \
blockingDomain == self.server.domain and \
blockingPort == self.server.port:
if self.server.debug:
print('You cannot block yourself!')
else:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('Adding block by ' + blockerNickname +
' of ' + blockingActor)
addBlock(self.server.baseDir, blockerNickname,
self.server.domain,
blockingNickname,
blockingDomainFull)
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
originPathStr = \
'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
originPathStr = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
self._redirect_headers(originPathStr, cookie, callingDomain)
self.server.POSTbusy = False
return
2019-08-24 23:00:03 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 13)
2019-11-16 10:40:35 +00:00
2019-08-24 23:00:03 +00:00
# an option was chosen from person options screen
# view/follow/block/report
if authorized and self.path.endswith('/personoptions'):
2020-04-02 21:35:06 +00:00
pageNumber = 1
usersPath = self.path.split('/personoptions')[0]
originPathStr = \
self.server.httpPrefix + '://' + \
self.server.domainFull + usersPath
2020-04-02 21:35:06 +00:00
chooserNickname = getNicknameFromActor(originPathStr)
2019-09-02 09:43:43 +00:00
if not chooserNickname:
2020-04-02 21:35:06 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
originPathStr = \
'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
originPathStr = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
print('WARN: unable to find nickname in ' + originPathStr)
self._redirect_headers(originPathStr, cookie, callingDomain)
self.server.POSTbusy = False
2020-03-22 21:16:02 +00:00
return
2020-04-02 21:35:06 +00:00
length = int(self.headers['Content-length'])
2020-06-08 18:52:18 +00:00
try:
optionsConfirmParams = self.rfile.read(length).decode('utf-8')
except BaseException:
print('ERROR: POST optionsConfirmParams rfile.read failed')
self.send_response(400)
self.end_headers()
self.server.POSTbusy = False
return
2020-04-15 10:57:04 +00:00
optionsConfirmParams = \
urllib.parse.unquote(optionsConfirmParams)
2019-09-04 11:29:44 +00:00
# page number to return to
if 'pageNumber=' in optionsConfirmParams:
2020-04-02 21:35:06 +00:00
pageNumberStr = optionsConfirmParams.split('pageNumber=')[1]
2019-09-04 11:29:44 +00:00
if '&' in pageNumberStr:
2020-04-02 21:35:06 +00:00
pageNumberStr = pageNumberStr.split('&')[0]
2019-09-04 11:29:44 +00:00
if pageNumberStr.isdigit():
2020-04-02 21:35:06 +00:00
pageNumber = int(pageNumberStr)
2019-08-25 09:41:57 +00:00
# actor for the person
2020-04-02 21:35:06 +00:00
optionsActor = optionsConfirmParams.split('actor=')[1]
2019-08-24 23:00:03 +00:00
if '&' in optionsActor:
2020-04-02 21:35:06 +00:00
optionsActor = optionsActor.split('&')[0]
2019-08-25 09:41:57 +00:00
# url of the avatar
2020-04-02 21:35:06 +00:00
optionsAvatarUrl = optionsConfirmParams.split('avatarUrl=')[1]
2019-08-25 09:41:57 +00:00
if '&' in optionsAvatarUrl:
2020-04-02 21:35:06 +00:00
optionsAvatarUrl = optionsAvatarUrl.split('&')[0]
2019-08-25 09:41:57 +00:00
# link to a post, which can then be included in reports
2020-04-02 21:35:06 +00:00
postUrl = None
2019-08-25 09:41:57 +00:00
if 'postUrl' in optionsConfirmParams:
2020-04-02 21:35:06 +00:00
postUrl = optionsConfirmParams.split('postUrl=')[1]
2019-08-25 09:41:57 +00:00
if '&' in postUrl:
2020-04-02 21:35:06 +00:00
postUrl = postUrl.split('&')[0]
2020-03-22 21:16:02 +00:00
2020-04-02 21:35:06 +00:00
optionsNickname = getNicknameFromActor(optionsActor)
2019-09-02 09:43:43 +00:00
if not optionsNickname:
2020-04-02 21:35:06 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
originPathStr = \
'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
originPathStr = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
print('WARN: unable to find nickname in ' + optionsActor)
self._redirect_headers(originPathStr, cookie, callingDomain)
self.server.POSTbusy = False
2020-03-22 21:16:02 +00:00
return
2020-04-02 21:35:06 +00:00
optionsDomain, optionsPort = getDomainFromActor(optionsActor)
optionsDomainFull = optionsDomain
2019-08-24 23:00:03 +00:00
if optionsPort:
2020-04-02 21:35:06 +00:00
if optionsPort != 80 and optionsPort != 443:
2019-08-24 23:00:03 +00:00
if ':' not in optionsDomain:
2020-04-02 21:35:06 +00:00
optionsDomainFull = optionsDomain + ':' + \
str(optionsPort)
if chooserNickname == optionsNickname and \
optionsDomain == self.server.domain and \
optionsPort == self.server.port:
2019-08-24 23:00:03 +00:00
if self.server.debug:
print('You cannot perform an option action on yourself')
if '&submitView=' in optionsConfirmParams:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('Viewing ' + optionsActor)
self._redirect_headers(optionsActor,
cookie, callingDomain)
self.server.POSTbusy = False
2019-08-24 23:00:03 +00:00
return
if '&submitBlock=' in optionsConfirmParams:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('Adding block by ' + chooserNickname +
' of ' + optionsActor)
addBlock(self.server.baseDir, chooserNickname,
self.server.domain,
optionsNickname, optionsDomainFull)
2019-08-24 23:00:03 +00:00
if '&submitUnblock=' in optionsConfirmParams:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('Unblocking ' + optionsActor)
msg = \
htmlUnblockConfirm(self.server.translate,
self.server.baseDir,
originPathStr,
optionsActor,
2020-06-14 19:06:10 +00:00
optionsAvatarUrl).encode('utf-8')
2020-04-02 21:35:06 +00:00
self._set_headers('text/html', len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-08-24 23:00:03 +00:00
return
if '&submitFollow=' in optionsConfirmParams:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('Following ' + optionsActor)
msg = \
htmlFollowConfirm(self.server.translate,
self.server.baseDir,
originPathStr,
optionsActor,
2020-06-14 19:06:10 +00:00
optionsAvatarUrl).encode('utf-8')
2020-04-02 21:35:06 +00:00
self._set_headers('text/html', len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-08-24 23:00:03 +00:00
return
if '&submitUnfollow=' in optionsConfirmParams:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('Unfollowing ' + optionsActor)
msg = \
htmlUnfollowConfirm(self.server.translate,
self.server.baseDir,
originPathStr,
optionsActor,
2020-06-14 19:06:10 +00:00
optionsAvatarUrl).encode('utf-8')
2020-04-02 21:35:06 +00:00
self._set_headers('text/html', len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-08-24 23:00:03 +00:00
return
2019-08-25 10:30:39 +00:00
if '&submitDM=' in optionsConfirmParams:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('Sending DM to ' + optionsActor)
reportPath = self.path.replace('/personoptions', '') + '/newdm'
msg = htmlNewPost(False, self.server.translate,
self.server.baseDir,
self.server.httpPrefix,
reportPath, None,
[optionsActor], None,
pageNumber,
chooserNickname,
2020-06-14 19:06:10 +00:00
self.server.domain).encode('utf-8')
2020-04-02 21:35:06 +00:00
self._set_headers('text/html', len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2020-03-22 21:16:02 +00:00
return
2019-11-06 11:39:41 +00:00
if '&submitSnooze=' in optionsConfirmParams:
2020-04-02 21:35:06 +00:00
usersPath = self.path.split('/personoptions')[0]
thisActor = \
self.server.httpPrefix + '://' + \
self.server.domainFull+usersPath
2019-11-06 11:55:22 +00:00
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('Snoozing ' + optionsActor + ' ' + thisActor)
2019-11-06 11:39:41 +00:00
if '/users/' in thisActor:
2020-04-02 21:35:06 +00:00
nickname = thisActor.split('/users/')[1]
personSnooze(self.server.baseDir, nickname,
self.server.domain, optionsActor)
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
thisActor = \
'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
thisActor = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
self._redirect_headers(thisActor + '/' +
self.server.defaultTimeline +
'?page='+str(pageNumber), cookie,
2020-03-28 17:24:40 +00:00
callingDomain)
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-11-15 19:44:20 +00:00
return
2019-11-06 11:39:41 +00:00
if '&submitUnSnooze=' in optionsConfirmParams:
2020-04-02 21:35:06 +00:00
usersPath = self.path.split('/personoptions')[0]
thisActor = \
self.server.httpPrefix + '://' + \
self.server.domainFull + usersPath
2019-11-06 11:55:22 +00:00
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('Unsnoozing ' + optionsActor + ' ' + thisActor)
2019-11-06 11:39:41 +00:00
if '/users/' in thisActor:
2020-04-02 21:35:06 +00:00
nickname = thisActor.split('/users/')[1]
personUnsnooze(self.server.baseDir, nickname,
self.server.domain, optionsActor)
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
thisActor = \
'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
thisActor = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
self._redirect_headers(thisActor + '/' +
self.server.defaultTimeline +
'?page=' + str(pageNumber), cookie,
2020-03-28 17:24:40 +00:00
callingDomain)
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-11-15 19:44:20 +00:00
return
2019-08-24 23:00:03 +00:00
if '&submitReport=' in optionsConfirmParams:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('Reporting ' + optionsActor)
reportPath = \
self.path.replace('/personoptions', '') + '/newreport'
msg = htmlNewPost(False, self.server.translate,
self.server.baseDir,
self.server.httpPrefix,
reportPath, None, [],
postUrl, pageNumber,
chooserNickname,
2020-06-14 19:06:10 +00:00
self.server.domain).encode('utf-8')
2020-04-02 21:35:06 +00:00
self._set_headers('text/html', len(msg),
cookie, callingDomain)
2019-10-22 12:35:51 +00:00
self._write(msg)
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2020-03-22 21:16:02 +00:00
return
2019-08-24 23:00:03 +00:00
if callingDomain.endswith('.onion') and self.server.onionDomain:
2020-04-02 21:35:06 +00:00
originPathStr = \
'http://' + self.server.onionDomain + usersPath
2020-06-03 19:14:24 +00:00
elif callingDomain.endswith('.i2p') and self.server.i2pDomain:
originPathStr = \
2020-06-19 09:50:00 +00:00
'http://' + self.server.i2pDomain + usersPath
2020-04-02 21:35:06 +00:00
self._redirect_headers(originPathStr, cookie, callingDomain)
self.server.POSTbusy = False
2019-08-24 23:00:03 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 14)
2019-11-16 10:40:35 +00:00
2019-11-29 13:07:20 +00:00
# receive different types of post created by htmlNewPost
2020-04-02 21:35:06 +00:00
postTypes = ("newpost", "newblog", "newunlisted", "newfollowers",
"newdm", "newreport", "newshare", "newquestion",
"editblogpost")
2019-11-29 13:07:20 +00:00
for currPostType in postTypes:
2020-04-16 08:55:53 +00:00
if not authorized:
break
2020-04-02 21:35:06 +00:00
if currPostType != 'newshare':
postRedirect = self.server.defaultTimeline
2019-11-29 13:07:20 +00:00
else:
2020-04-02 21:35:06 +00:00
postRedirect = 'shares'
2019-11-29 13:07:20 +00:00
2020-04-16 08:55:53 +00:00
pageNumber = self._receiveNewPost(currPostType, self.path)
2019-11-29 13:07:20 +00:00
if pageNumber:
2020-04-02 21:35:06 +00:00
nickname = self.path.split('/users/')[1]
2020-02-21 12:55:11 +00:00
if '/' in nickname:
2020-04-02 21:35:06 +00:00
nickname = nickname.split('/')[0]
2020-02-21 12:55:11 +00:00
2020-06-03 19:14:24 +00:00
if callingDomain.endswith('.onion') and \
self.server.onionDomain:
self._redirect_headers('http://' +
self.server.onionDomain +
'/users/' + nickname +
'/' + postRedirect +
'?page=' + str(pageNumber), cookie,
callingDomain)
elif (callingDomain.endswith('.i2p') and
self.server.i2pDomain):
2020-06-19 09:50:00 +00:00
self._redirect_headers('http://' +
2020-06-03 19:14:24 +00:00
self.server.i2pDomain +
2020-04-02 21:35:06 +00:00
'/users/' + nickname +
'/' + postRedirect +
'?page=' + str(pageNumber), cookie,
2020-03-28 17:24:40 +00:00
callingDomain)
else:
2020-06-03 19:14:24 +00:00
self._redirect_headers(self.server.httpPrefix + '://' +
self.server.domainFull +
2020-04-02 21:35:06 +00:00
'/users/' + nickname +
'/' + postRedirect +
'?page=' + str(pageNumber), cookie,
2020-03-28 17:24:40 +00:00
callingDomain)
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-11-29 13:07:20 +00:00
return
2019-07-29 16:13:48 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 15)
2019-11-16 10:40:35 +00:00
2019-07-23 19:02:26 +00:00
if self.path.endswith('/outbox') or self.path.endswith('/shares'):
if '/users/' in self.path:
2019-07-27 20:30:58 +00:00
if authorized:
2020-04-02 21:35:06 +00:00
self.outboxAuthenticated = True
pathUsersSection = self.path.split('/users/')[1]
self.postToNickname = pathUsersSection.split('/')[0]
2019-07-03 21:37:46 +00:00
if not self.outboxAuthenticated:
self.send_response(405)
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-07-03 21:37:46 +00:00
return
2019-07-03 16:14:45 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 16)
2019-11-16 10:40:35 +00:00
2019-07-03 16:14:45 +00:00
# check that the post is to an expected path
2020-04-02 21:35:06 +00:00
if not (self.path.endswith('/outbox') or
self.path.endswith('/inbox') or
self.path.endswith('/shares') or
self.path.endswith('/moderationaction') or
self.path.endswith('/caps/new') or
self.path == '/sharedInbox'):
print('Attempt to POST to invalid path ' + self.path)
2020-04-16 11:48:00 +00:00
self._400()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-07-03 16:14:45 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 17)
2019-11-16 10:40:35 +00:00
2019-06-28 18:55:29 +00:00
# read the message and convert it into a python dictionary
2020-04-02 21:35:06 +00:00
length = int(self.headers['Content-length'])
2019-07-03 16:14:45 +00:00
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: content-length: ' + str(length))
if not self.headers['Content-type'].startswith('image/') and \
not self.headers['Content-type'].startswith('video/') and \
not self.headers['Content-type'].startswith('audio/'):
2020-04-02 21:35:06 +00:00
if length > self.server.maxMessageLength:
print('Maximum message length exceeded ' + str(length))
2020-04-16 11:48:00 +00:00
self._400()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-07-16 14:23:06 +00:00
return
else:
2020-04-02 21:35:06 +00:00
if length > self.server.maxMediaSize:
print('Maximum media size exceeded ' + str(length))
2020-04-16 11:48:00 +00:00
self._400()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-07-16 14:23:06 +00:00
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:
2020-04-02 21:35:06 +00:00
print('DEBUG: unauthenticated attempt to ' +
2020-03-29 10:31:59 +00:00
'post image to outbox')
2019-07-16 14:23:06 +00:00
self.send_response(403)
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2020-03-22 21:16:02 +00:00
return
2020-04-02 21:35:06 +00:00
pathUsersSection = self.path.split('/users/')[1]
2019-07-16 14:23:06 +00:00
if '/' not in pathUsersSection:
2020-04-16 11:48:00 +00:00
self._404()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2020-03-22 21:16:02 +00:00
return
2020-04-02 21:35:06 +00:00
self.postFromNickname = pathUsersSection.split('/')[0]
accountsDir = \
self.server.baseDir + '/accounts/' + \
self.postFromNickname + '@' + self.server.domain
2019-07-16 14:23:06 +00:00
if not os.path.isdir(accountsDir):
2020-04-16 11:48:00 +00:00
self._404()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2020-03-22 21:16:02 +00:00
return
2020-06-08 18:52:18 +00:00
try:
mediaBytes = self.rfile.read(length)
except BaseException:
print('ERROR: POST mediaBytes rfile.read failed')
self.send_response(400)
self.end_headers()
self.server.POSTbusy = False
return
2020-04-02 21:35:06 +00:00
mediaFilenameBase = accountsDir + '/upload'
mediaFilename = mediaFilenameBase + '.png'
2019-07-16 14:23:06 +00:00
if self.headers['Content-type'].endswith('jpeg'):
2020-04-02 21:35:06 +00:00
mediaFilename = mediaFilenameBase + '.jpg'
2019-07-16 14:23:06 +00:00
if self.headers['Content-type'].endswith('gif'):
2020-04-02 21:35:06 +00:00
mediaFilename = mediaFilenameBase + '.gif'
2019-11-14 15:11:20 +00:00
if self.headers['Content-type'].endswith('webp'):
2020-04-02 21:35:06 +00:00
mediaFilename = mediaFilenameBase + '.webp'
2019-07-16 14:23:06 +00:00
with open(mediaFilename, 'wb') as avFile:
avFile.write(mediaBytes)
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: image saved to ' + mediaFilename)
2019-07-16 14:23:06 +00:00
self.send_response(201)
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2020-03-22 21:16:02 +00:00
return
2019-07-16 14:23:06 +00:00
# refuse to receive non-json content
2019-11-09 21:39:04 +00:00
if self.headers['Content-type'] != 'application/json' and \
self.headers['Content-type'] != 'application/activity+json':
2020-04-02 21:35:06 +00:00
print("POST is not json: " + self.headers['Content-type'])
2019-08-14 22:33:55 +00:00
if self.server.debug:
print(str(self.headers))
2020-04-02 21:35:06 +00:00
length = int(self.headers['Content-length'])
if length < self.server.maxPostLength:
2020-06-08 18:52:18 +00:00
try:
unknownPost = self.rfile.read(length).decode('utf-8')
except BaseException:
print('ERROR: POST unknownPost rfile.read failed')
self.send_response(400)
self.end_headers()
self.server.POSTbusy = False
return
2019-08-14 22:33:55 +00:00
print(str(unknownPost))
2020-04-16 11:48:00 +00:00
self._400()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-06-28 21:06:05 +00:00
return
2019-07-03 16:14:45 +00:00
if self.server.debug:
print('DEBUG: Reading message')
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 18)
2019-11-16 10:40:35 +00:00
2019-11-16 00:07:07 +00:00
# check content length before reading bytes
if self.path == '/sharedInbox' or self.path == '/inbox':
2020-04-02 21:35:06 +00:00
length = 0
2019-11-16 00:07:07 +00:00
if self.headers.get('Content-length'):
2020-04-02 21:35:06 +00:00
length = int(self.headers['Content-length'])
2019-11-16 09:53:52 +00:00
elif self.headers.get('Content-Length'):
2020-04-02 21:35:06 +00:00
length = int(self.headers['Content-Length'])
2019-11-16 09:53:52 +00:00
elif self.headers.get('content-length'):
2020-04-02 21:35:06 +00:00
length = int(self.headers['content-length'])
if length > 10240:
print('WARN: post to shared inbox is too long ' +
str(length) + ' bytes')
2019-11-16 00:07:07 +00:00
self._400()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-11-16 00:07:07 +00:00
return
2020-06-08 18:52:18 +00:00
try:
messageBytes = self.rfile.read(length)
except BaseException:
print('ERROR: POST messageBytes rfile.read failed')
self.send_response(400)
self.end_headers()
self.server.POSTbusy = False
return
2019-11-16 00:07:07 +00:00
# check content length after reading bytes
2019-11-16 00:01:00 +00:00
if self.path == '/sharedInbox' or self.path == '/inbox':
2020-04-02 21:35:06 +00:00
lenMessage = len(messageBytes)
if lenMessage > 10240:
print('WARN: post to shared inbox is too long ' +
str(lenMessage) + ' bytes')
2019-11-16 00:01:00 +00:00
self._400()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-11-16 00:01:00 +00:00
return
# convert the raw bytes to json
2020-04-02 21:35:06 +00:00
messageJson = json.loads(messageBytes)
2020-03-22 21:16:02 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 19)
2019-11-16 10:40:35 +00:00
2019-07-03 21:37:46 +00:00
# https://www.w3.org/TR/activitypub/#object-without-create
if self.outboxAuthenticated:
2020-04-02 21:35:06 +00:00
if self._postToOutbox(messageJson, __version__):
2019-07-16 19:07:45 +00:00
if messageJson.get('id'):
2020-04-02 21:35:06 +00:00
locnStr = messageJson['id'].replace('/activity', '')
locnStr = locnStr.replace('/undo', '')
self.headers['Location'] = locnStr
self.send_response(201)
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
return
else:
2019-08-16 18:32:26 +00:00
if self.server.debug:
print('Failed to post to outbox')
self.send_response(403)
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
return
2019-07-03 21:37:46 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 20)
2019-11-16 11:55:14 +00:00
2019-07-02 15:07:27 +00:00
# check the necessary properties are available
2019-07-03 16:14:45 +00:00
if self.server.debug:
print('DEBUG: Check message has params')
2019-07-05 15:10:21 +00:00
if self.path.endswith('/inbox') or \
2020-04-02 21:35:06 +00:00
self.path == '/sharedInbox':
2019-07-03 21:37:46 +00:00
if not inboxMessageHasParams(messageJson):
2019-07-06 13:49:25 +00:00
if self.server.debug:
2020-04-02 21:35:06 +00:00
print("DEBUG: inbox message doesn't have the " +
2020-03-29 10:31:59 +00:00
"required parameters")
2019-07-03 21:37:46 +00:00
self.send_response(403)
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-07-03 21:37:46 +00:00
return
2019-07-02 15:07:27 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 21)
2019-11-16 11:55:14 +00:00
2019-11-16 12:07:57 +00:00
if not self.headers.get('signature'):
if 'keyId=' not in self.headers['signature']:
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('DEBUG: POST to inbox has no keyId in ' +
2020-03-29 10:31:59 +00:00
'header signature parameter')
2019-11-16 12:07:57 +00:00
self.send_response(403)
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-11-16 12:07:57 +00:00
return
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 22)
2019-11-16 12:07:57 +00:00
2020-04-02 21:35:06 +00:00
if not inboxPermittedMessage(self.server.domain,
messageJson,
2019-07-09 14:20:23 +00:00
self.server.federationList):
2019-07-03 16:14:45 +00:00
if self.server.debug:
# https://www.youtube.com/watch?v=K3PrSj9XEu4
2019-07-03 16:14:45 +00:00
print('DEBUG: Ah Ah Ah')
2019-06-28 21:59:54 +00:00
self.send_response(403)
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-07-01 11:48:54 +00:00
return
2020-03-22 21:16:02 +00:00
2020-04-02 21:35:06 +00:00
self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 23)
2019-11-16 11:55:14 +00:00
2019-07-03 16:14:45 +00:00
if self.server.debug:
2019-07-06 13:49:25 +00:00
print('DEBUG: POST saving to inbox queue')
2019-07-04 10:02:56 +00:00
if '/users/' in self.path:
2020-04-02 21:35:06 +00:00
pathUsersSection = self.path.split('/users/')[1]
2019-07-04 10:02:56 +00:00
if '/' not in pathUsersSection:
if self.server.debug:
print('DEBUG: This is not a users endpoint')
else:
2020-04-02 21:35:06 +00:00
self.postToNickname = pathUsersSection.split('/')[0]
2019-07-04 10:02:56 +00:00
if self.postToNickname:
2020-04-02 21:35:06 +00:00
queueStatus = \
self._updateInboxQueue(self.postToNickname,
messageJson, messageBytes)
2020-04-16 19:17:19 +00:00
if queueStatus >= 0 and queueStatus <= 3:
2019-08-14 23:04:41 +00:00
return
2019-08-16 18:32:26 +00:00
if self.server.debug:
2020-04-02 21:35:06 +00:00
print('_updateInboxQueue exited ' +
'without doing anything')
2019-08-14 23:04:41 +00:00
else:
if self.server.debug:
print('self.postToNickname is None')
2019-07-04 12:23:53 +00:00
self.send_response(403)
2019-07-01 14:30:48 +00:00
self.end_headers()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-07-01 14:30:48 +00:00
return
2019-07-04 12:23:53 +00:00
else:
2019-07-05 15:10:21 +00:00
if self.path == '/sharedInbox' or self.path == '/inbox':
2019-07-05 10:24:20 +00:00
print('DEBUG: POST to shared inbox')
2020-04-02 21:35:06 +00:00
queueStatus = \
self._updateInboxQueue('inbox', messageJson, messageBytes)
2020-04-16 19:17:19 +00:00
if queueStatus >= 0 and queueStatus <= 3:
2020-03-22 21:16:02 +00:00
return
2020-04-16 11:49:17 +00:00
self._200()
2020-04-02 21:35:06 +00:00
self.server.POSTbusy = False
2019-07-03 16:14:45 +00:00
2019-08-17 15:16:27 +00:00
class PubServerUnitTest(PubServer):
2020-04-02 21:35:06 +00:00
protocol_version = 'HTTP/1.0'
2019-10-16 18:19:18 +00:00
2020-04-02 21:35:06 +00:00
def runPostsQueue(baseDir: str, sendThreads: [], debug: bool) -> None:
2019-10-16 18:19:18 +00:00
"""Manages the threads used to send posts
"""
while True:
time.sleep(1)
2020-04-02 21:35:06 +00:00
removeDormantThreads(baseDir, sendThreads, debug)
2019-10-16 18:19:18 +00:00
2020-04-02 21:35:06 +00:00
def runSharesExpire(versionNumber: str, baseDir: str) -> None:
2019-10-17 09:58:30 +00:00
"""Expires shares as needed
"""
while True:
time.sleep(120)
expireShares(baseDir)
2020-04-02 21:35:06 +00:00
def runPostsWatchdog(projectVersion: str, httpd) -> None:
2019-10-16 18:19:18 +00:00
"""This tries to keep the posts thread running even if it dies
"""
print('Starting posts queue watchdog')
2020-04-02 21:35:06 +00:00
postsQueueOriginal = httpd.thrPostsQueue.clone(runPostsQueue)
2019-10-16 18:19:18 +00:00
httpd.thrPostsQueue.start()
while True:
2020-03-22 21:16:02 +00:00
time.sleep(20)
2019-10-16 18:19:18 +00:00
if not httpd.thrPostsQueue.isAlive():
httpd.thrPostsQueue.kill()
2020-04-02 21:35:06 +00:00
httpd.thrPostsQueue = postsQueueOriginal.clone(runPostsQueue)
2019-10-16 18:19:18 +00:00
httpd.thrPostsQueue.start()
print('Restarting posts queue...')
2020-04-02 21:35:06 +00:00
def runSharesExpireWatchdog(projectVersion: str, httpd) -> None:
2019-10-17 09:58:30 +00:00
"""This tries to keep the shares expiry thread running even if it dies
"""
print('Starting shares expiry watchdog')
2020-04-02 21:35:06 +00:00
sharesExpireOriginal = httpd.thrSharesExpire.clone(runSharesExpire)
2019-10-17 09:58:30 +00:00
httpd.thrSharesExpire.start()
while True:
2020-03-22 21:16:02 +00:00
time.sleep(20)
2019-10-17 09:58:30 +00:00
if not httpd.thrSharesExpire.isAlive():
httpd.thrSharesExpire.kill()
2020-04-02 21:35:06 +00:00
httpd.thrSharesExpire = sharesExpireOriginal.clone(runSharesExpire)
2019-10-17 09:58:30 +00:00
httpd.thrSharesExpire.start()
print('Restarting shares expiry...')
2020-04-02 21:35:06 +00:00
def loadTokens(baseDir: str, tokensDict: {}, tokensLookup: {}) -> None:
for subdir, dirs, files in os.walk(baseDir + '/accounts'):
2019-10-25 16:48:53 +00:00
for handle in dirs:
if '@' in handle:
2020-04-02 21:35:06 +00:00
tokenFilename = baseDir + '/accounts/' + handle + '/.token'
2019-10-25 16:48:53 +00:00
if not os.path.isfile(tokenFilename):
continue
2020-04-02 21:35:06 +00:00
nickname = handle.split('@')[0]
token = None
2019-10-25 16:48:53 +00:00
try:
with open(tokenFilename, 'r') as fp:
2020-04-02 21:35:06 +00:00
token = fp.read()
2019-10-25 16:48:53 +00:00
except Exception as e:
2020-04-02 21:35:06 +00:00
print('WARN: Unable to read token for ' +
nickname + ' ' + str(e))
2019-10-25 16:48:53 +00:00
if not token:
continue
2020-04-02 21:35:06 +00:00
tokensDict[nickname] = token
tokensLookup[token] = nickname
def runDaemon(blogsInstance: bool, mediaInstance: bool,
maxRecentPosts: int,
enableSharedInbox: bool, registration: bool,
language: str, projectVersion: str,
instanceId: str, clientToServer: bool,
baseDir: str, domain: str,
onionDomain: str, i2pDomain: str,
2020-04-02 21:35:06 +00:00
port=80, proxyPort=80, httpPrefix='https',
fedList=[], maxMentions=10, maxEmoji=10,
authenticatedFetch=False,
noreply=False, nolike=False, nopics=False,
noannounce=False, cw=False, ocapAlways=False,
2020-06-09 11:03:59 +00:00
proxyType=None, maxReplies=64,
2020-04-02 21:35:06 +00:00
domainMaxPostsPerDay=8640, accountMaxPostsPerDay=864,
allowDeletion=False, debug=False, unitTest=False,
instanceOnlySkillsSearch=False, sendThreads=[],
2019-12-04 11:11:18 +00:00
useBlurHash=False) -> None:
2020-04-02 21:35:06 +00:00
if len(domain) == 0:
domain = 'localhost'
2019-06-28 18:55:29 +00:00
if '.' not in domain:
2019-07-03 12:24:54 +00:00
if domain != 'localhost':
print('Invalid domain: ' + domain)
return
2019-06-28 18:55:29 +00:00
2020-03-22 21:16:02 +00:00
if unitTest:
2020-04-02 21:35:06 +00:00
serverAddress = (domain, proxyPort)
pubHandler = partial(PubServerUnitTest)
2019-08-17 15:16:27 +00:00
else:
2020-04-02 21:35:06 +00:00
serverAddress = ('', proxyPort)
pubHandler = partial(PubServer)
2020-02-19 11:37:33 +00:00
2020-02-19 10:37:40 +00:00
try:
2020-04-02 21:35:06 +00:00
httpd = ThreadingHTTPServer(serverAddress, pubHandler)
2020-02-19 10:37:40 +00:00
except Exception as e:
2020-04-02 21:35:06 +00:00
if e.errno == 98:
print('ERROR: HTTP server address is already in use. ' +
2020-03-29 10:31:59 +00:00
str(serverAddress))
2020-02-19 13:16:38 +00:00
return False
2020-03-22 21:16:02 +00:00
2020-04-02 21:35:06 +00:00
print('ERROR: HTTP server failed to start. ' + str(e))
2020-02-19 10:37:40 +00:00
return False
2019-09-07 08:57:52 +00:00
2020-03-28 10:33:04 +00:00
# This counter is used to update the list of blocked domains in memory.
# It helps to avoid touching the disk and so improves flooding resistance
2020-04-02 21:35:06 +00:00
httpd.blocklistUpdateCtr = 0
httpd.blocklistUpdateInterval = 100
httpd.domainBlocklist = getDomainBlocklist(baseDir)
httpd.onionDomain = onionDomain
httpd.i2pDomain = i2pDomain
2020-04-02 21:35:06 +00:00
httpd.useBlurHash = useBlurHash
httpd.mediaInstance = mediaInstance
httpd.blogsInstance = blogsInstance
httpd.defaultTimeline = 'inbox'
2019-11-28 16:16:43 +00:00
if mediaInstance:
2020-04-02 21:35:06 +00:00
httpd.defaultTimeline = 'tlmedia'
2020-02-24 14:39:25 +00:00
if blogsInstance:
2020-04-02 21:35:06 +00:00
httpd.defaultTimeline = 'tlblogs'
2019-11-28 16:16:43 +00:00
2019-09-07 08:57:52 +00:00
# load translations dictionary
2020-04-02 21:35:06 +00:00
httpd.translate = {}
httpd.systemLanguage = 'en'
2019-09-07 08:57:52 +00:00
if not unitTest:
2020-04-02 21:35:06 +00:00
if not os.path.isdir(baseDir + '/translations'):
2019-09-07 08:57:52 +00:00
print('ERROR: translations directory not found')
return
2019-11-11 17:49:08 +00:00
if not language:
2020-04-02 21:35:06 +00:00
systemLanguage = locale.getdefaultlocale()[0]
2019-11-11 17:49:08 +00:00
else:
2020-04-02 21:35:06 +00:00
systemLanguage = language
2019-11-12 21:38:02 +00:00
if not systemLanguage:
2020-04-02 21:35:06 +00:00
systemLanguage = 'en'
2019-09-07 08:57:52 +00:00
if '_' in systemLanguage:
2020-04-02 21:35:06 +00:00
systemLanguage = systemLanguage.split('_')[0]
2019-11-11 17:49:08 +00:00
while '/' in systemLanguage:
2020-04-02 21:35:06 +00:00
systemLanguage = systemLanguage.split('/')[1]
2019-09-07 08:57:52 +00:00
if '.' in systemLanguage:
2020-04-02 21:35:06 +00:00
systemLanguage = systemLanguage.split('.')[0]
translationsFile = baseDir + '/translations/' + \
systemLanguage + '.json'
2019-09-07 08:57:52 +00:00
if not os.path.isfile(translationsFile):
2020-04-02 21:35:06 +00:00
systemLanguage = 'en'
translationsFile = baseDir + '/translations/' + \
systemLanguage + '.json'
print('System language: ' + systemLanguage)
httpd.systemLanguage = systemLanguage
httpd.translate = loadJson(translationsFile)
2020-06-12 12:02:04 +00:00
if not httpd.translate:
print('ERROR: no translations loaded from ' + translationsFile)
2020-06-12 21:41:32 +00:00
sys.exit()
2020-04-02 21:35:06 +00:00
if registration == 'open':
httpd.registration = True
2019-11-13 12:49:40 +00:00
else:
2020-04-02 21:35:06 +00:00
httpd.registration = False
httpd.enableSharedInbox = enableSharedInbox
httpd.outboxThread = {}
httpd.newPostThread = {}
httpd.projectVersion = projectVersion
httpd.authenticatedFetch = authenticatedFetch
2019-09-03 19:30:41 +00:00
# max POST size of 30M
2020-04-02 21:35:06 +00:00
httpd.maxPostLength = 1024 * 1024 * 30
httpd.maxMediaSize = httpd.maxPostLength
2020-02-24 11:50:50 +00:00
# Maximum text length is 32K - enough for a blog post
2020-04-02 21:35:06 +00:00
httpd.maxMessageLength = 32000
2020-02-24 11:50:50 +00:00
# Maximum overall number of posts per box
2020-04-02 21:35:06 +00:00
httpd.maxPostsInBox = 32000
httpd.domain = domain
httpd.port = port
httpd.domainFull = domain
if port:
2020-04-02 21:35:06 +00:00
if port != 80 and port != 443:
if ':' not in domain:
2020-04-02 21:35:06 +00:00
httpd.domainFull = domain + ':' + str(port)
2020-06-20 19:37:44 +00:00
saveDomainQrcode(baseDir, httpPrefix, httpd.domainFull)
2020-04-02 21:35:06 +00:00
httpd.httpPrefix = httpPrefix
httpd.debug = debug
httpd.federationList = fedList.copy()
httpd.baseDir = baseDir
httpd.instanceId = instanceId
httpd.personCache = {}
httpd.cachedWebfingers = {}
2020-06-09 11:03:59 +00:00
httpd.proxyType = proxyType
2020-04-02 21:35:06 +00:00
httpd.session = None
httpd.sessionLastUpdate = 0
httpd.lastGET = 0
httpd.lastPOST = 0
httpd.GETbusy = False
httpd.POSTbusy = False
httpd.receivedMessage = False
httpd.inboxQueue = []
httpd.sendThreads = sendThreads
httpd.postLog = []
2020-04-16 18:25:59 +00:00
httpd.maxQueueLength = 64
2020-04-02 21:35:06 +00:00
httpd.ocapAlways = ocapAlways
httpd.allowDeletion = allowDeletion
httpd.lastLoginTime = 0
httpd.maxReplies = maxReplies
httpd.tokens = {}
httpd.tokensLookup = {}
loadTokens(baseDir, httpd.tokens, httpd.tokensLookup)
httpd.instanceOnlySkillsSearch = instanceOnlySkillsSearch
httpd.acceptedCaps = ["inbox:write", "objects:read"]
2019-11-04 10:43:19 +00:00
# contains threads used to send posts to followers
2020-04-02 21:35:06 +00:00
httpd.followersThreads = []
2019-07-09 17:54:08 +00:00
if noreply:
httpd.acceptedCaps.append('inbox:noreply')
if nolike:
httpd.acceptedCaps.append('inbox:nolike')
2019-07-09 18:11:23 +00:00
if nopics:
httpd.acceptedCaps.append('inbox:nopics')
if noannounce:
httpd.acceptedCaps.append('inbox:noannounce')
if cw:
httpd.acceptedCaps.append('inbox:cw')
2019-07-11 12:29:31 +00:00
2020-04-02 21:35:06 +00:00
if not os.path.isdir(baseDir + '/accounts/inbox@' + domain):
print('Creating shared inbox: inbox@' + domain)
createSharedInbox(baseDir, 'inbox', domain, port, httpPrefix)
2019-07-12 09:52:06 +00:00
2020-04-02 21:35:06 +00:00
if not os.path.isdir(baseDir + '/cache'):
os.mkdir(baseDir + '/cache')
if not os.path.isdir(baseDir + '/cache/actors'):
2019-08-20 10:10:33 +00:00
print('Creating actors cache')
2020-04-02 21:35:06 +00:00
os.mkdir(baseDir + '/cache/actors')
if not os.path.isdir(baseDir + '/cache/announce'):
2019-08-20 12:39:59 +00:00
print('Creating announce cache')
2020-04-02 21:35:06 +00:00
os.mkdir(baseDir + '/cache/announce')
if not os.path.isdir(baseDir + '/cache/avatars'):
2019-09-14 17:12:03 +00:00
print('Creating avatars cache')
2020-04-02 21:35:06 +00:00
os.mkdir(baseDir + '/cache/avatars')
2019-08-20 10:10:33 +00:00
2020-04-02 21:35:06 +00:00
archiveDir = baseDir + '/archive'
2019-08-20 11:51:29 +00:00
if not os.path.isdir(archiveDir):
print('Creating archive')
os.mkdir(archiveDir)
2020-03-22 21:16:02 +00:00
2019-08-20 10:28:05 +00:00
print('Creating cache expiry thread')
2020-04-02 21:35:06 +00:00
httpd.thrCache = \
threadWithTrace(target=expireCache,
args=(baseDir, httpd.personCache,
httpd.httpPrefix,
archiveDir,
httpd.maxPostsInBox), daemon=True)
2019-08-20 10:28:05 +00:00
httpd.thrCache.start()
2019-10-16 18:19:18 +00:00
print('Creating posts queue')
2020-04-02 21:35:06 +00:00
httpd.thrPostsQueue = \
threadWithTrace(target=runPostsQueue,
args=(baseDir, httpd.sendThreads, debug), daemon=True)
2020-03-22 21:16:02 +00:00
if not unitTest:
2020-04-02 21:35:06 +00:00
httpd.thrPostsWatchdog = \
threadWithTrace(target=runPostsWatchdog,
args=(projectVersion, httpd), daemon=True)
2019-10-16 18:19:18 +00:00
httpd.thrPostsWatchdog.start()
else:
httpd.thrPostsQueue.start()
2019-10-17 09:58:30 +00:00
print('Creating expire thread for shared items')
2020-04-02 21:35:06 +00:00
httpd.thrSharesExpire = \
threadWithTrace(target=runSharesExpire,
args=(__version__, baseDir), daemon=True)
2020-03-22 21:16:02 +00:00
if not unitTest:
2020-04-02 21:35:06 +00:00
httpd.thrSharesExpireWatchdog = \
threadWithTrace(target=runSharesExpireWatchdog,
args=(projectVersion, httpd), daemon=True)
2019-10-17 09:58:30 +00:00
httpd.thrSharesExpireWatchdog.start()
else:
httpd.thrSharesExpire.start()
2020-04-02 21:35:06 +00:00
httpd.recentPostsCache = {}
httpd.maxRecentPosts = maxRecentPosts
httpd.iconsCache = {}
2020-05-24 22:10:14 +00:00
httpd.fontsCache = {}
2019-07-12 09:52:06 +00:00
print('Creating inbox queue')
2020-04-02 21:35:06 +00:00
httpd.thrInboxQueue = \
threadWithTrace(target=runInboxQueue,
args=(httpd.recentPostsCache, httpd.maxRecentPosts,
projectVersion,
baseDir, httpPrefix, httpd.sendThreads,
httpd.postLog, httpd.cachedWebfingers,
httpd.personCache, httpd.inboxQueue,
2020-06-09 11:03:59 +00:00
domain, onionDomain, i2pDomain, port, proxyType,
2020-04-02 21:35:06 +00:00
httpd.federationList,
httpd.ocapAlways, maxReplies,
domainMaxPostsPerDay, accountMaxPostsPerDay,
allowDeletion, debug, maxMentions, maxEmoji,
httpd.translate,
unitTest, httpd.acceptedCaps), daemon=True)
2020-01-12 20:13:44 +00:00
print('Creating scheduled post thread')
2020-04-02 21:35:06 +00:00
httpd.thrPostSchedule = \
threadWithTrace(target=runPostSchedule,
args=(baseDir, httpd, 20), daemon=True)
2020-04-27 09:41:38 +00:00
# flags used when restarting the inbox queue
2020-05-02 10:19:24 +00:00
httpd.restartInboxQueueInProgress = False
httpd.restartInboxQueue = False
2020-04-27 09:41:38 +00:00
2020-03-22 21:16:02 +00:00
if not unitTest:
2020-01-12 20:13:44 +00:00
print('Creating inbox queue watchdog')
2020-04-02 21:35:06 +00:00
httpd.thrWatchdog = \
threadWithTrace(target=runInboxQueueWatchdog,
args=(projectVersion, httpd), daemon=True)
2019-09-05 12:43:59 +00:00
httpd.thrWatchdog.start()
2020-01-12 20:13:44 +00:00
print('Creating scheduled post watchdog')
2020-04-02 21:35:06 +00:00
httpd.thrWatchdogSchedule = \
threadWithTrace(target=runPostScheduleWatchdog,
args=(projectVersion, httpd), daemon=True)
2020-01-12 20:13:44 +00:00
httpd.thrWatchdogSchedule.start()
2019-09-05 12:43:59 +00:00
else:
httpd.thrInboxQueue.start()
2020-01-12 20:13:44 +00:00
httpd.thrPostSchedule.start()
2019-09-05 12:43:59 +00:00
2019-07-13 09:37:17 +00:00
if clientToServer:
2020-04-02 21:35:06 +00:00
print('Running ActivityPub client on ' +
domain + ' port ' + str(proxyPort))
2019-07-13 09:37:17 +00:00
else:
2020-04-02 21:35:06 +00:00
print('Running ActivityPub server on ' +
domain + ' port ' + str(proxyPort))
2019-06-28 18:55:29 +00:00
httpd.serve_forever()