diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index d6501a39a..d3e2d1587 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -3,6 +3,6 @@ image: debian:testing
test:
script:
- apt-get update
- - apt-get install -y python3-crypto python3-dateutil python3-idna python3-numpy python3-pil.imagetk python3-pycryptodome python3-requests python3-socks python3-setuptools python3-pyqrcode
+ - apt-get install -y python3-cryptography python3-dateutil python3-idna python3-numpy python3-pil.imagetk python3-requests python3-socks python3-setuptools python3-pyqrcode
- python3 epicyon.py --tests
- python3 epicyon.py --testsnetwork
diff --git a/blocking.py b/blocking.py
index f0759f963..29aa2a45b 100644
--- a/blocking.py
+++ b/blocking.py
@@ -7,6 +7,9 @@ __email__ = "bob@freedombone.net"
__status__ = "Production"
import os
+from datetime import datetime
+from utils import fileLastModified
+from utils import setConfigParam
from utils import hasUsersPath
from utils import getFullDomain
from utils import removeIdEnding
@@ -175,15 +178,27 @@ def isBlockedDomain(baseDir: str, domain: str) -> bool:
if noOfSections > 2:
shortDomain = domain[noOfSections-2] + '.' + domain[noOfSections-1]
- globalBlockingFilename = baseDir + '/accounts/blocking.txt'
- if os.path.isfile(globalBlockingFilename):
- with open(globalBlockingFilename, 'r') as fpBlocked:
- blockedStr = fpBlocked.read()
- if '*@' + domain in blockedStr:
- return True
- if shortDomain:
- if '*@' + shortDomain in blockedStr:
+ allowFilename = baseDir + '/accounts/allowedinstances.txt'
+ if not os.path.isfile(allowFilename):
+ # instance block list
+ globalBlockingFilename = baseDir + '/accounts/blocking.txt'
+ if os.path.isfile(globalBlockingFilename):
+ with open(globalBlockingFilename, 'r') as fpBlocked:
+ blockedStr = fpBlocked.read()
+ if '*@' + domain in blockedStr:
return True
+ if shortDomain:
+ if '*@' + shortDomain in blockedStr:
+ return True
+ else:
+ # instance allow list
+ if not shortDomain:
+ if domain not in open(allowFilename).read():
+ return True
+ else:
+ if shortDomain not in open(allowFilename).read():
+ return True
+
return False
@@ -344,3 +359,91 @@ def outboxUndoBlock(baseDir: str, httpPrefix: str,
nicknameBlocked, domainBlockedFull)
if debug:
print('DEBUG: post undo blocked via c2s - ' + postFilename)
+
+
+def setBrochMode(baseDir: str, domainFull: str, enabled: bool) -> None:
+ """Broch mode can be used to lock down the instance during
+ a period of time when it is temporarily under attack.
+ For example, where an adversary is constantly spinning up new
+ instances.
+ It surveys the following lists of all accounts and uses that
+ to construct an instance level allow list. Anything arriving
+ which is then not from one of the allowed domains will be dropped
+ """
+ allowFilename = baseDir + '/accounts/allowedinstances.txt'
+
+ if not enabled:
+ # remove instance allow list
+ if os.path.isfile(allowFilename):
+ os.remove(allowFilename)
+ print('Broch mode turned off')
+ else:
+ if os.path.isfile(allowFilename):
+ lastModified = fileLastModified(allowFilename)
+ print('Broch mode already activated ' + lastModified)
+ return
+ # generate instance allow list
+ allowedDomains = [domainFull]
+ followFiles = ('following.txt', 'followers.txt')
+ for subdir, dirs, files in os.walk(baseDir + '/accounts'):
+ for acct in dirs:
+ if '@' not in acct:
+ continue
+ if 'inbox@' in acct or 'news@' in acct:
+ continue
+ accountDir = os.path.join(baseDir + '/accounts', acct)
+ for followFileType in followFiles:
+ followingFilename = accountDir + '/' + followFileType
+ if not os.path.isfile(followingFilename):
+ continue
+ with open(followingFilename, "r") as f:
+ followList = f.readlines()
+ for handle in followList:
+ if '@' not in handle:
+ continue
+ handle = handle.replace('\n', '')
+ handleDomain = handle.split('@')[1]
+ if handleDomain not in allowedDomains:
+ allowedDomains.append(handleDomain)
+ break
+
+ # write the allow file
+ allowFile = open(allowFilename, "w+")
+ if allowFile:
+ allowFile.write(domainFull + '\n')
+ for d in allowedDomains:
+ allowFile.write(d + '\n')
+ allowFile.close()
+ print('Broch mode enabled')
+
+ setConfigParam(baseDir, "brochMode", enabled)
+
+
+def brochModeLapses(baseDir: str, lapseDays=7) -> bool:
+ """After broch mode is enabled it automatically
+ elapses after a period of time
+ """
+ allowFilename = baseDir + '/accounts/allowedinstances.txt'
+ if not os.path.isfile(allowFilename):
+ return False
+ lastModified = fileLastModified(allowFilename)
+ modifiedDate = None
+ brochMode = True
+ try:
+ modifiedDate = \
+ datetime.strptime(lastModified, "%Y-%m-%dT%H:%M:%SZ")
+ except BaseException:
+ return brochMode
+ if not modifiedDate:
+ return brochMode
+ currTime = datetime.datetime.utcnow()
+ daysSinceBroch = (currTime - modifiedDate).days
+ if daysSinceBroch >= lapseDays:
+ try:
+ os.remove(allowFilename)
+ brochMode = False
+ setConfigParam(baseDir, "brochMode", brochMode)
+ print('Broch mode has elapsed')
+ except BaseException:
+ pass
+ return brochMode
diff --git a/daemon.py b/daemon.py
index 577afe37f..f2515ad8e 100644
--- a/daemon.py
+++ b/daemon.py
@@ -109,6 +109,7 @@ from threads import threadWithTrace
from threads import removeDormantThreads
from media import replaceYouTube
from media import attachMedia
+from blocking import setBrochMode
from blocking import addBlock
from blocking import removeBlock
from blocking import addGlobalBlock
@@ -185,6 +186,7 @@ from shares import addShare
from shares import removeShare
from shares import expireShares
from categories import setHashtagCategory
+from utils import getLocalNetworkAddresses
from utils import decodedHost
from utils import isPublicPost
from utils import getLockedAccount
@@ -478,6 +480,10 @@ class PubServer(BaseHTTPRequestHandler):
if 'text/html' not in self.headers['Accept']:
return False
if self.headers['Accept'].startswith('*'):
+ if self.headers.get('User-Agent'):
+ if 'ELinks' in self.headers['User-Agent'] or \
+ 'Lynx' in self.headers['User-Agent']:
+ return True
return False
if 'json' in self.headers['Accept']:
return False
@@ -1153,20 +1159,46 @@ class PubServer(BaseHTTPRequestHandler):
# check for blocked domains so that they can be rejected early
messageDomain = None
- if messageJson.get('actor'):
- messageDomain, messagePort = \
- getDomainFromActor(messageJson['actor'])
- if isBlockedDomain(self.server.baseDir, messageDomain):
- print('POST from blocked domain ' + messageDomain)
- self._400()
- self.server.POSTbusy = False
- return 3
- else:
+ if not messageJson.get('actor'):
print('Message arriving at inbox queue has no actor')
self._400()
self.server.POSTbusy = False
return 3
+ # actor should be a string
+ if not isinstance(messageJson['actor'], str):
+ self._400()
+ self.server.POSTbusy = False
+ return 3
+
+ # actor should look like a url
+ if '://' not in messageJson['actor'] or \
+ '.' not in messageJson['actor']:
+ print('POST actor does not look like a url ' +
+ messageJson['actor'])
+ self._400()
+ self.server.POSTbusy = False
+ return 3
+
+ # sent by an actor on a local network address?
+ if not self.server.allowLocalNetworkAccess:
+ localNetworkPatternList = getLocalNetworkAddresses()
+ for localNetworkPattern in localNetworkPatternList:
+ if localNetworkPattern in messageJson['actor']:
+ print('POST actor contains local network address ' +
+ messageJson['actor'])
+ self._400()
+ self.server.POSTbusy = False
+ return 3
+
+ messageDomain, messagePort = \
+ getDomainFromActor(messageJson['actor'])
+ if isBlockedDomain(self.server.baseDir, messageDomain):
+ print('POST from blocked domain ' + messageDomain)
+ self._400()
+ self.server.POSTbusy = False
+ return 3
+
# if the inbox queue is full then return a busy code
if len(self.server.inboxQueue) >= self.server.maxQueueLength:
if messageDomain:
@@ -4512,6 +4544,18 @@ class PubServer(BaseHTTPRequestHandler):
setConfigParam(baseDir, "verifyAllSignatures",
verifyAllSignatures)
+ brochMode = False
+ if fields.get('brochMode'):
+ if fields['brochMode'] == 'on':
+ brochMode = True
+ currBrochMode = \
+ getConfigParam(baseDir, "brochMode")
+ if brochMode != currBrochMode:
+ setBrochMode(self.server.baseDir,
+ self.server.domainFull,
+ brochMode)
+ setConfigParam(baseDir, "brochMode", brochMode)
+
# change moderators list
if fields.get('moderators'):
if path.startswith('/users/' +
@@ -10099,9 +10143,13 @@ class PubServer(BaseHTTPRequestHandler):
# manifest for progressive web apps
if '/manifest.json' in self.path:
- self._progressiveWebAppManifest(callingDomain,
- GETstartTime, GETtimings)
- return
+ if self._hasAccept(callingDomain):
+ if not self._requestHTTP():
+ self._progressiveWebAppManifest(callingDomain,
+ GETstartTime, GETtimings)
+ return
+ else:
+ self.path = '/'
# default newswire favicon, for links to sites which
# have no favicon
@@ -13889,7 +13937,8 @@ def loadTokens(baseDir: str, tokensDict: {}, tokensLookup: {}) -> None:
break
-def runDaemon(verifyAllSignatures: bool,
+def runDaemon(brochMode: bool,
+ verifyAllSignatures: bool,
sendThreadsTimeoutMins: int,
dormantMonths: int,
maxNewswirePosts: int,
@@ -14142,6 +14191,9 @@ def runDaemon(verifyAllSignatures: bool,
# cache to store css files
httpd.cssCache = {}
+ # whether to enable broch mode, which locks down the instance
+ setBrochMode(baseDir, httpd.domainFull, brochMode)
+
if not os.path.isdir(baseDir + '/accounts/inbox@' + domain):
print('Creating shared inbox: inbox@' + domain)
createSharedInbox(baseDir, 'inbox', domain, port, httpPrefix)
diff --git a/epicyon-profile.css b/epicyon-profile.css
index 473fbdb37..f5af28671 100644
--- a/epicyon-profile.css
+++ b/epicyon-profile.css
@@ -106,11 +106,11 @@
--column-left-icons-margin: 0;
--column-right-border-width: 0;
--column-left-border-color: black;
- --column-left-icon-size: 20%;
+ --column-left-icon-size: 2.1vw;
--column-left-icon-size-mobile: 10%;
--column-left-image-width-mobile: 40vw;
--column-right-image-width-mobile: 100vw;
- --column-right-icon-size: 20%;
+ --column-right-icon-size: 2.1vw;
--column-right-icon-size-mobile: 10%;
--newswire-date-color: white;
--newswire-voted-background-color: black;
diff --git a/epicyon.py b/epicyon.py
index 1a741302c..cf0289c65 100644
--- a/epicyon.py
+++ b/epicyon.py
@@ -279,6 +279,11 @@ parser.add_argument("--verifyAllSignatures",
const=True, default=False,
help="Whether to require that all incoming " +
"posts have valid jsonld signatures")
+parser.add_argument("--brochMode",
+ dest='brochMode',
+ type=str2bool, nargs='?',
+ const=True, default=False,
+ help="Enable broch mode")
parser.add_argument("--noapproval", type=str2bool, nargs='?',
const=True, default=False,
help="Allow followers without approval")
@@ -2292,6 +2297,11 @@ verifyAllSignatures = \
if verifyAllSignatures is not None:
args.verifyAllSignatures = bool(verifyAllSignatures)
+brochMode = \
+ getConfigParam(baseDir, 'brochMode')
+if brochMode is not None:
+ args.brochMode = bool(brochMode)
+
YTDomain = getConfigParam(baseDir, 'youtubedomain')
if YTDomain:
if '://' in YTDomain:
@@ -2305,7 +2315,8 @@ if setTheme(baseDir, themeName, domain, args.allowLocalNetworkAccess):
print('Theme set to ' + themeName)
if __name__ == "__main__":
- runDaemon(args.verifyAllSignatures,
+ runDaemon(args.brochMode,
+ args.verifyAllSignatures,
args.sendThreadsTimeoutMins,
args.dormantMonths,
args.maxNewswirePosts,
diff --git a/inbox.py b/inbox.py
index 8f216e1c6..86507b87d 100644
--- a/inbox.py
+++ b/inbox.py
@@ -51,6 +51,7 @@ from bookmarks import updateBookmarksCollection
from bookmarks import undoBookmarksCollectionEntry
from blocking import isBlocked
from blocking import isBlockedDomain
+from blocking import brochModeLapses
from filters import isFiltered
from utils import updateAnnounceCollection
from utils import undoAnnounceCollectionEntry
@@ -2518,6 +2519,8 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int,
# heartbeat to monitor whether the inbox queue is running
heartBeatCtr += 5
if heartBeatCtr >= 10:
+ # turn off broch mode after it has timed out
+ brochModeLapses(baseDir)
print('>>> Heartbeat Q:' + str(len(queue)) + ' ' +
'{:%F %T}'.format(datetime.datetime.now()))
heartBeatCtr = 0
diff --git a/outbox.py b/outbox.py
index 2c3fda855..11d595c6e 100644
--- a/outbox.py
+++ b/outbox.py
@@ -14,6 +14,7 @@ from posts import outboxMessageCreateWrap
from posts import savePostToBox
from posts import sendToFollowersThread
from posts import sendToNamedAddresses
+from utils import getLocalNetworkAddresses
from utils import getFullDomain
from utils import removeIdEnding
from utils import getDomainFromActor
@@ -114,6 +115,23 @@ def postMessageToOutbox(messageJson: {}, postToNickname: str,
'Create does not have the "to" parameter ' +
str(messageJson))
return False
+
+ # actor should be a string
+ if not isinstance(messageJson['actor'], str):
+ return False
+
+ # actor should look like a url
+ if '://' not in messageJson['actor'] or \
+ '.' not in messageJson['actor']:
+ return False
+
+ # sent by an actor on a local network address?
+ if not allowLocalNetworkAccess:
+ localNetworkPatternList = getLocalNetworkAddresses()
+ for localNetworkPattern in localNetworkPatternList:
+ if localNetworkPattern in messageJson['actor']:
+ return False
+
testDomain, testPort = getDomainFromActor(messageJson['actor'])
testDomain = getFullDomain(testDomain, testPort)
if isBlockedDomain(baseDir, testDomain):
diff --git a/setup.py b/setup.py
new file mode 100644
index 000000000..6819ec0fd
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,6 @@
+#!/usr/bin/python3
+
+import setuptools
+
+if __name__ == "__main__":
+ setuptools.setup()
diff --git a/tests.py b/tests.py
index 7186012a1..5a4bd233e 100644
--- a/tests.py
+++ b/tests.py
@@ -325,8 +325,10 @@ def createServerAlice(path: str, domain: str, port: int,
sendThreadsTimeoutMins = 30
maxFollowers = 10
verifyAllSignatures = True
+ brochMode = False
print('Server running: Alice')
- runDaemon(verifyAllSignatures,
+ runDaemon(brochMode,
+ verifyAllSignatures,
sendThreadsTimeoutMins,
dormantMonths, maxNewswirePosts,
allowLocalNetworkAccess,
@@ -420,8 +422,10 @@ def createServerBob(path: str, domain: str, port: int,
sendThreadsTimeoutMins = 30
maxFollowers = 10
verifyAllSignatures = True
+ brochMode = False
print('Server running: Bob')
- runDaemon(verifyAllSignatures,
+ runDaemon(brochMode,
+ verifyAllSignatures,
sendThreadsTimeoutMins,
dormantMonths, maxNewswirePosts,
allowLocalNetworkAccess,
@@ -469,8 +473,10 @@ def createServerEve(path: str, domain: str, port: int, federationList: [],
sendThreadsTimeoutMins = 30
maxFollowers = 10
verifyAllSignatures = True
+ brochMode = False
print('Server running: Eve')
- runDaemon(verifyAllSignatures,
+ runDaemon(brochMode,
+ verifyAllSignatures,
sendThreadsTimeoutMins,
dormantMonths, maxNewswirePosts,
allowLocalNetworkAccess,
diff --git a/translations/ar.json b/translations/ar.json
index 605d5b98b..fd4f8fbbe 100644
--- a/translations/ar.json
+++ b/translations/ar.json
@@ -368,5 +368,6 @@
"Skip to Newswire": "انتقل إلى Newswire",
"Skip to Links": "تخطي إلى روابط الويب",
"Publish a blog article": "نشر مقال بلوق",
- "Featured writer": "كاتب متميز"
+ "Featured writer": "كاتب متميز",
+ "Broch mode": "وضع الكتيب"
}
diff --git a/translations/ca.json b/translations/ca.json
index 3c8846bbf..2965f88d8 100644
--- a/translations/ca.json
+++ b/translations/ca.json
@@ -368,5 +368,6 @@
"Skip to Newswire": "Vés a Newswire",
"Skip to Links": "Vés als enllaços web",
"Publish a blog article": "Publicar un article del bloc",
- "Featured writer": "Escriptor destacat"
+ "Featured writer": "Escriptor destacat",
+ "Broch mode": "Mode Broch"
}
diff --git a/translations/cy.json b/translations/cy.json
index 6e45de39d..f1e31b718 100644
--- a/translations/cy.json
+++ b/translations/cy.json
@@ -368,5 +368,6 @@
"Skip to Newswire": "Neidio i Newswire",
"Skip to Links": "Neidio i Dolenni Gwe",
"Publish a blog article": "Cyhoeddi erthygl blog",
- "Featured writer": "Awdur dan sylw"
+ "Featured writer": "Awdur dan sylw",
+ "Broch mode": "Modd Broch"
}
diff --git a/translations/de.json b/translations/de.json
index c15cb492f..4053a453c 100644
--- a/translations/de.json
+++ b/translations/de.json
@@ -368,5 +368,6 @@
"Skip to Newswire": "Springe zu Newswire",
"Skip to Links": "Springe zu Weblinks",
"Publish a blog article": "Veröffentlichen Sie einen Blog-Artikel",
- "Featured writer": "Ausgewählter Schriftsteller"
+ "Featured writer": "Ausgewählter Schriftsteller",
+ "Broch mode": "Broch-Modus"
}
diff --git a/translations/en.json b/translations/en.json
index 3fbbfdd34..d376154ee 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -368,5 +368,6 @@
"Skip to Newswire": "Skip to Newswire",
"Skip to Links": "Skip to Links",
"Publish a blog article": "Publish a blog article",
- "Featured writer": "Featured writer"
+ "Featured writer": "Featured writer",
+ "Broch mode": "Broch mode"
}
diff --git a/translations/es.json b/translations/es.json
index 3566d06b0..135ff9c13 100644
--- a/translations/es.json
+++ b/translations/es.json
@@ -368,5 +368,6 @@
"Skip to Newswire": "Saltar a Newswire",
"Skip to Links": "Saltar a enlaces web",
"Publish a blog article": "Publica un artículo de blog",
- "Featured writer": "Escritora destacada"
+ "Featured writer": "Escritora destacada",
+ "Broch mode": "Modo broche"
}
diff --git a/translations/fr.json b/translations/fr.json
index 78c7ff547..308ba5e9b 100644
--- a/translations/fr.json
+++ b/translations/fr.json
@@ -368,5 +368,6 @@
"Skip to Newswire": "Passer à Newswire",
"Skip to Links": "Passer aux liens Web",
"Publish a blog article": "Publier un article de blog",
- "Featured writer": "Écrivain en vedette"
+ "Featured writer": "Écrivain en vedette",
+ "Broch mode": "Mode Broch"
}
diff --git a/translations/ga.json b/translations/ga.json
index 75f87780c..92f67c50f 100644
--- a/translations/ga.json
+++ b/translations/ga.json
@@ -368,5 +368,6 @@
"Skip to Newswire": "Scipeáil chuig Newswire",
"Skip to Links": "Scipeáil chuig Naisc Ghréasáin",
"Publish a blog article": "Foilsigh alt blagála",
- "Featured writer": "Scríbhneoir mór le rá"
+ "Featured writer": "Scríbhneoir mór le rá",
+ "Broch mode": "Modh broch"
}
diff --git a/translations/hi.json b/translations/hi.json
index acf417f88..82c401422 100644
--- a/translations/hi.json
+++ b/translations/hi.json
@@ -368,5 +368,6 @@
"Skip to Newswire": "Newswire पर जाएं",
"Skip to Links": "वेब लिंक पर जाएं",
"Publish a blog article": "एक ब्लॉग लेख प्रकाशित करें",
- "Featured writer": "फीचर्ड लेखक"
+ "Featured writer": "फीचर्ड लेखक",
+ "Broch mode": "ब्रोच मोड"
}
diff --git a/translations/it.json b/translations/it.json
index f2bc1e1ed..c189c2335 100644
--- a/translations/it.json
+++ b/translations/it.json
@@ -368,5 +368,6 @@
"Skip to Newswire": "Passa a Newswire",
"Skip to Links": "Passa a collegamenti Web",
"Publish a blog article": "Pubblica un articolo sul blog",
- "Featured writer": "Scrittore in primo piano"
+ "Featured writer": "Scrittore in primo piano",
+ "Broch mode": "Modalità Broch"
}
diff --git a/translations/ja.json b/translations/ja.json
index 9f1275544..49bfffbef 100644
--- a/translations/ja.json
+++ b/translations/ja.json
@@ -368,5 +368,6 @@
"Skip to Newswire": "Newswireにスキップ",
"Skip to Links": "Webリンクにスキップ",
"Publish a blog article": "ブログ記事を公開する",
- "Featured writer": "注目の作家"
+ "Featured writer": "注目の作家",
+ "Broch mode": "ブロッホモード"
}
diff --git a/translations/oc.json b/translations/oc.json
index c9e3717a0..fa4620d9a 100644
--- a/translations/oc.json
+++ b/translations/oc.json
@@ -364,5 +364,6 @@
"Skip to Newswire": "Skip to Newswire",
"Skip to Links": "Skip to Links",
"Publish a blog article": "Publish a blog article",
- "Featured writer": "Featured writer"
+ "Featured writer": "Featured writer",
+ "Broch mode": "Broch mode"
}
diff --git a/translations/pt.json b/translations/pt.json
index b2a29ca2d..52694082b 100644
--- a/translations/pt.json
+++ b/translations/pt.json
@@ -368,5 +368,6 @@
"Skip to Newswire": "Pular para Newswire",
"Skip to Links": "Pular para links da web",
"Publish a blog article": "Publique um artigo de blog",
- "Featured writer": "Escritor em destaque"
+ "Featured writer": "Escritor em destaque",
+ "Broch mode": "Modo broch"
}
diff --git a/translations/ru.json b/translations/ru.json
index 618c5cbf0..41c2d5a91 100644
--- a/translations/ru.json
+++ b/translations/ru.json
@@ -368,5 +368,6 @@
"Skip to Newswire": "Перейти к ленте новостей",
"Skip to Links": "Перейти к веб-ссылкам",
"Publish a blog article": "Опубликовать статью в блоге",
- "Featured writer": "Избранный писатель"
+ "Featured writer": "Избранный писатель",
+ "Broch mode": "Брош режим"
}
diff --git a/translations/zh.json b/translations/zh.json
index e7fab5755..f63deb616 100644
--- a/translations/zh.json
+++ b/translations/zh.json
@@ -368,5 +368,6 @@
"Skip to Newswire": "跳到新闻专线",
"Skip to Links": "跳到网页链接",
"Publish a blog article": "发布博客文章",
- "Featured writer": "特色作家"
+ "Featured writer": "特色作家",
+ "Broch mode": "断点模式"
}
diff --git a/utils.py b/utils.py
index f1247e898..8f2348062 100644
--- a/utils.py
+++ b/utils.py
@@ -605,6 +605,12 @@ def urlPermitted(url: str, federationList: []):
return False
+def getLocalNetworkAddresses() -> []:
+ """Returns patterns for local network address detection
+ """
+ return ('localhost', '127.0.', '192.168', '10.0.')
+
+
def dangerousMarkup(content: str, allowLocalNetworkAccess: bool) -> bool:
"""Returns true if the given content contains dangerous html markup
"""
@@ -615,7 +621,7 @@ def dangerousMarkup(content: str, allowLocalNetworkAccess: bool) -> bool:
contentSections = content.split('<')
invalidPartials = ()
if not allowLocalNetworkAccess:
- invalidPartials = ('localhost', '127.0.', '192.168', '10.0.')
+ invalidPartials = getLocalNetworkAddresses()
invalidStrings = ('script', 'canvas', 'style', 'abbr',
'frame', 'iframe', 'html', 'body',
'hr', 'allow-popups', 'allow-scripts')
diff --git a/webapp_profile.py b/webapp_profile.py
index 8c7704390..0d2fe362d 100644
--- a/webapp_profile.py
+++ b/webapp_profile.py
@@ -1278,6 +1278,16 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
' ' + \
translate['Verify all signatures'] + '
\n'
+ if getConfigParam(baseDir, "brochMode"):
+ instanceStr += \
+ ' ' + \
+ translate['Broch mode'] + '
\n'
+ else:
+ instanceStr += \
+ ' ' + \
+ translate['Broch mode'] + '
\n'
instanceStr += ''
moderators = ''
diff --git a/webapp_timeline.py b/webapp_timeline.py
index 0c0407520..b5a9a41b0 100644
--- a/webapp_timeline.py
+++ b/webapp_timeline.py
@@ -416,19 +416,19 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
translate['Mod']
navLinks = {
menuProfile: '/users/' + nickname,
- menuInbox: usersPath + '/inbox#timeline',
+ menuInbox: usersPath + '/inbox#timelineposts',
menuSearch: usersPath + '/search',
menuNewPost: usersPath + '/newpost',
menuCalendar: usersPath + '/calendar',
- menuDM: usersPath + '/dm#timeline',
- menuReplies: usersPath + '/tlreplies#timeline',
- menuOutbox: usersPath + '/inbox#timeline',
- menuBookmarks: usersPath + '/tlbookmarks#timeline',
- menuShares: usersPath + '/tlshares#timeline',
- menuBlogs: usersPath + '/tlblogs#timeline',
- # menuEvents: usersPath + '/tlevents#timeline',
- menuNewswire: '#newswire',
- menuLinks: '#links'
+ menuDM: usersPath + '/dm#timelineposts',
+ menuReplies: usersPath + '/tlreplies#timelineposts',
+ menuOutbox: usersPath + '/inbox#timelineposts',
+ menuBookmarks: usersPath + '/tlbookmarks#timelineposts',
+ menuShares: usersPath + '/tlshares#timelineposts',
+ menuBlogs: usersPath + '/tlblogs#timelineposts',
+ # menuEvents: usersPath + '/tlevents#timelineposts',
+ menuNewswire: usersPath + '/newswiremobile',
+ menuLinks: usersPath + '/linksmobile'
}
if moderator:
navLinks[menuModeration] = usersPath + '/moderation#modtimeline'
@@ -502,7 +502,7 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
calendarImage, followApprovals,
iconsAsButtons)
- tlStr += '
' + banner + '' + htmlStr += '
\n' + banner + '\n\n' if subHeading: htmlStr += '