daemon.py 647 KB
Newer Older
Bob Mottram's avatar
Bob Mottram committed
1 2 3
__filename__ = "daemon.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
Bob Mottram's avatar
Bob Mottram committed
4
__version__ = "1.2.0"
Bob Mottram's avatar
Bob Mottram committed
5 6 7 8
__maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"

Bob Mottram's avatar
Bob Mottram committed
9
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer, HTTPServer
Bob Mottram's avatar
Bob Mottram committed
10
import sys
Bob Mottram's avatar
Initial  
Bob Mottram committed
11
import json
12
import time
Bob Mottram's avatar
Bob Mottram committed
13
import locale
Bob Mottram's avatar
unquote  
Bob Mottram committed
14
import urllib.parse
15
import datetime
Bob Mottram's avatar
Bob Mottram committed
16 17
from socket import error as SocketError
import errno
Bob Mottram's avatar
Bob Mottram committed
18
from functools import partial
19
import pyqrcode
20
# for saving images
21
from hashlib import sha256
Bob Mottram's avatar
Bob Mottram committed
22
from hashlib import sha1
Bob Mottram's avatar
Initial  
Bob Mottram committed
23 24
from session import createSession
from webfinger import webfingerMeta
Bob Mottram's avatar
Bob Mottram committed
25
from webfinger import webfingerNodeInfo
Bob Mottram's avatar
Initial  
Bob Mottram committed
26
from webfinger import webfingerLookup
27
from webfinger import webfingerUpdate
Bob Mottram's avatar
Bob Mottram committed
28
from mastoapiv1 import getMastoApiV1Account
Bob Mottram's avatar
Bob Mottram committed
29 30
from mastoapiv1 import getMastApiV1Id
from mastoapiv1 import getNicknameFromMastoApiV1Id
Bob Mottram's avatar
Bob Mottram committed
31
from metadata import metaDataInstance
Bob Mottram's avatar
Bob Mottram committed
32
from metadata import metaDataNodeInfo
33 34 35
from pgp import getEmailAddress
from pgp import setEmailAddress
from pgp import getPGPpubKey
Bob Mottram's avatar
Bob Mottram committed
36
from pgp import getPGPfingerprint
37
from pgp import setPGPpubKey
Bob Mottram's avatar
Bob Mottram committed
38
from pgp import setPGPfingerprint
Bob Mottram's avatar
Bob Mottram committed
39 40
from xmpp import getXmppAddress
from xmpp import setXmppAddress
Bob Mottram's avatar
Bob Mottram committed
41 42
from ssb import getSSBAddress
from ssb import setSSBAddress
Bob Mottram's avatar
Bob Mottram committed
43 44
from tox import getToxAddress
from tox import setToxAddress
Bob Mottram's avatar
Bob Mottram committed
45 46
from briar import getBriarAddress
from briar import setBriarAddress
Bob Mottram's avatar
Bob Mottram committed
47 48
from jami import getJamiAddress
from jami import setJamiAddress
Bob Mottram's avatar
Bob Mottram committed
49 50
from matrix import getMatrixAddress
from matrix import setMatrixAddress
51 52
from donate import getDonationUrl
from donate import setDonationUrl
Bob Mottram's avatar
Bob Mottram committed
53
from person import setPersonNotes
Bob Mottram's avatar
Bob Mottram committed
54
from person import getDefaultPersonContext
Bob Mottram's avatar
Bob Mottram committed
55
from person import savePersonQrcode
56
from person import randomizeActorImages
Bob Mottram's avatar
Bob Mottram committed
57
from person import personUpgradeActor
58
from person import activateAccount
59
from person import deactivateAccount
Bob Mottram's avatar
Bob Mottram committed
60
from person import registerAccount
Bob Mottram's avatar
Initial  
Bob Mottram committed
61
from person import personLookup
Bob Mottram's avatar
Bob Mottram committed
62
from person import personBoxJson
63
from person import createSharedInbox
Bob Mottram's avatar
Bob Mottram committed
64
from person import createNewsInbox
Bob Mottram's avatar
Bob Mottram committed
65
from person import suspendAccount
66
from person import reenableAccount
Bob Mottram's avatar
Bob Mottram committed
67
from person import removeAccount
68
from person import canRemovePost
Bob Mottram's avatar
Bob Mottram committed
69 70
from person import personSnooze
from person import personUnsnooze
Bob Mottram's avatar
Bob Mottram committed
71 72
from posts import outboxMessageCreateWrap
from posts import getPinnedPostAsJson
Bob Mottram's avatar
Bob Mottram committed
73
from posts import pinPost
Bob Mottram's avatar
Bob Mottram committed
74 75
from posts import jsonPinPost
from posts import undoPinnedPost
Bob Mottram's avatar
Bob Mottram committed
76
from posts import isModerator
Bob Mottram's avatar
Bob Mottram committed
77
from posts import mutePost
Bob Mottram's avatar
Bob Mottram committed
78
from posts import unmutePost
Bob Mottram's avatar
Bob Mottram committed
79
from posts import createQuestionPost
80
from posts import createPublicPost
Bob Mottram's avatar
Bob Mottram committed
81
from posts import createBlogPost
Bob Mottram's avatar
Bob Mottram committed
82
from posts import createReportPost
Bob Mottram's avatar
Bob Mottram committed
83
from posts import createUnlistedPost
84
from posts import createFollowersOnlyPost
Bob Mottram's avatar
Bob Mottram committed
85
from posts import createEventPost
86
from posts import createDirectMessagePost
Bob Mottram's avatar
Bob Mottram committed
87
from posts import populateRepliesJson
Bob Mottram's avatar
Bob Mottram committed
88
from posts import addToField
Bob Mottram's avatar
Bob Mottram committed
89
from posts import expireCache
Bob Mottram's avatar
Bob Mottram committed
90
from inbox import clearQueueItems
Bob Mottram's avatar
Bob Mottram committed
91
from inbox import inboxPermittedMessage
92
from inbox import inboxMessageHasParams
Bob Mottram's avatar
Bob Mottram committed
93
from inbox import runInboxQueue
94
from inbox import runInboxQueueWatchdog
Bob Mottram's avatar
Bob Mottram committed
95
from inbox import savePostToInboxQueue
Bob Mottram's avatar
Bob Mottram committed
96
from inbox import populateReplies
97
from inbox import getPersonPubKey
98
from follow import isFollowingActor
Bob Mottram's avatar
Bob Mottram committed
99
from follow import getFollowingFeed
Bob Mottram's avatar
Bob Mottram committed
100
from follow import sendFollowRequest
Bob Mottram's avatar
Bob Mottram committed
101
from follow import unfollowAccount
102
from follow import createInitialLastSeen
Bob Mottram's avatar
Bob Mottram committed
103
from auth import authorize
104
from auth import createPassword
Bob Mottram's avatar
Bob Mottram committed
105 106
from auth import createBasicAuthHeader
from auth import authorizeBasic
107
from auth import storeBasicCredentials
Bob Mottram's avatar
Bob Mottram committed
108
from threads import threadWithTrace
Bob Mottram's avatar
Bob Mottram committed
109
from threads import removeDormantThreads
110 111
from media import replaceYouTube
from media import attachMedia
112 113
from blocking import addBlock
from blocking import removeBlock
114 115
from blocking import addGlobalBlock
from blocking import removeGlobalBlock
116
from blocking import isBlockedHashtag
117
from blocking import isBlockedDomain
118
from blocking import getDomainBlocklist
Bob Mottram's avatar
Bob Mottram committed
119 120
from roles import setRole
from roles import clearModeratorStatus
Bob Mottram's avatar
Bob Mottram committed
121
from roles import clearEditorStatus
122
from blog import htmlBlogPageRSS2
123
from blog import htmlBlogPageRSS3
Bob Mottram's avatar
Bob Mottram committed
124 125 126
from blog import htmlBlogView
from blog import htmlBlogPage
from blog import htmlBlogPost
Bob Mottram's avatar
Bob Mottram committed
127
from blog import htmlEditBlog
128
from webapp_utils import getAvatarImageUrl
Bob Mottram's avatar
Tidying  
Bob Mottram committed
129 130
from webapp_utils import htmlHashtagBlocked
from webapp_utils import htmlFollowingList
Bob Mottram's avatar
Bob Mottram committed
131
from webapp_utils import setBlogAddress
132
from webapp_utils import getBlogAddress
Bob Mottram's avatar
Bob Mottram committed
133 134
from webapp_calendar import htmlCalendarDeleteConfirm
from webapp_calendar import htmlCalendar
Bob Mottram's avatar
Bob Mottram committed
135
from webapp_about import htmlAbout
Bob Mottram's avatar
Bob Mottram committed
136
from webapp_confirm import htmlConfirmDelete
137 138
from webapp_confirm import htmlConfirmRemoveSharedItem
from webapp_confirm import htmlConfirmUnblock
139 140 141 142 143 144 145 146 147 148
from webapp_person_options import htmlPersonOptions
from webapp_timeline import htmlShares
from webapp_timeline import htmlInbox
from webapp_timeline import htmlBookmarks
from webapp_timeline import htmlEvents
from webapp_timeline import htmlInboxDMs
from webapp_timeline import htmlInboxReplies
from webapp_timeline import htmlInboxMedia
from webapp_timeline import htmlInboxBlogs
from webapp_timeline import htmlInboxNews
Bob Mottram's avatar
Bob Mottram committed
149
from webapp_timeline import htmlInboxFeatures
150
from webapp_timeline import htmlOutbox
Bob Mottram's avatar
Tidying  
Bob Mottram committed
151
from webapp_media import loadPeertubeInstances
Bob Mottram's avatar
Bob Mottram committed
152
from webapp_moderation import htmlAccountInfo
Bob Mottram's avatar
Bob Mottram committed
153 154
from webapp_moderation import htmlModeration
from webapp_moderation import htmlModerationInfo
155
from webapp_create_post import htmlNewPost
Bob Mottram's avatar
Bob Mottram committed
156 157
from webapp_login import htmlLogin
from webapp_login import htmlGetLoginCredentials
Bob Mottram's avatar
Bob Mottram committed
158 159
from webapp_suspended import htmlSuspended
from webapp_tos import htmlTermsOfService
160 161
from webapp_confirm import htmlConfirmFollow
from webapp_confirm import htmlConfirmUnfollow
162 163 164 165 166 167 168 169 170 171
from webapp_post import htmlPostReplies
from webapp_post import htmlIndividualPost
from webapp_profile import htmlEditProfile
from webapp_profile import htmlProfileAfterSearch
from webapp_profile import htmlProfile
from webapp_column_left import htmlLinksMobile
from webapp_column_left import htmlEditLinks
from webapp_column_right import htmlNewswireMobile
from webapp_column_right import htmlEditNewswire
from webapp_column_right import htmlCitations
172
from webapp_column_right import htmlEditNewsPost
173 174 175 176 177 178 179 180
from webapp_search import htmlSkillsSearch
from webapp_search import htmlHistorySearch
from webapp_search import htmlHashtagSearch
from webapp_search import rssHashtagSearch
from webapp_search import htmlSearchEmoji
from webapp_search import htmlSearchSharedItems
from webapp_search import htmlSearchEmojiTextEntry
from webapp_search import htmlSearch
Bob Mottram's avatar
Bob Mottram committed
181
from webapp_hashtagswarm import getHashtagCategoriesFeed
182
from webapp_hashtagswarm import htmlSearchHashtagCategory
Bob Mottram's avatar
Bob Mottram committed
183
from shares import getSharesFeedForPerson
184
from shares import addShare
Bob Mottram's avatar
Bob Mottram committed
185
from shares import removeShare
Bob Mottram's avatar
Bob Mottram committed
186
from shares import expireShares
Bob Mottram's avatar
Bob Mottram committed
187
from categories import setHashtagCategory
188
from utils import decodedHost
189
from utils import isPublicPost
190
from utils import getLockedAccount
Bob Mottram's avatar
Bob Mottram committed
191
from utils import hasUsersPath
Bob Mottram's avatar
Tidying  
Bob Mottram committed
192
from utils import getFullDomain
193
from utils import removeHtml
194
from utils import isEditor
195
from utils import getImageExtensions
Bob Mottram's avatar
Bob Mottram committed
196
from utils import mediaFileMimeType
Bob Mottram's avatar
Bob Mottram committed
197
from utils import getCSS
198
from utils import firstParagraphFromString
199
from utils import clearFromPostCaches
Bob Mottram's avatar
Bob Mottram committed
200
from utils import containsInvalidChars
Bob Mottram's avatar
Bob Mottram committed
201
from utils import isSystemAccount
Bob Mottram's avatar
Tidying  
Bob Mottram committed
202 203
from utils import setConfigParam
from utils import getConfigParam
204
from utils import removeIdEnding
Bob Mottram's avatar
Bob Mottram committed
205 206
from utils import updateLikesCollection
from utils import undoLikesCollectionEntry
Bob Mottram's avatar
Bob Mottram committed
207
from utils import deletePost
Bob Mottram's avatar
Bob Mottram committed
208
from utils import isBlogPost
209
from utils import removeAvatarFromCache
Bob Mottram's avatar
Bob Mottram committed
210
from utils import locatePost
Bob Mottram's avatar
Bob Mottram committed
211 212
from utils import getCachedPostFilename
from utils import removePostFromCache
Bob Mottram's avatar
Bob Mottram committed
213 214
from utils import getNicknameFromActor
from utils import getDomainFromActor
Bob Mottram's avatar
Bob Mottram committed
215
from utils import getStatusNumber
216
from utils import urlPermitted
217 218
from utils import loadJson
from utils import saveJson
Bob Mottram's avatar
Tidying  
Bob Mottram committed
219
from utils import isSuspended
220
from utils import dangerousMarkup
221 222
from manualapprove import manualDenyFollowRequest
from manualapprove import manualApproveFollowRequest
Bob Mottram's avatar
Bob Mottram committed
223
from announce import createAnnounce
Bob Mottram's avatar
Bob Mottram committed
224
from content import replaceEmojiFromTags
Bob Mottram's avatar
Bob Mottram committed
225
from content import addHtmlTags
Bob Mottram's avatar
Bob Mottram committed
226 227 228
from content import extractMediaInFormPOST
from content import saveMediaInFormPOST
from content import extractTextFieldsInPOST
Bob Mottram's avatar
Bob Mottram committed
229
from media import removeMetaData
Bob Mottram's avatar
Bob Mottram committed
230
from cache import storePersonInCache
Bob Mottram's avatar
Bob Mottram committed
231
from cache import getPersonFromCache
232
from httpsig import verifyPostHeaders
Bob Mottram's avatar
Bob Mottram committed
233
from theme import getTextModeBanner
Bob Mottram's avatar
Bob Mottram committed
234
from theme import setNewsAvatar
Bob Mottram's avatar
Bob Mottram committed
235
from theme import setTheme
236
from theme import getTheme
Bob Mottram's avatar
Bob Mottram committed
237 238
from theme import enableGrayscale
from theme import disableGrayscale
Bob Mottram's avatar
Bob Mottram committed
239 240
from schedule import runPostSchedule
from schedule import runPostScheduleWatchdog
241
from schedule import removeScheduledPosts
242
from outbox import postMessageToOutbox
243
from happening import removeCalendarEvent
244 245
from bookmarks import bookmark
from bookmarks import undoBookmark
Bob Mottram's avatar
Bob Mottram committed
246
from petnames import setPetName
Bob Mottram's avatar
Bob Mottram committed
247 248
from followingCalendar import addPersonToCalendar
from followingCalendar import removePersonFromCalendar
Bob Mottram's avatar
Bob Mottram committed
249
from devices import E2EEdevicesCollection
Bob Mottram's avatar
Bob Mottram committed
250
from devices import E2EEvalidDevice
Bob Mottram's avatar
Bob Mottram committed
251
from devices import E2EEaddDevice
Bob Mottram's avatar
Bob Mottram committed
252
from newswire import getRSSfromDict
253 254
from newswire import rss2Header
from newswire import rss2Footer
255
from newswire import loadHashtagCategories
Bob Mottram's avatar
Bob Mottram committed
256 257
from newsdaemon import runNewswireWatchdog
from newsdaemon import runNewswireDaemon
Bob Mottram's avatar
Bob Mottram committed
258
from filters import isFiltered
259 260
from filters import addGlobalFilter
from filters import removeGlobalFilter
261
from context import hasValidContext
Bob Mottram's avatar
Initial  
Bob Mottram committed
262
import os
Bob Mottram's avatar
Bob Mottram committed
263

Bob Mottram's avatar
Initial  
Bob Mottram committed
264

Bob Mottram's avatar
Bob Mottram committed
265
# maximum number of posts to list in outbox feed
Bob Mottram's avatar
Bob Mottram committed
266
maxPostsInFeed = 12
Bob Mottram's avatar
Bob Mottram committed
267

Bob Mottram's avatar
Bob Mottram committed
268
# reduced posts for media feed because it can take a while
Bob Mottram's avatar
Bob Mottram committed
269
maxPostsInMediaFeed = 6
Bob Mottram's avatar
Bob Mottram committed
270

Bob Mottram's avatar
Bob Mottram committed
271
# Blogs can be longer, so don't show many per page
Bob Mottram's avatar
Bob Mottram committed
272
maxPostsInBlogsFeed = 4
Bob Mottram's avatar
Bob Mottram committed
273

274 275
maxPostsInNewsFeed = 10

Bob Mottram's avatar
Bob Mottram committed
276
# Maximum number of entries in returned rss.xml
Bob Mottram's avatar
Bob Mottram committed
277
maxPostsInRSSFeed = 10
Bob Mottram's avatar
Bob Mottram committed
278

Bob Mottram's avatar
Bob Mottram committed
279
# number of follows/followers per page
280
followsPerPage = 6
Bob Mottram's avatar
Bob Mottram committed
281

Bob Mottram's avatar
Bob Mottram committed
282
# number of item shares per page
Bob Mottram's avatar
Bob Mottram committed
283 284
sharesPerPage = 12

Bob Mottram's avatar
Bob Mottram committed
285

286 287 288 289 290 291 292 293 294 295
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)


Bob Mottram's avatar
Initial  
Bob Mottram committed
296
class PubServer(BaseHTTPRequestHandler):
Bob Mottram's avatar
Bob Mottram committed
297
    protocol_version = 'HTTP/1.1'
Bob Mottram's avatar
Tidying  
Bob Mottram committed
298

299 300 301 302
    def _pathIsImage(self, path: str) -> bool:
        if path.endswith('.png') or \
           path.endswith('.jpg') or \
           path.endswith('.gif') or \
Bob Mottram's avatar
Bob Mottram committed
303
           path.endswith('.svg') or \
Bob Mottram's avatar
Bob Mottram committed
304
           path.endswith('.avif') or \
305
           path.endswith('.webp'):
Bob Mottram's avatar
Tidying  
Bob Mottram committed
306 307 308
            return True
        return False

309 310 311
    def _pathIsVideo(self, path: str) -> bool:
        if path.endswith('.ogv') or \
           path.endswith('.mp4'):
Bob Mottram's avatar
Tidying  
Bob Mottram committed
312 313 314
            return True
        return False

315 316 317
    def _pathIsAudio(self, path: str) -> bool:
        if path.endswith('.ogg') or \
           path.endswith('.mp3'):
Bob Mottram's avatar
Tidying  
Bob Mottram committed
318 319 320
            return True
        return False

Bob Mottram's avatar
Bob Mottram committed
321 322 323 324 325
    def handle_error(self, request, client_address):
        print('ERROR: http server error: ' + str(request) + ', ' +
              str(client_address))
        pass

Bob Mottram's avatar
Bob Mottram committed
326 327 328 329
    def _isMinimal(self, nickname: str) -> bool:
        """Returns true if minimal buttons should be shown
        for the given account
        """
Bob Mottram's avatar
baseDir  
Bob Mottram committed
330
        accountDir = self.server.baseDir + '/accounts/' + \
Bob Mottram's avatar
Bob Mottram committed
331 332 333
            nickname + '@' + self.server.domain
        if not os.path.isdir(accountDir):
            return True
334 335 336 337
        minimalFilename = accountDir + '/.notminimal'
        if os.path.isfile(minimalFilename):
            return False
        return True
Bob Mottram's avatar
Bob Mottram committed
338 339 340 341

    def _setMinimal(self, nickname: str, minimal: bool) -> None:
        """Sets whether an account should display minimal buttons
        """
Bob Mottram's avatar
baseDir  
Bob Mottram committed
342
        accountDir = self.server.baseDir + '/accounts/' + \
Bob Mottram's avatar
Bob Mottram committed
343 344 345
            nickname + '@' + self.server.domain
        if not os.path.isdir(accountDir):
            return
346
        minimalFilename = accountDir + '/.notminimal'
Bob Mottram's avatar
Bob Mottram committed
347
        minimalFileExists = os.path.isfile(minimalFilename)
348
        if minimal and minimalFileExists:
Bob Mottram's avatar
Bob Mottram committed
349
            os.remove(minimalFilename)
350
        elif not minimal and not minimalFileExists:
Bob Mottram's avatar
Bob Mottram committed
351
            with open(minimalFilename, 'w+') as fp:
Bob Mottram's avatar
Bob Mottram committed
352 353
                fp.write('\n')

Bob Mottram's avatar
Bob Mottram committed
354 355
    def _sendReplyToQuestion(self, nickname: str, messageId: str,
                             answer: str) -> None:
Bob Mottram's avatar
Bob Mottram committed
356
        """Sends a reply to a question
Bob Mottram's avatar
Tidying  
Bob Mottram committed
357
        """
Bob Mottram's avatar
Bob Mottram committed
358 359
        votesFilename = self.server.baseDir + '/accounts/' + \
            nickname + '@' + self.server.domain + '/questions.txt'
Bob Mottram's avatar
Tidying  
Bob Mottram committed
360

Bob Mottram's avatar
Bob Mottram committed
361 362 363
        if os.path.isfile(votesFilename):
            # have we already voted on this?
            if messageId in open(votesFilename).read():
Bob Mottram's avatar
Bob Mottram committed
364
                print('Already voted on message ' + messageId)
Bob Mottram's avatar
Bob Mottram committed
365
                return
Bob Mottram's avatar
Tidying  
Bob Mottram committed
366

Bob Mottram's avatar
Bob Mottram committed
367 368
        print('Voting on message ' + messageId)
        print('Vote for: ' + answer)
Bob Mottram's avatar
Bob Mottram committed
369
        commentsEnabled = True
Bob Mottram's avatar
Bob Mottram committed
370 371 372 373 374 375 376 377 378 379
        attachImageFilename = None
        mediaType = None
        imageDescription = None
        inReplyTo = messageId
        inReplyToAtomUri = messageId
        subject = None
        schedulePost = False
        eventDate = None
        eventTime = None
        location = None
Bob Mottram's avatar
Bob Mottram committed
380 381 382 383 384 385
        messageJson = \
            createPublicPost(self.server.baseDir,
                             nickname,
                             self.server.domain, self.server.port,
                             self.server.httpPrefix,
                             answer, False, False, False,
Bob Mottram's avatar
Bob Mottram committed
386
                             commentsEnabled,
Bob Mottram's avatar
Bob Mottram committed
387 388 389 390 391 392 393 394 395
                             attachImageFilename, mediaType,
                             imageDescription,
                             inReplyTo,
                             inReplyToAtomUri,
                             subject,
                             schedulePost,
                             eventDate,
                             eventTime,
                             location)
Bob Mottram's avatar
Tidying  
Bob Mottram committed
396
        if messageJson:
Bob Mottram's avatar
Bob Mottram committed
397
            # name field contains the answer
Bob Mottram's avatar
Bob Mottram committed
398 399 400 401 402
            messageJson['object']['name'] = answer
            if self._postToOutbox(messageJson, __version__, nickname):
                postFilename = \
                    locatePost(self.server.baseDir, nickname,
                               self.server.domain, messageId)
Bob Mottram's avatar
Bob Mottram committed
403
                if postFilename:
Bob Mottram's avatar
Bob Mottram committed
404
                    postJsonObject = loadJson(postFilename)
Bob Mottram's avatar
Bob Mottram committed
405
                    if postJsonObject:
Bob Mottram's avatar
Bob Mottram committed
406 407 408 409 410
                        populateReplies(self.server.baseDir,
                                        self.server.httpPrefix,
                                        self.server.domainFull,
                                        postJsonObject,
                                        self.server.maxReplies,
Bob Mottram's avatar
Bob Mottram committed
411 412
                                        self.server.debug)
                        # record the vote
Bob Mottram's avatar
Bob Mottram committed
413
                        votesFile = open(votesFilename, 'a+')
Bob Mottram's avatar
Bob Mottram committed
414
                        if votesFile:
Bob Mottram's avatar
Bob Mottram committed
415
                            votesFile.write(messageId + '\n')
Bob Mottram's avatar
Bob Mottram committed
416 417 418 419
                            votesFile.close()

                        # ensure that the cached post is removed if it exists,
                        # so that it then will be recreated
Bob Mottram's avatar
Bob Mottram committed
420 421 422 423
                        cachedPostFilename = \
                            getCachedPostFilename(self.server.baseDir,
                                                  nickname,
                                                  self.server.domain,
Bob Mottram's avatar
Bob Mottram committed
424 425 426 427 428
                                                  postJsonObject)
                        if cachedPostFilename:
                            if os.path.isfile(cachedPostFilename):
                                os.remove(cachedPostFilename)
                        # remove from memory cache
Bob Mottram's avatar
Bob Mottram committed
429
                        removePostFromCache(postJsonObject,
Bob Mottram's avatar
Bob Mottram committed
430
                                            self.server.recentPostsCache)
Bob Mottram's avatar
Tidying  
Bob Mottram committed
431 432 433 434
            else:
                print('ERROR: unable to post vote to outbox')
        else:
            print('ERROR: unable to create vote')
Bob Mottram's avatar
Bob Mottram committed
435

Bob Mottram's avatar
Bob Mottram committed
436
    def _removePostInteractions(self, postJsonObject: {}) -> None:
Bob Mottram's avatar
Tidying  
Bob Mottram committed
437 438 439 440 441
        """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'):
Bob Mottram's avatar
Bob Mottram committed
442
            postJsonObject['likes'] = {'items': []}
Bob Mottram's avatar
Tidying  
Bob Mottram committed
443
        if postJsonObject.get('shares'):
Bob Mottram's avatar
Bob Mottram committed
444
            postJsonObject['shares'] = {}
Bob Mottram's avatar
Tidying  
Bob Mottram committed
445
        if postJsonObject.get('replies'):
Bob Mottram's avatar
Bob Mottram committed
446
            postJsonObject['replies'] = {}
Bob Mottram's avatar
Tidying  
Bob Mottram committed
447
        if postJsonObject.get('bookmarks'):
Bob Mottram's avatar
Bob Mottram committed
448
            postJsonObject['bookmarks'] = {}
Bob Mottram's avatar
Tidying  
Bob Mottram committed
449 450 451 452 453
        if not postJsonObject.get('object'):
            return
        if not isinstance(postJsonObject['object'], dict):
            return
        if postJsonObject['object'].get('likes'):
Bob Mottram's avatar
Bob Mottram committed
454
            postJsonObject['object']['likes'] = {'items': []}
Bob Mottram's avatar
Tidying  
Bob Mottram committed
455
        if postJsonObject['object'].get('shares'):
Bob Mottram's avatar
Bob Mottram committed
456
            postJsonObject['object']['shares'] = {}
Bob Mottram's avatar
Tidying  
Bob Mottram committed
457
        if postJsonObject['object'].get('replies'):
Bob Mottram's avatar
Bob Mottram committed
458
            postJsonObject['object']['replies'] = {}
Bob Mottram's avatar
Tidying  
Bob Mottram committed
459
        if postJsonObject['object'].get('bookmarks'):
Bob Mottram's avatar
Bob Mottram committed
460
            postJsonObject['object']['bookmarks'] = {}
Bob Mottram's avatar
Tidying  
Bob Mottram committed
461

Bob Mottram's avatar
Bob Mottram committed
462 463 464
    def _requestHTTP(self) -> bool:
        """Should a http response be given?
        """
Bob Mottram's avatar
Bob Mottram committed
465 466
        if not self.headers.get('Accept'):
            return False
Bob Mottram's avatar
Bob Mottram committed
467
        if self.server.debug:
Bob Mottram's avatar
Bob Mottram committed
468
            print('ACCEPT: ' + self.headers['Accept'])
Bob Mottram's avatar
Bob Mottram committed
469
        if 'image/' in self.headers['Accept']:
Bob Mottram's avatar
Bob Mottram committed
470 471
            if 'text/html' not in self.headers['Accept']:
                return False
Bob Mottram's avatar
Bob Mottram committed
472
        if 'video/' in self.headers['Accept']:
Bob Mottram's avatar
Bob Mottram committed
473 474
            if 'text/html' not in self.headers['Accept']:
                return False
Bob Mottram's avatar
Bob Mottram committed
475
        if 'audio/' in self.headers['Accept']:
Bob Mottram's avatar
Bob Mottram committed
476 477
            if 'text/html' not in self.headers['Accept']:
                return False
Bob Mottram's avatar
Bob Mottram committed
478
        if self.headers['Accept'].startswith('*'):
Bob Mottram's avatar
Bob Mottram committed
479
            return False
Bob Mottram's avatar
Bob Mottram committed
480 481 482
        if 'json' in self.headers['Accept']:
            return False
        return True
483 484 485 486 487 488

    def _fetchAuthenticated(self) -> bool:
        """http authentication of GET requests for json
        """
        if not self.server.authenticatedFetch:
            return True
489 490 491
        # check that the headers are signed
        if not self.headers.get('signature'):
            if self.server.debug:
Bob Mottram's avatar
Bob Mottram committed
492 493
                print('WARN: authenticated fetch, ' +
                      'GET has no signature in headers')
494 495
            return False
        # get the keyId
Bob Mottram's avatar
Bob Mottram committed
496 497
        keyId = None
        signatureParams = self.headers['signature'].split(',')
498 499 500
        for signatureItem in signatureParams:
            if signatureItem.startswith('keyId='):
                if '"' in signatureItem:
Bob Mottram's avatar
Bob Mottram committed
501
                    keyId = signatureItem.split('"')[1]
502 503 504
                    break
        if not keyId:
            if self.server.debug:
Bob Mottram's avatar
Bob Mottram committed
505 506
                print('WARN: authenticated fetch, ' +
                      'failed to obtain keyId from signature')
507 508
            return False
        # is the keyId (actor) valid?
Bob Mottram's avatar
Bob Mottram committed
509
        if not urlPermitted(keyId, self.server.federationList):
510
            if self.server.debug:
Bob Mottram's avatar
Bob Mottram committed
511 512
                print('Authorized fetch failed: ' + keyId +
                      ' is not permitted')
513 514 515
            return False
        # make sure we have a session
        if not self.server.session:
Bob Mottram's avatar
Bob Mottram committed
516
            print('DEBUG: creating new session during authenticated fetch')
Bob Mottram's avatar
Bob Mottram committed
517
            self.server.session = createSession(self.server.proxyType)
518
            if not self.server.session:
Bob Mottram's avatar
Bob Mottram committed
519 520
                print('ERROR: GET failed to create session during ' +
                      'authenticated fetch')
521
                return False
522
        # obtain the public key
Bob Mottram's avatar
Bob Mottram committed
523 524 525 526 527
        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)
528 529
        if not pubKey:
            if self.server.debug:
Bob Mottram's avatar
Bob Mottram committed
530 531
                print('DEBUG: Authenticated fetch failed to ' +
                      'obtain public key for ' + keyId)
532
            return False
Bob Mottram's avatar
Bob Mottram committed
533 534 535 536
        # it is assumed that there will be no message body on
        # authenticated fetches and also consequently no digest
        GETrequestBody = ''
        GETrequestDigest = None
537
        # verify the GET request without any digest
Bob Mottram's avatar
Bob Mottram committed
538 539 540 541 542 543
        if verifyPostHeaders(self.server.httpPrefix,
                             pubKey, self.headers,
                             self.path, True,
                             GETrequestDigest,
                             GETrequestBody,
                             self.server.debug):
544 545
            return True
        return False
546

Bob Mottram's avatar
Bob Mottram committed
547
    def _login_headers(self, fileFormat: str, length: int,
Bob Mottram's avatar
Bob Mottram committed
548
                       callingDomain: str) -> None:
Bob Mottram's avatar
Initial  
Bob Mottram committed
549 550
        self.send_response(200)
        self.send_header('Content-type', fileFormat)
551
        self.send_header('Content-Length', str(length))
Bob Mottram's avatar
Bob Mottram committed
552
        self.send_header('Host', callingDomain)
Bob Mottram's avatar
Bob Mottram committed
553
        self.send_header('WWW-Authenticate',
Bob Mottram's avatar
Tidying  
Bob Mottram committed
554
                         'title="Login to Epicyon", Basic realm="epicyon"')
Bob Mottram's avatar
Bob Mottram committed
555 556
        self.send_header('X-Robots-Tag',
                         'noindex, nofollow, noarchive, nosnippet')
Bob Mottram's avatar
Bob Mottram committed
557
        self.send_header('Referrer-Policy', 'origin')
Bob Mottram's avatar
Initial  
Bob Mottram committed
558 559
        self.end_headers()

Bob Mottram's avatar
Bob Mottram committed
560
    def _logout_headers(self, fileFormat: str, length: int,
Bob Mottram's avatar
Bob Mottram committed
561
                        callingDomain: str) -> None:
562 563 564 565
        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')
Bob Mottram's avatar
Bob Mottram committed
566
        self.send_header('Host', callingDomain)
Bob Mottram's avatar
Bob Mottram committed
567
        self.send_header('WWW-Authenticate',
Bob Mottram's avatar
Tidying  
Bob Mottram committed
568
                         'title="Login to Epicyon", Basic realm="epicyon"')
Bob Mottram's avatar
Bob Mottram committed
569 570
        self.send_header('X-Robots-Tag',
                         'noindex, nofollow, noarchive, nosnippet')
Bob Mottram's avatar
Bob Mottram committed
571
        self.send_header('Referrer-Policy', 'origin')
572
        self.end_headers()
Bob Mottram's avatar
Bob Mottram committed
573

Bob Mottram's avatar
Bob Mottram committed
574 575 576 577 578 579 580 581 582 583 584 585
    def _logout_redirect(self, redirect: str, cookie: str,
                         callingDomain: str) -> None:
        if '://' not in redirect:
            print('REDIRECT ERROR: redirect is not an absolute url ' +
                  redirect)

        self.send_response(303)
        self.send_header('Set-Cookie', 'epicyon=; SameSite=Strict')
        self.send_header('Location', redirect)
        self.send_header('Host', callingDomain)
        self.send_header('InstanceID', self.server.instanceId)
        self.send_header('Content-Length', '0')
Bob Mottram's avatar
Bob Mottram committed
586 587
        self.send_header('X-Robots-Tag',
                         'noindex, nofollow, noarchive, nosnippet')
Bob Mottram's avatar
Bob Mottram committed
588
        self.send_header('Referrer-Policy', 'origin')
Bob Mottram's avatar
Bob Mottram committed
589 590
        self.end_headers()

Bob Mottram's avatar
Bob Mottram committed
591
    def _set_headers_base(self, fileFormat: str, length: int, cookie: str,
Bob Mottram's avatar
Bob Mottram committed
592
                          callingDomain: str) -> None:
Bob Mottram's avatar
Bob Mottram committed
593 594
        self.send_response(200)
        self.send_header('Content-type', fileFormat)
Bob Mottram's avatar
Bob Mottram committed
595
        if length > -1:
596
            self.send_header('Content-Length', str(length))
Bob Mottram's avatar
Bob Mottram committed
597
        if cookie:
Bob Mottram's avatar
Bob Mottram committed
598 599 600 601
            cookieStr = cookie
            if 'HttpOnly;' not in cookieStr:
                if self.server.httpPrefix == 'https':
                    cookieStr += '; Secure'
602
                cookieStr += '; HttpOnly; SameSite=Strict'
Bob Mottram's avatar
Bob Mottram committed
603
            self.send_header('Cookie', cookieStr)
Bob Mottram's avatar
Bob Mottram committed
604
        self.send_header('Host', callingDomain)
Bob Mottram's avatar
Bob Mottram committed
605
        self.send_header('InstanceID', self.server.instanceId)
Bob Mottram's avatar
Bob Mottram committed
606 607
        self.send_header('X-Robots-Tag',
                         'noindex, nofollow, noarchive, nosnippet')
Bob Mottram's avatar
Bob Mottram committed
608
        self.send_header('X-Clacks-Overhead', 'GNU Natalie Nguyen')
Bob Mottram's avatar
Bob Mottram committed
609
        self.send_header('Referrer-Policy', 'origin')
Bob Mottram's avatar
Bob Mottram committed
610
        self.send_header('Accept-Ranges', 'none')
Bob Mottram's avatar
Bob Mottram committed
611

Bob Mottram's avatar
Bob Mottram committed
612
    def _set_headers(self, fileFormat: str, length: int, cookie: str,
Bob Mottram's avatar
Bob Mottram committed
613
                     callingDomain: str) -> None:
Bob Mottram's avatar
Bob Mottram committed
614
        self._set_headers_base(fileFormat, length, cookie, callingDomain)
Bob Mottram's avatar
Bob Mottram committed
615
        self.send_header('Cache-Control', 'public, max-age=0')
Bob Mottram's avatar
Bob Mottram committed
616 617
        self.end_headers()

Bob Mottram's avatar
Bob Mottram committed
618
    def _set_headers_head(self, fileFormat: str, length: int, etag: str,
Bob Mottram's avatar
Bob Mottram committed
619
                          callingDomain: str) -> None:
Bob Mottram's avatar
Bob Mottram committed
620
        self._set_headers_base(fileFormat, length, None, callingDomain)
Bob Mottram's avatar
Bob Mottram committed
621
        if etag:
Bob Mottram's avatar
Bob Mottram committed
622
            self.send_header('ETag', etag)
Bob Mottram's avatar
Bob Mottram committed
623 624
        self.end_headers()

Bob Mottram's avatar
Bob Mottram committed
625 626
    def _set_headers_etag(self, mediaFilename: str, fileFormat: str,
                          data, cookie: str, callingDomain: str) -> None:
Bob Mottram's avatar
Bob Mottram committed
627 628
        datalen = len(data)
        self._set_headers_base(fileFormat, datalen, cookie, callingDomain)
Bob Mottram's avatar
Bob Mottram committed
629
        self.send_header('Cache-Control', 'public, max-age=86400')
Bob Mottram's avatar
Bob Mottram committed
630 631
        etag = None
        if os.path.isfile(mediaFilename + '.etag'):
Bob Mottram's avatar
Bob Mottram committed
632
            try:
Bob Mottram's avatar
Bob Mottram committed
633 634 635
                with open(mediaFilename + '.etag', 'r') as etagFile:
                    etag = etagFile.read()
            except BaseException:
Bob Mottram's avatar
Bob Mottram committed
636 637
                pass
        if not etag:
Bob Mottram's avatar
Tidying  
Bob Mottram committed
638
            etag = sha1(data).hexdigest()  # nosec
Bob Mottram's avatar
Bob Mottram committed
639
            try:
Bob Mottram's avatar
Bob Mottram committed
640
                with open(mediaFilename + '.etag', 'w+') as etagFile:
Bob Mottram's avatar
Bob Mottram committed
641
                    etagFile.write(etag)
Bob Mottram's avatar
Bob Mottram committed
642
            except BaseException:
Bob Mottram's avatar
Bob Mottram committed
643 644
                pass
        if etag:
Bob Mottram's avatar
Bob Mottram committed
645
            self.send_header('ETag', etag)
Bob Mottram's avatar
Bob Mottram committed
646 647
        self.end_headers()

Bob Mottram's avatar
Tidying  
Bob Mottram committed
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671
    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

Bob Mottram's avatar
Bob Mottram committed
672
    def _redirect_headers(self, redirect: str, cookie: str,
Bob Mottram's avatar
Tidying  
Bob Mottram committed
673
                          callingDomain: str) -> None:
Bob Mottram's avatar
Bob Mottram committed
674
        if '://' not in redirect:
Bob Mottram's avatar
Bob Mottram committed
675 676
            print('REDIRECT ERROR: redirect is not an absolute url ' +
                  redirect)
Bob Mottram's avatar
Bob Mottram committed
677

Bob Mottram's avatar
Tidying  
Bob Mottram committed
678
        self.send_response(303)
Bob Mottram's avatar
Bob Mottram committed
679 680

        if cookie:
Bob Mottram's avatar
Bob Mottram committed
681 682
            cookieStr = cookie.replace('SET:', '').strip()
            if 'HttpOnly;' not in cookieStr:
Bob Mottram's avatar
Bob Mottram committed
683 684 685
                if self.server.httpPrefix == 'https':
                    cookieStr += '; Secure'
                cookieStr += '; HttpOnly; SameSite=Strict'
Bob Mottram's avatar
Bob Mottram committed
686
            if not cookie.startswith('SET:'):
Bob Mottram's avatar
Bob Mottram committed
687
                self.send_header('Cookie', cookieStr)
Bob Mottram's avatar
Bob Mottram committed
688
            else:
Bob Mottram's avatar
Bob Mottram committed
689
                self.send_header('Set-Cookie', cookieStr)
Bob Mottram's avatar
Bob Mottram committed
690 691 692 693
        self.send_header('Location', redirect)
        self.send_header('Host', callingDomain)
        self.send_header('InstanceID', self.server.instanceId)
        self.send_header('Content-Length', '0')
Bob Mottram's avatar
Bob Mottram committed
694 695
        self.send_header('X-Robots-Tag',
                         'noindex, nofollow, noarchive, nosnippet')
Bob Mottram's avatar
Bob Mottram committed
696
        self.send_header('Referrer-Policy', 'origin')
Bob Mottram's avatar
Bob Mottram committed
697
        self.end_headers()
Bob Mottram's avatar
Bob Mottram committed
698

Bob Mottram's avatar
Bob Mottram committed
699 700 701 702 703 704 705 706 707
    def _httpReturnCode(self, httpCode: int, httpDescription: str,
                        longDescription: str) -> None:
        msg = \
            '<html><head><title>' + str(httpCode) + '</title></head>' \
            '<body bgcolor="linen" text="black">' \
            '<div style="font-size: 400px; ' \
            'text-align: center;">' + str(httpCode) + '</div>' \
            '<div style="font-size: 128px; ' \
            'text-align: center; font-variant: ' \
Bob Mottram's avatar
Bob Mottram committed
708
            'small-caps;"><p role="alert">' + httpDescription + '</p></div>' \
Bob Mottram's avatar
Bob Mottram committed
709 710
            '<div style="text-align: center;">' + longDescription + '</div>' \
            '</body></html>'
Bob Mottram's avatar
Bob Mottram committed
711
        msg = msg.encode('utf-8')
Bob Mottram's avatar
Bob Mottram committed
712
        self.send_response(httpCode)
Bob Mottram's avatar
Initial  
Bob Mottram committed
713
        self.send_header('Content-Type', 'text/html; charset=utf-8')
Bob Mottram's avatar
Bob Mottram committed
714 715
        msgLenStr = str(len(msg))
        self.send_header('Content-Length', msgLenStr)
Bob Mottram's avatar
Bob Mottram committed
716 717
        self.send_header('X-Robots-Tag',
                         'noindex, nofollow, noarchive, nosnippet')
Bob Mottram's avatar
Bob Mottram committed
718
        self.send_header('Referrer-Policy', 'origin')
Bob Mottram's avatar
Initial  
Bob Mottram committed
719
        self.end_headers()
Bob Mottram's avatar
Bob Mottram committed
720
        if not self._write(msg):
Bob Mottram's avatar
Bob Mottram committed
721
            print('Error when showing ' + str(httpCode))
Bob Mottram's avatar
Initial  
Bob Mottram committed
722

Bob Mottram's avatar
Bob Mottram committed
723
    def _200(self) -> None:
Bob Mottram's avatar
Bob Mottram committed
724 725 726 727 728 729 730 731 732
        if self.server.translate:
            self._httpReturnCode(200, self.server.translate['Ok'],
                                 self.server.translate['This is nothing ' +
                                                       'less than an utter ' +
                                                       'triumph'])
        else:
            self._httpReturnCode(200, 'Ok',
                                 'This is nothing less ' +
                                 'than an utter triumph')
Bob Mottram's avatar
Bob Mottram committed
733

Bob Mottram's avatar
Bob Mottram committed
734
    def _404(self) -> None:
Bob Mottram's avatar
Bob Mottram committed
735 736 737 738 739 740 741 742 743 744
        if self.server.translate:
            self._httpReturnCode(404, self.server.translate['Not Found'],
                                 self.server.translate['These are not the ' +
                                                       'droids you are ' +
                                                       'looking for'])
        else:
            self._httpReturnCode(404, 'Not Found',
                                 'These are not the ' +
                                 'droids you are ' +
                                 'looking for')
Bob Mottram's avatar
Bob Mottram committed
745

746
    def _304(self) -> None:
Bob Mottram's avatar
Bob Mottram committed
747 748 749 750 751 752 753 754 755 756
        if self.server.translate:
            self._httpReturnCode(304, self.server.translate['Not changed'],
                                 self.server.translate['The contents of ' +
                                                       'your local cache ' +
                                                       'are up to date'])
        else:
            self._httpReturnCode(304, 'Not changed',
                                 'The contents of ' +
                                 'your local cache ' +
                                 'are up to date')
757

Bob Mottram's avatar
Bob Mottram committed
758
    def _400(self) -> None:
Bob Mottram's avatar
Bob Mottram committed
759 760 761 762 763 764 765
        if self.server.translate:
            self._httpReturnCode(400, self.server.translate['Bad Request'],
                                 self.server.translate['Better luck ' +
                                                       'next time'])
        else:
            self._httpReturnCode(400, 'Bad Request',
                                 'Better luck next time')
Bob Mottram's avatar
Bob Mottram committed
766 767

    def _503(self) -> None:
Bob Mottram's avatar
Bob Mottram committed
768 769 770 771 772 773 774 775 776
        if self.server.translate:
            self._httpReturnCode(503, self.server.translate['Unavailable'],
                                 self.server.translate['The server is busy. ' +
                                                       'Please try again ' +
                                                       'later'])
        else:
            self._httpReturnCode(503, 'Unavailable',
                                 'The server is busy. Please try again ' +
                                 'later')
Bob Mottram's avatar
Bob Mottram committed
777

Bob Mottram's avatar
Bob Mottram committed
778
    def _write(self, msg) -> bool:
Bob Mottram's avatar
Bob Mottram committed
779 780
        tries = 0
        while tries < 5:
Bob Mottram's avatar
Bob Mottram committed
781 782
            try:
                self.wfile.write(msg)
Bob Mottram's avatar
Bob Mottram committed
783
                return True
Bob Mottram's avatar
Bob Mottram committed
784 785
            except Exception as e:
                print(e)
Bob Mottram's avatar
Bob Mottram committed
786
                time.sleep(0.5)
Bob Mottram's avatar
Bob Mottram committed
787
            tries += 1
Bob Mottram's avatar
Bob Mottram committed
788
        return False
789 790 791 792

    def _robotsTxt(self) -> bool:
        if not self.path.lower().startswith('/robot'):
            return False
Bob Mottram's avatar
Bob Mottram committed
793 794
        msg = 'User-agent: *\nDisallow: /'
        msg = msg.encode('utf-8')
Bob Mottram's avatar
Bob Mottram committed
795 796
        msglen = len(msg)
        self._set_headers('text/plain; charset=utf-8', msglen,
Bob Mottram's avatar
Bob Mottram committed
797
                          None, self.server.domainFull)
798
        self._write(msg)
Bob Mottram's avatar
Bob Mottram committed
799 800
        return True

801 802
    def _hasAccept(self, callingDomain: str) -> bool:
        if self.headers.get('Accept') or callingDomain.endswith('.b32.i2p'):
803 804 805 806
            if not self.headers.get('Accept'):
                self.headers['Accept'] = \
                    'text/html,application/xhtml+xml,' \
                    'application/xml;q=0.9,image/webp,*/*;q=0.8'
807 808 809
            return True
        return False

Bob Mottram's avatar
Bob Mottram committed
810 811
    def _mastoApiV1(self, path: str, callingDomain: str,
                    authorized: bool,
812 813 814
                    httpPrefix: str,
                    baseDir: str, nickname: str, domain: str,
                    domainFull: str) -> bool:
Bob Mottram's avatar
Bob Mottram committed
815 816 817 818
        """This is a vestigil mastodon API for the purpose
        of returning an empty result to sites like
        https://mastopeek.app-dist.eu
        """