forked from cory/tildefriends
Compare commits
107 Commits
5474c5a101
...
tasiaiso-s
Author | SHA1 | Date | |
---|---|---|---|
0b4ac2b355
|
|||
fae2771645
|
|||
2bb6d68122
|
|||
5c8c6e8760
|
|||
85ac8080f4
|
|||
0751699bc8
|
|||
5551fd2dea
|
|||
69b2e2a955
|
|||
34c7fa8312
|
|||
396f37ee3b
|
|||
7caf4a0173 | |||
385524352c | |||
5ca5323782 | |||
ba6da856bb | |||
c0e72246cc | |||
c7ab5447ea | |||
5fdd461159 | |||
421955f2a0 | |||
a28f6985ed | |||
8244dddab7 | |||
a5ca436eaa | |||
d7fc1c2c88 | |||
382627ef8d | |||
17667b4cf8 | |||
5231ec22e7 | |||
929ae1b709 | |||
f01f7a5ab9 | |||
a2dce833f8 | |||
de6c7a4fd4 | |||
4edee0f7f6 | |||
988a807fa4 | |||
5258e4253d | |||
09ba86dec5 | |||
78d8a1aa23 | |||
22def15209 | |||
4cbda7a849 | |||
be85a620ef | |||
0b07b678b4 | |||
4733ce9287 | |||
48d6bf4c15 | |||
8c759bcbac | |||
b5ed7014f6 | |||
6cd9dea186 | |||
202b416acf | |||
93d46f5610 | |||
c5ddf3ac99 | |||
a9cb913a47 | |||
b7b5d4f1a5 | |||
a947396bad | |||
d528bc808e | |||
c6fd05c2cf | |||
d6bb9d311a | |||
53b4cbbf8c | |||
628716ec28 | |||
bd14168627 | |||
96037d4da6 | |||
5448e773d8 | |||
848ef21c7c | |||
2ecae7da93 | |||
d9ce569eb9 | |||
eacaf392b1 | |||
ce16592b6a | |||
295d76d354 | |||
23b3c998bd | |||
b5e966c9a1 | |||
96cb6f4b12 | |||
e2c0f82ec0 | |||
dbf28c03e6 | |||
26165e30de | |||
c52331a23a | |||
8007e71e1d | |||
28d08e013f | |||
64bbd383de | |||
8a9f53102b | |||
0412b97170 | |||
c8b8a8fc03 | |||
95d3090b9b | |||
49129ee6dd | |||
6a7ecb0d4a | |||
1ceeed1007 | |||
a7922ff44e | |||
a421604ed5 | |||
7d182db32f | |||
c5cb9979d3 | |||
b9a73106ed | |||
c674cca482 | |||
81d1228b92 | |||
6ae61d5b81 | |||
9cb872eec2 | |||
68e8c010b7 | |||
9671413906 | |||
4c8d24c319 | |||
e50144bd34 | |||
9f3171e3f1 | |||
cc92748747 | |||
0a0b0c1adb | |||
92a74026a6 | |||
3fa1c6c420 | |||
b04eccdbda | |||
9ce30dee70 | |||
3c0b680b8e | |||
895356897b | |||
9164be2f37 | |||
5385264f94 | |||
610e756c07 | |||
15c9f8f458 | |||
fb704a5b83 |
@ -1,4 +1,5 @@
|
||||
.svn
|
||||
db.sqlite
|
||||
db.*
|
||||
out/**/*.o
|
||||
out/**/*.d
|
||||
NOTES.md
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,3 +8,4 @@ out
|
||||
*.swo
|
||||
*.swp
|
||||
.zsign_cache/
|
||||
NOTES.md
|
||||
|
21
.gitmodules
vendored
Normal file
21
.gitmodules
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
[submodule "deps/zlib"]
|
||||
path = deps/zlib
|
||||
url = https://github.com/madler/zlib.git
|
||||
[submodule "deps/libsodium"]
|
||||
path = deps/libsodium
|
||||
url = https://github.com/jedisct1/libsodium.git
|
||||
[submodule "deps/quickjs"]
|
||||
path = deps/quickjs
|
||||
url = https://github.com/bellard/quickjs.git
|
||||
[submodule "deps/crypt_blowfish"]
|
||||
path = deps/crypt_blowfish
|
||||
url = https://github.com/openwall/crypt_blowfish.git
|
||||
[submodule "deps/libbacktrace"]
|
||||
path = deps/libbacktrace
|
||||
url = https://github.com/ianlancetaylor/libbacktrace.git
|
||||
[submodule "deps/libuv"]
|
||||
path = deps/libuv
|
||||
url = https://github.com/libuv/libuv.git
|
||||
[submodule "deps/picohttpparser"]
|
||||
path = deps/picohttpparser
|
||||
url = https://github.com/h2o/picohttpparser.git
|
5
.markdownlint.yaml
Normal file
5
.markdownlint.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
default: true
|
||||
MD010: false # Ignore tabs in code blocks
|
||||
MD013: false # Don't wrap lines by default
|
||||
MD046:
|
||||
style: "fenced" # Force fenced code blocks
|
@ -12,3 +12,8 @@ deps
|
||||
apps/ssb/tribute.esm.js
|
||||
apps/api/app.js
|
||||
**/emojis.json
|
||||
|
||||
# only markdownlint should deal with the documentation
|
||||
docs/**/*.md
|
||||
|
||||
NOTES.md
|
||||
|
69
GNUmakefile
69
GNUmakefile
@ -3,11 +3,11 @@
|
||||
MAKEFLAGS += --warn-undefined-variables
|
||||
MAKEFLAGS += --no-builtin-rules
|
||||
|
||||
VERSION_CODE := 17
|
||||
VERSION_NUMBER := 0.0.17-wip
|
||||
VERSION_NAME := Please enjoy responsibly.
|
||||
VERSION_CODE := 19
|
||||
VERSION_NUMBER := 0.0.19-wip
|
||||
VERSION_NAME := Don't let your loyalty become a burden.
|
||||
|
||||
SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3450200.zip
|
||||
SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3450300.zip
|
||||
LIBUV_URL := https://dist.libuv.org/dist/v1.48.0/libuv-v1.48.0.tar.gz
|
||||
|
||||
PROJECT = tildefriends
|
||||
@ -17,17 +17,7 @@ UNAME_M := $(shell uname -m)
|
||||
|
||||
ANDROID_SDK ?= ~/Android/Sdk
|
||||
|
||||
ifeq ($(UNAME_M),x86_64)
|
||||
ifneq ($(UNAME_S),Haiku)
|
||||
debug: CFLAGS += -fsanitize=address -fsanitize=undefined -fno-common
|
||||
debug: LDFLAGS += -fsanitize=address -fsanitize=undefined
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME_M),aarch64)
|
||||
debug: CFLAGS += -fsanitize=address -fsanitize=undefined -fno-common
|
||||
debug: LDFLAGS += -fsanitize=address -fsanitize=undefined
|
||||
endif
|
||||
HAVE_WIN := 0
|
||||
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
BUILD_TYPES := macosdebug macosrelease iosdebug iosrelease iossimdebug iossimrelease
|
||||
@ -51,7 +41,6 @@ LDFLAGS += \
|
||||
-lc++abi
|
||||
HAVE_ANDROID := 0
|
||||
HAVE_LINUX_IOS := 0
|
||||
HAVE_WIN := 0
|
||||
else
|
||||
$(error Unexpected host platform $(UNAME_S).)
|
||||
endif
|
||||
@ -68,11 +57,11 @@ CFLAGS += \
|
||||
-fno-exceptions \
|
||||
-g
|
||||
|
||||
ANDROID_BUILD_TOOLS := $(ANDROID_SDK)/build-tools/34.0.0
|
||||
ANDROID_PLATFORM := $(ANDROID_SDK)/platforms/android-34
|
||||
ANDROID_NDK ?= $(ANDROID_SDK)/ndk/26.2.11394342
|
||||
ANDROID_MIN_SDK_VERSION := 24
|
||||
ANDROID_TARGET_SDK_VERSION := 34
|
||||
ANDROID_BUILD_TOOLS := $(ANDROID_SDK)/build-tools/34.0.0
|
||||
ANDROID_PLATFORM := $(ANDROID_SDK)/platforms/android-$(ANDROID_TARGET_SDK_VERSION)
|
||||
ANDROID_NDK ?= $(ANDROID_SDK)/ndk/26.2.11394342
|
||||
|
||||
ANDROID_ARMV7A_TARGETS := \
|
||||
out/androiddebug-armv7a/tildefriends \
|
||||
@ -222,6 +211,18 @@ $(IOS_TARGETS): LDFLAGS += -Ldeps/openssl/ios/ios64-xcrun/usr/local/lib
|
||||
$(IOSSIM_TARGETS): CFLAGS += -Ideps/openssl/ios/iossimulator-xcrun/usr/local/include
|
||||
$(IOSSIM_TARGETS): LDFLAGS += -Ldeps/openssl/ios/iossimulator-xcrun/usr/local/lib
|
||||
|
||||
ifeq ($(UNAME_M),x86_64)
|
||||
ifneq ($(UNAME_S),Haiku)
|
||||
out/debug/tildefriends: CFLAGS += -fsanitize=address -fsanitize=undefined -fno-common
|
||||
out/debug/tildefriends: LDFLAGS += -fsanitize=address -fsanitize=undefined
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME_M),aarch64)
|
||||
out/debug/tildefriends: CFLAGS += -fsanitize=address -fsanitize=undefined -fno-common
|
||||
out/debug/tildefriends: LDFLAGS += -fsanitize=address -fsanitize=undefined
|
||||
endif
|
||||
|
||||
get_objs = \
|
||||
$(foreach build_type,$(BUILD_TYPES),$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)))))) \
|
||||
$(foreach build_type,debug release,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_unix))))) \
|
||||
@ -578,7 +579,7 @@ $(MINIUNZIP_OBJS): CFLAGS += \
|
||||
LDFLAGS += \
|
||||
-pthread \
|
||||
-lm
|
||||
debug release $(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
|
||||
$(LINUX_TARGETS) $(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
|
||||
-lssl \
|
||||
-lcrypto
|
||||
ifneq ($(UNAME_S),Haiku)
|
||||
@ -692,7 +693,7 @@ CLASS_FILES := $(foreach src,$(JAVA_FILES),out/classes/com/unprompted/tildefrien
|
||||
|
||||
$(CLASS_FILES) &: $(JAVA_FILES)
|
||||
@echo "[javac] $(CLASS_FILES)"
|
||||
@javac --release 8 -Xlint:deprecation -classpath $(ANDROID_PLATFORM)/android.jar -d out/classes $(JAVA_FILES)
|
||||
@javac --release 8 -encoding UTF-8 -Xlint:deprecation -XDuseUnsharedTable=true -classpath $(ANDROID_PLATFORM)/android.jar:$(ANDROID_BUILD_TOOLS)/core-lambda-stubs.jar -d out/classes $(JAVA_FILES)
|
||||
|
||||
out/apk/classes.dex: $(CLASS_FILES)
|
||||
@mkdir -p $(dir $@)
|
||||
@ -728,7 +729,7 @@ out/apk/TildeFriends-arm-%.unsigned.apk:
|
||||
@cp out/apk/res.apk $@.zip
|
||||
@cp out/apk/classes.dex out/apk-arm-$(BUILD_TYPE)/
|
||||
@cd out/apk-arm-$(BUILD_TYPE) && zip -u ../../$@.zip -q -9 -r . && cd ../../
|
||||
@zip -u $@.zip -q $(RAW_FILES)
|
||||
@zip -u $@.zip -q -9 $(RAW_FILES)
|
||||
@$(ANDROID_BUILD_TOOLS)/zipalign -f 4 $@.zip $@
|
||||
|
||||
out/apk/TildeFriends-x86-%.unsigned.apk:
|
||||
@ -741,7 +742,7 @@ out/apk/TildeFriends-x86-%.unsigned.apk:
|
||||
@cp out/apk/res.apk $@.zip
|
||||
@cp out/apk/classes.dex out/apk-x86-$(BUILD_TYPE)/
|
||||
@cd out/apk-x86-$(BUILD_TYPE) && zip -u ../../$@.zip -q -9 -r . && cd ../../
|
||||
@zip -u $@.zip -q $(RAW_FILES)
|
||||
@zip -u $@.zip -q -9 $(RAW_FILES)
|
||||
@$(ANDROID_BUILD_TOOLS)/zipalign -f 4 $@.zip $@
|
||||
|
||||
out/%.apk: out/apk/%.unsigned.apk
|
||||
@ -758,7 +759,7 @@ release-apk: out/TildeFriends-arm-release.zopfli.apk out/TildeFriends-x86-releas
|
||||
|
||||
releaseapkgo: out/TildeFriends-arm-release.apk
|
||||
@adb install -r $<
|
||||
@adb shell am start com.unprompted.tildefriends/.MainActivity
|
||||
@adb shell am start com.unprompted.tildefriends/.TildeFriendsActivity
|
||||
.PHONY: releaseapkgo
|
||||
|
||||
# iOS Support
|
||||
@ -769,10 +770,10 @@ out/%.app/tildefriends.png: src/ios/tildefriends.png
|
||||
@mkdir -p $(dir $@)
|
||||
@cp -v $< $@
|
||||
|
||||
out/%/data.zip: $(RAW_FILES)
|
||||
out/data.zip: $(RAW_FILES)
|
||||
@zip -u $@ -q -9 $(RAW_FILES)
|
||||
|
||||
out/tildefriends-%.app/tildefriends: out/%/tildefriends out/tildefriends-%.app/Info.plist out/tildefriends-%.app/tildefriends.png out/tildefriends-%.app/data.zip
|
||||
out/tildefriends-%.app/tildefriends: out/%/tildefriends out/tildefriends-%.app/Info.plist out/tildefriends-%.app/tildefriends.png out/data.zip
|
||||
@mkdir -p $(dir $@)
|
||||
@cp -v $< $@
|
||||
ifeq ($(HAVE_LINUX_IOS),1)
|
||||
@ -787,6 +788,16 @@ out/tildefriends-%.ipa: out/tildefriends-ios%.app/tildefriends
|
||||
@cd $@.tmp/ && zip -u ../../$@ -q -9 -r ./
|
||||
@rm -rf $@.tmp/
|
||||
|
||||
|
||||
out/%/tildefriends.standalone: out/%/tildefriends out/data.zip
|
||||
@echo "[standalone] $@"
|
||||
@cat $< out/data.zip > $@
|
||||
@chmod +x $@
|
||||
out/%/tildefriends.standalone.exe: out/%/tildefriends.exe out/data.zip
|
||||
@echo "[standalone] $@"
|
||||
@cat $< out/data.zip > $@
|
||||
@chmod +x $@
|
||||
|
||||
iossimdebug-app: out/tildefriends-iossimdebug.app/tildefriends
|
||||
iossimrelease-app: out/tildefriends-iossimrelease.app/tildefriends
|
||||
iosdebug-app: out/tildefriends-iosdebug.app/tildefriends
|
||||
@ -847,11 +858,11 @@ clean:
|
||||
rm -rf $(BUILD_DIR)
|
||||
.PHONY: clean
|
||||
|
||||
dist: release-apk iosrelease-ipa
|
||||
dist: release-apk iosrelease-ipa $(if $(HAVE_WIN), out/winrelease/tildefriends.standalone.exe)
|
||||
@echo [archive] dist/tildefriends-$(VERSION_NUMBER).tar.xz
|
||||
@rm -rf out/tildefriends-$(VERSION_NUMBER)
|
||||
@mkdir -p dist/ out/tildefriends-$(VERSION_NUMBER)
|
||||
@git archive main | tar -x -C out/tildefriends-$(VERSION_NUMBER)
|
||||
@git ls-files --recurse-submodules | tar -c -T- | tar -x -C out/tildefriends-$(VERSION_NUMBER)
|
||||
@tar \
|
||||
--exclude=apps/welcome* \
|
||||
--exclude=deps/libbacktrace/Isaac.Newton-Opticks.txt \
|
||||
@ -876,6 +887,8 @@ dist: release-apk iosrelease-ipa
|
||||
@cp out/TildeFriends-arm-release.zopfli.apk dist/TildeFriends-arm-$(VERSION_NUMBER).apk
|
||||
@echo "[cp] TildeFriends-$(VERSION_NUMBER).ipa"
|
||||
@cp out/tildefriends-release.ipa dist/TildeFriends-$(VERSION_NUMBER).ipa
|
||||
@test $(HAVE_WIN) && echo "[cp] tildefriends-$(VERSION_NUMBER).exe"
|
||||
@test $(HAVE_WIN) && cp out/winrelease/tildefriends.standalone.exe dist/tildefriends-$(VERSION_NUMBER).exe
|
||||
.PHONY: dist
|
||||
|
||||
dist-test: dist
|
||||
|
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
||||
Copyright 2014 Cory McWilliams
|
||||
Copyright 2014-2024 Cory McWilliams
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
37
README.md
37
README.md
@ -4,46 +4,19 @@ Tilde Friends is a tool for making and sharing.
|
||||
|
||||
A public instance lives at https://www.tildefriends.net/.
|
||||
|
||||
It is both a peer-to-peer social network client, participating in Secure
|
||||
Scuttlebutt, as well as a platform for writing and running web applications.
|
||||
It is both a peer-to-peer social network client, participating in Secure Scuttlebutt, as well as a platform for writing and running web applications.
|
||||
|
||||
## Goals
|
||||
|
||||
1. Make it easy and fun to run all sorts of web applications.
|
||||
2. Provide security that is easy to understand and protects your data.
|
||||
3. Make creating and sharing web applications accessible to anyone with a
|
||||
browser.
|
||||
|
||||
## Building
|
||||
|
||||
Builds on Linux (x86_64 and aarch64), MacOS, OpenBSD, and Haiku. Builds for
|
||||
all of those host platforms plus mingw64, iOS, and android.
|
||||
|
||||
1. Requires openssl (`libssl-dev`, in debian-speak). All other dependencies
|
||||
are kept up to date in the tree.
|
||||
2. To build, run `make debug` or `make release`. An executable will be
|
||||
generated in a subdirectory of `out/`.
|
||||
3. It's possible to build for Android, iOS, and Windows on Linux, if you have
|
||||
the right dependencies in the right places. `make windebug winrelease
|
||||
iosdebug-ipa iosrelease-ipa release-apk`.
|
||||
4. To build in docker, `docker build .`.
|
||||
5. `make format` will normalize formatting to the coding standard.
|
||||
|
||||
## Running
|
||||
|
||||
By default, running the built `tildefriends` executable will start a web server
|
||||
at <http://localhost:12345/>. `tildefriends -h` lists further options.
|
||||
|
||||
The first user to create an account and log in will be granted administrative
|
||||
privileges. Further administration can be done at
|
||||
<http://localhost:12345/~core/admin/>.
|
||||
3. Make creating and sharing web applications accessible to anyone with a browser.
|
||||
|
||||
## Documentation
|
||||
|
||||
Docs are a work in progress:
|
||||
<https://www.tildefriends.net/~cory/wiki/#test-wiki/tf-app-quick-reference>.
|
||||
Docs are a work in progress in the `docs` folder, or alternatively in Tilde Friends: <https://www.tildefriends.net/~cory/wiki/#test-wiki/tf-app-quick-reference>.
|
||||
|
||||
## License
|
||||
|
||||
All code unless otherwise noted in is provided under the
|
||||
[MIT](https://opensource.org/licenses/MIT) license.
|
||||
All code, documentation and assets unless otherwise noted in is provided under the
|
||||
[MIT](https://opensource.org/licenses/MIT/) license.
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🎛"
|
||||
"emoji": "🎛",
|
||||
"previous": "&vrpS/vE7n588iYv1p8HafDxHB+YDHTrtUbJiu9nGA9I=.sha256"
|
||||
}
|
||||
|
@ -4,9 +4,37 @@
|
||||
<script>
|
||||
const g_data = $data;
|
||||
</script>
|
||||
<link rel="stylesheet" href="w3.css"></link>
|
||||
<style>
|
||||
/* 2018 Valiant Poppy */
|
||||
.w3-theme-l5 {color:#000 !important; background-color:#fbf3f3 !important}
|
||||
.w3-theme-l4 {color:#000 !important; background-color:#f3d7d6 !important}
|
||||
.w3-theme-l3 {color:#000 !important; background-color:#e6afae !important}
|
||||
.w3-theme-l2 {color:#fff !important; background-color:#da8785 !important}
|
||||
.w3-theme-l1 {color:#fff !important; background-color:#cd5f5d !important}
|
||||
.w3-theme-d1 {color:#fff !important; background-color:#a93634 !important}
|
||||
.w3-theme-d2 {color:#fff !important; background-color:#96302e !important}
|
||||
.w3-theme-d3 {color:#fff !important; background-color:#832a28 !important}
|
||||
.w3-theme-d4 {color:#fff !important; background-color:#702423 !important}
|
||||
.w3-theme-d5 {color:#fff !important; background-color:#5e1e1d !important}
|
||||
|
||||
.w3-theme-light {color:#000 !important; background-color:#fbf3f3 !important}
|
||||
.w3-theme-dark {color:#fff !important; background-color:#5e1e1d !important}
|
||||
.w3-theme-action {color:#fff !important; background-color:#5e1e1d !important}
|
||||
|
||||
.w3-theme {color:#fff !important; background-color:#bd3d3a !important}
|
||||
.w3-text-theme {color:#bd3d3a !important}
|
||||
.w3-border-theme {border-color:#bd3d3a !important}
|
||||
|
||||
.w3-hover-theme:hover {color:#fff !important; background-color:#bd3d3a !important}
|
||||
.w3-hover-text-theme:hover {color:#bd3d3a !important}
|
||||
.w3-hover-border-theme:hover {border-color:#bd3d3a !important}
|
||||
</style>
|
||||
</head>
|
||||
<body style="color: #fff; width: 100%">
|
||||
<h1>Tilde Friends Administration</h1>
|
||||
<body class="w3-theme-l4">
|
||||
<header class="w3-row w3-padding w3-header w3-theme-l1">
|
||||
<h1>Tilde Friends Administration</h1>
|
||||
</header>
|
||||
</body>
|
||||
<script type="module" src="script.js"></script>
|
||||
</html>
|
||||
|
@ -32,59 +32,54 @@ window.addEventListener('load', function () {
|
||||
function input_template(key, description) {
|
||||
if (description.type === 'boolean') {
|
||||
return html`
|
||||
<div style="margin-top: 1em">
|
||||
<label for=${'gs_' + key} style="font-weight: bold">${key}: </label>
|
||||
<div>
|
||||
<input type="checkbox" ?checked=${description.value} id=${'gs_' + key}></input>
|
||||
<button @click=${(e) => global_settings_set(key, e.srcElement.previousElementSibling.checked)}>Set</button>
|
||||
<div>${description.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
<li class="w3-row">
|
||||
<label class="w3-quarter" for=${'gs_' + key} style="font-weight: bold">${key}</label>
|
||||
<div class="w3-quarter w3-padding">${description.description}</div>
|
||||
<input class="w3-quarter w3-check" type="checkbox" ?checked=${description.value} id=${'gs_' + key}></input>
|
||||
<button class="w3-quarter w3-button w3-theme-action" @click=${(e) => global_settings_set(key, e.srcElement.previousElementSibling.checked)}>Set</button>
|
||||
</li>
|
||||
`;
|
||||
} else if (description.type === 'textarea') {
|
||||
return html`
|
||||
<div style="margin-top: 1em"">
|
||||
<label for=${'gs_' + key} style="font-weight: bold">${key}: </label>
|
||||
<div style="width: 100%; padding: 0; margin: 0">
|
||||
<div style="width: 90%; padding: 0 margin: 0">
|
||||
<textarea style="vertical-align: top; width: 100%" rows=20 cols=80 id=${'gs_' + key}>${description.value}</textarea>
|
||||
</div>
|
||||
<button @click=${(e) => global_settings_set(key, e.srcElement.previousElementSibling.firstElementChild.value)}>Set</button>
|
||||
<div>${description.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
<li class="w3-row">
|
||||
<label class="w3-quarter" for=${'gs_' + key} style="font-weight: bold">${key}</label>
|
||||
<div class="w3-rest w3-padding">${description.description}</div>
|
||||
<textarea class="w3-input" style="vertical-align: top; resize: vertical" id=${'gs_' + key}>${description.value}</textarea>
|
||||
<button class="w3-button w3-right w3-quarter w3-theme-action" @click=${(e) => global_settings_set(key, e.srcElement.previousElementSibling.value)}>Set</button>
|
||||
</li>
|
||||
`;
|
||||
} else {
|
||||
return html`
|
||||
<div style="margin-top: 1em">
|
||||
<label for=${'gs_' + key} style="font-weight: bold">${key}: </label>
|
||||
<div>
|
||||
<input type="text" value="${description.value}" id=${'gs_' + key}></input>
|
||||
<button @click=${(e) => global_settings_set(key, e.srcElement.previousElementSibling.value)}>Set</button>
|
||||
<div>${description.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
<li class="w3-row">
|
||||
<label class="w3-quarter" for=${'gs_' + key} style="font-weight: bold">${key}</label>
|
||||
<div class="w3-quarter w3-padding">${description.description}</div>
|
||||
<input class="w3-input w3-quarter" type="text" value="${description.value}" id=${'gs_' + key}></input>
|
||||
<button class="w3-button w3-quarter w3-theme-action" @click=${(e) => global_settings_set(key, e.srcElement.previousElementSibling.value)}>Set</button>
|
||||
</li>
|
||||
`;
|
||||
}
|
||||
}
|
||||
const user_template = (user, permissions) => html`
|
||||
<li>
|
||||
<button @click=${(e) => delete_user(user)}>Delete</button>
|
||||
<li class="w3-card w3-margin">
|
||||
<button class="w3-button w3-theme-action" @click=${(e) => delete_user(user)}>Delete</button>
|
||||
${user}: ${permissions.map((x) => permission_template(x))}
|
||||
</li>
|
||||
`;
|
||||
const users_template = (users) =>
|
||||
html`<h2>Users</h2>
|
||||
<ul>
|
||||
html`
|
||||
<header class="w3-container w3-theme-l2"><h2>Users</h2></header>
|
||||
<ul class="w3-ul">
|
||||
${Object.entries(users).map((u) => user_template(u[0], u[1]))}
|
||||
</ul>`;
|
||||
const page_template = (data) =>
|
||||
html`<div style="padding: 0; margin: 0; width: 100%; max-width: 100%">
|
||||
<h2>Global Settings</h2>
|
||||
<div>
|
||||
${Object.keys(data.settings)
|
||||
.sort()
|
||||
.map((x) => html`${input_template(x, data.settings[x])}`)}
|
||||
<header class="w3-container w3-theme-l2"><h2>Global Settings</h2></header>
|
||||
<div class="w3-container">
|
||||
<ul class="w3-ul">
|
||||
${Object.keys(data.settings)
|
||||
.sort()
|
||||
.map((x) => html`${input_template(x, data.settings[x])}`)}
|
||||
</ul>
|
||||
</div>
|
||||
${users_template(data.users)}
|
||||
</div> `;
|
||||
|
235
apps/admin/w3.css
Normal file
235
apps/admin/w3.css
Normal file
@ -0,0 +1,235 @@
|
||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}summary{display:list-item}
|
||||
audio,canvas,progress,video{display:inline-block}progress{vertical-align:baseline}
|
||||
audio:not([controls]){display:none;height:0}[hidden],template{display:none}
|
||||
a{background-color:transparent}a:active,a:hover{outline-width:0}
|
||||
abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}
|
||||
b,strong{font-weight:bolder}dfn{font-style:italic}mark{background:#ff0;color:#000}
|
||||
small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
|
||||
sub{bottom:-0.25em}sup{top:-0.5em}figure{margin:1em 40px}img{border-style:none}
|
||||
code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}hr{box-sizing:content-box;height:0;overflow:visible}
|
||||
button,input,select,textarea,optgroup{font:inherit;margin:0}optgroup{font-weight:bold}
|
||||
button,input{overflow:visible}button,select{text-transform:none}
|
||||
button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}
|
||||
button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}
|
||||
button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}
|
||||
fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}
|
||||
legend{color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}
|
||||
[type=checkbox],[type=radio]{padding:0}
|
||||
[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}
|
||||
[type=search]{-webkit-appearance:textfield;outline-offset:-2px}
|
||||
[type=search]::-webkit-search-decoration{-webkit-appearance:none}
|
||||
::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}
|
||||
/* End extract */
|
||||
html,body{font-family:Verdana,sans-serif;font-size:15px;line-height:1.5}html{overflow-x:hidden}
|
||||
h1{font-size:36px}h2{font-size:30px}h3{font-size:24px}h4{font-size:20px}h5{font-size:18px}h6{font-size:16px}
|
||||
.w3-serif{font-family:serif}.w3-sans-serif{font-family:sans-serif}.w3-cursive{font-family:cursive}.w3-monospace{font-family:monospace}
|
||||
h1,h2,h3,h4,h5,h6{font-family:"Segoe UI",Arial,sans-serif;font-weight:400;margin:10px 0}.w3-wide{letter-spacing:4px}
|
||||
hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-image{max-width:100%;height:auto}img{vertical-align:middle}a{color:inherit}
|
||||
.w3-table,.w3-table-all{border-collapse:collapse;border-spacing:0;width:100%;display:table}.w3-table-all{border:1px solid #ccc}
|
||||
.w3-bordered tr,.w3-table-all tr{border-bottom:1px solid #ddd}.w3-striped tbody tr:nth-child(even){background-color:#f1f1f1}
|
||||
.w3-table-all tr:nth-child(odd){background-color:#fff}.w3-table-all tr:nth-child(even){background-color:#f1f1f1}
|
||||
.w3-hoverable tbody tr:hover,.w3-ul.w3-hoverable li:hover{background-color:#ccc}.w3-centered tr th,.w3-centered tr td{text-align:center}
|
||||
.w3-table td,.w3-table th,.w3-table-all td,.w3-table-all th{padding:8px 8px;display:table-cell;text-align:left;vertical-align:top}
|
||||
.w3-table th:first-child,.w3-table td:first-child,.w3-table-all th:first-child,.w3-table-all td:first-child{padding-left:16px}
|
||||
.w3-btn,.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap}
|
||||
.w3-btn:hover{box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)}
|
||||
.w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
||||
.w3-disabled,.w3-btn:disabled,.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none}
|
||||
.w3-btn.w3-disabled:hover,.w3-btn:disabled:hover{box-shadow:none}
|
||||
.w3-badge,.w3-tag{background-color:#000;color:#fff;display:inline-block;padding-left:8px;padding-right:8px;text-align:center}.w3-badge{border-radius:50%}
|
||||
.w3-ul{list-style-type:none;padding:0;margin:0}.w3-ul li{padding:8px 16px;border-bottom:1px solid #ddd}.w3-ul li:last-child{border-bottom:none}
|
||||
.w3-tooltip,.w3-display-container{position:relative}.w3-tooltip .w3-text{display:none}.w3-tooltip:hover .w3-text{display:inline-block}
|
||||
.w3-ripple:active{opacity:0.5}.w3-ripple{transition:opacity 0s}
|
||||
.w3-input{padding:8px;display:block;border:none;border-bottom:1px solid #ccc;width:100%}
|
||||
.w3-select{padding:9px 0;width:100%;border:none;border-bottom:1px solid #ccc}
|
||||
.w3-dropdown-click,.w3-dropdown-hover{position:relative;display:inline-block;cursor:pointer}
|
||||
.w3-dropdown-hover:hover .w3-dropdown-content{display:block}
|
||||
.w3-dropdown-hover:first-child,.w3-dropdown-click:hover{background-color:#ccc;color:#000}
|
||||
.w3-dropdown-hover:hover > .w3-button:first-child,.w3-dropdown-click:hover > .w3-button:first-child{background-color:#ccc;color:#000}
|
||||
.w3-dropdown-content{cursor:auto;color:#000;background-color:#fff;display:none;position:absolute;min-width:160px;margin:0;padding:0;z-index:1}
|
||||
.w3-check,.w3-radio{width:24px;height:24px;position:relative;top:6px}
|
||||
.w3-sidebar{height:100%;width:200px;background-color:#fff;position:fixed!important;z-index:1;overflow:auto}
|
||||
.w3-bar-block .w3-dropdown-hover,.w3-bar-block .w3-dropdown-click{width:100%}
|
||||
.w3-bar-block .w3-dropdown-hover .w3-dropdown-content,.w3-bar-block .w3-dropdown-click .w3-dropdown-content{min-width:100%}
|
||||
.w3-bar-block .w3-dropdown-hover .w3-button,.w3-bar-block .w3-dropdown-click .w3-button{width:100%;text-align:left;padding:8px 16px}
|
||||
.w3-main,#main{transition:margin-left .4s}
|
||||
.w3-modal{z-index:3;display:none;padding-top:100px;position:fixed;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:rgb(0,0,0);background-color:rgba(0,0,0,0.4)}
|
||||
.w3-modal-content{margin:auto;background-color:#fff;position:relative;padding:0;outline:0;width:600px}
|
||||
.w3-bar{width:100%;overflow:hidden}.w3-center .w3-bar{display:inline-block;width:auto}
|
||||
.w3-bar .w3-bar-item{padding:8px 16px;float:left;width:auto;border:none;display:block;outline:0}
|
||||
.w3-bar .w3-dropdown-hover,.w3-bar .w3-dropdown-click{position:static;float:left}
|
||||
.w3-bar .w3-button{white-space:normal}
|
||||
.w3-bar-block .w3-bar-item{width:100%;display:block;padding:8px 16px;text-align:left;border:none;white-space:normal;float:none;outline:0}
|
||||
.w3-bar-block.w3-center .w3-bar-item{text-align:center}.w3-block{display:block;width:100%}
|
||||
.w3-responsive{display:block;overflow-x:auto}
|
||||
.w3-container:after,.w3-container:before,.w3-panel:after,.w3-panel:before,.w3-row:after,.w3-row:before,.w3-row-padding:after,.w3-row-padding:before,
|
||||
.w3-cell-row:before,.w3-cell-row:after,.w3-clear:after,.w3-clear:before,.w3-bar:before,.w3-bar:after{content:"";display:table;clear:both}
|
||||
.w3-col,.w3-half,.w3-third,.w3-twothird,.w3-threequarter,.w3-quarter{float:left;width:100%}
|
||||
.w3-col.s1{width:8.33333%}.w3-col.s2{width:16.66666%}.w3-col.s3{width:24.99999%}.w3-col.s4{width:33.33333%}
|
||||
.w3-col.s5{width:41.66666%}.w3-col.s6{width:49.99999%}.w3-col.s7{width:58.33333%}.w3-col.s8{width:66.66666%}
|
||||
.w3-col.s9{width:74.99999%}.w3-col.s10{width:83.33333%}.w3-col.s11{width:91.66666%}.w3-col.s12{width:99.99999%}
|
||||
@media (min-width:601px){.w3-col.m1{width:8.33333%}.w3-col.m2{width:16.66666%}.w3-col.m3,.w3-quarter{width:24.99999%}.w3-col.m4,.w3-third{width:33.33333%}
|
||||
.w3-col.m5{width:41.66666%}.w3-col.m6,.w3-half{width:49.99999%}.w3-col.m7{width:58.33333%}.w3-col.m8,.w3-twothird{width:66.66666%}
|
||||
.w3-col.m9,.w3-threequarter{width:74.99999%}.w3-col.m10{width:83.33333%}.w3-col.m11{width:91.66666%}.w3-col.m12{width:99.99999%}}
|
||||
@media (min-width:993px){.w3-col.l1{width:8.33333%}.w3-col.l2{width:16.66666%}.w3-col.l3{width:24.99999%}.w3-col.l4{width:33.33333%}
|
||||
.w3-col.l5{width:41.66666%}.w3-col.l6{width:49.99999%}.w3-col.l7{width:58.33333%}.w3-col.l8{width:66.66666%}
|
||||
.w3-col.l9{width:74.99999%}.w3-col.l10{width:83.33333%}.w3-col.l11{width:91.66666%}.w3-col.l12{width:99.99999%}}
|
||||
.w3-rest{overflow:hidden}.w3-stretch{margin-left:-16px;margin-right:-16px}
|
||||
.w3-content,.w3-auto{margin-left:auto;margin-right:auto}.w3-content{max-width:980px}.w3-auto{max-width:1140px}
|
||||
.w3-cell-row{display:table;width:100%}.w3-cell{display:table-cell}
|
||||
.w3-cell-top{vertical-align:top}.w3-cell-middle{vertical-align:middle}.w3-cell-bottom{vertical-align:bottom}
|
||||
.w3-hide{display:none!important}.w3-show-block,.w3-show{display:block!important}.w3-show-inline-block{display:inline-block!important}
|
||||
@media (max-width:1205px){.w3-auto{max-width:95%}}
|
||||
@media (max-width:600px){.w3-modal-content{margin:0 10px;width:auto!important}.w3-modal{padding-top:30px}
|
||||
.w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative}
|
||||
.w3-hide-small{display:none!important}.w3-mobile{display:block;width:100%!important}.w3-bar-item.w3-mobile,.w3-dropdown-hover.w3-mobile,.w3-dropdown-click.w3-mobile{text-align:center}
|
||||
.w3-dropdown-hover.w3-mobile,.w3-dropdown-hover.w3-mobile .w3-btn,.w3-dropdown-hover.w3-mobile .w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}}
|
||||
@media (max-width:768px){.w3-modal-content{width:500px}.w3-modal{padding-top:50px}}
|
||||
@media (min-width:993px){.w3-modal-content{width:900px}.w3-hide-large{display:none!important}.w3-sidebar.w3-collapse{display:block!important}}
|
||||
@media (max-width:992px) and (min-width:601px){.w3-hide-medium{display:none!important}}
|
||||
@media (max-width:992px){.w3-sidebar.w3-collapse{display:none}.w3-main{margin-left:0!important;margin-right:0!important}.w3-auto{max-width:100%}}
|
||||
.w3-top,.w3-bottom{position:fixed;width:100%;z-index:1}.w3-top{top:0}.w3-bottom{bottom:0}
|
||||
.w3-overlay{position:fixed;display:none;width:100%;height:100%;top:0;left:0;right:0;bottom:0;background-color:rgba(0,0,0,0.5);z-index:2}
|
||||
.w3-display-topleft{position:absolute;left:0;top:0}.w3-display-topright{position:absolute;right:0;top:0}
|
||||
.w3-display-bottomleft{position:absolute;left:0;bottom:0}.w3-display-bottomright{position:absolute;right:0;bottom:0}
|
||||
.w3-display-middle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%)}
|
||||
.w3-display-left{position:absolute;top:50%;left:0%;transform:translate(0%,-50%);-ms-transform:translate(-0%,-50%)}
|
||||
.w3-display-right{position:absolute;top:50%;right:0%;transform:translate(0%,-50%);-ms-transform:translate(0%,-50%)}
|
||||
.w3-display-topmiddle{position:absolute;left:50%;top:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
|
||||
.w3-display-bottommiddle{position:absolute;left:50%;bottom:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
|
||||
.w3-display-container:hover .w3-display-hover{display:block}.w3-display-container:hover span.w3-display-hover{display:inline-block}.w3-display-hover{display:none}
|
||||
.w3-display-position{position:absolute}
|
||||
.w3-circle{border-radius:50%}
|
||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||
.w3-card,.w3-card-2{box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12)}
|
||||
.w3-card-4,.w3-hover-shadow:hover{box-shadow:0 4px 10px 0 rgba(0,0,0,0.2),0 4px 20px 0 rgba(0,0,0,0.19)}
|
||||
.w3-spin{animation:w3-spin 2s infinite linear}@keyframes w3-spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}
|
||||
.w3-animate-fading{animation:fading 10s infinite}@keyframes fading{0%{opacity:0}50%{opacity:1}100%{opacity:0}}
|
||||
.w3-animate-opacity{animation:opac 0.8s}@keyframes opac{from{opacity:0} to{opacity:1}}
|
||||
.w3-animate-top{position:relative;animation:animatetop 0.4s}@keyframes animatetop{from{top:-300px;opacity:0} to{top:0;opacity:1}}
|
||||
.w3-animate-left{position:relative;animation:animateleft 0.4s}@keyframes animateleft{from{left:-300px;opacity:0} to{left:0;opacity:1}}
|
||||
.w3-animate-right{position:relative;animation:animateright 0.4s}@keyframes animateright{from{right:-300px;opacity:0} to{right:0;opacity:1}}
|
||||
.w3-animate-bottom{position:relative;animation:animatebottom 0.4s}@keyframes animatebottom{from{bottom:-300px;opacity:0} to{bottom:0;opacity:1}}
|
||||
.w3-animate-zoom {animation:animatezoom 0.6s}@keyframes animatezoom{from{transform:scale(0)} to{transform:scale(1)}}
|
||||
.w3-animate-input{transition:width 0.4s ease-in-out}.w3-animate-input:focus{width:100%!important}
|
||||
.w3-opacity,.w3-hover-opacity:hover{opacity:0.60}.w3-opacity-off,.w3-hover-opacity-off:hover{opacity:1}
|
||||
.w3-opacity-max{opacity:0.25}.w3-opacity-min{opacity:0.75}
|
||||
.w3-greyscale-max,.w3-grayscale-max,.w3-hover-greyscale:hover,.w3-hover-grayscale:hover{filter:grayscale(100%)}
|
||||
.w3-greyscale,.w3-grayscale{filter:grayscale(75%)}.w3-greyscale-min,.w3-grayscale-min{filter:grayscale(50%)}
|
||||
.w3-sepia{filter:sepia(75%)}.w3-sepia-max,.w3-hover-sepia:hover{filter:sepia(100%)}.w3-sepia-min{filter:sepia(50%)}
|
||||
.w3-tiny{font-size:10px!important}.w3-small{font-size:12px!important}.w3-medium{font-size:15px!important}.w3-large{font-size:18px!important}
|
||||
.w3-xlarge{font-size:24px!important}.w3-xxlarge{font-size:36px!important}.w3-xxxlarge{font-size:48px!important}.w3-jumbo{font-size:64px!important}
|
||||
.w3-left-align{text-align:left!important}.w3-right-align{text-align:right!important}.w3-justify{text-align:justify!important}.w3-center{text-align:center!important}
|
||||
.w3-border-0{border:0!important}.w3-border{border:1px solid #ccc!important}
|
||||
.w3-border-top{border-top:1px solid #ccc!important}.w3-border-bottom{border-bottom:1px solid #ccc!important}
|
||||
.w3-border-left{border-left:1px solid #ccc!important}.w3-border-right{border-right:1px solid #ccc!important}
|
||||
.w3-topbar{border-top:6px solid #ccc!important}.w3-bottombar{border-bottom:6px solid #ccc!important}
|
||||
.w3-leftbar{border-left:6px solid #ccc!important}.w3-rightbar{border-right:6px solid #ccc!important}
|
||||
.w3-section,.w3-code{margin-top:16px!important;margin-bottom:16px!important}
|
||||
.w3-margin{margin:16px!important}.w3-margin-top{margin-top:16px!important}.w3-margin-bottom{margin-bottom:16px!important}
|
||||
.w3-margin-left{margin-left:16px!important}.w3-margin-right{margin-right:16px!important}
|
||||
.w3-padding-small{padding:4px 8px!important}.w3-padding{padding:8px 16px!important}.w3-padding-large{padding:12px 24px!important}
|
||||
.w3-padding-16{padding-top:16px!important;padding-bottom:16px!important}.w3-padding-24{padding-top:24px!important;padding-bottom:24px!important}
|
||||
.w3-padding-32{padding-top:32px!important;padding-bottom:32px!important}.w3-padding-48{padding-top:48px!important;padding-bottom:48px!important}
|
||||
.w3-padding-64{padding-top:64px!important;padding-bottom:64px!important}
|
||||
.w3-padding-top-64{padding-top:64px!important}.w3-padding-top-48{padding-top:48px!important}
|
||||
.w3-padding-top-32{padding-top:32px!important}.w3-padding-top-24{padding-top:24px!important}
|
||||
.w3-left{float:left!important}.w3-right{float:right!important}
|
||||
.w3-button:hover{color:#000!important;background-color:#ccc!important}
|
||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||
.w3-hover-none:hover{box-shadow:none!important}
|
||||
/* Colors */
|
||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||
.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important}
|
||||
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
||||
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
||||
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
||||
.w3-blue-grey,.w3-hover-blue-grey:hover,.w3-blue-gray,.w3-hover-blue-gray:hover{color:#fff!important;background-color:#607d8b!important}
|
||||
.w3-green,.w3-hover-green:hover{color:#fff!important;background-color:#4CAF50!important}
|
||||
.w3-light-green,.w3-hover-light-green:hover{color:#000!important;background-color:#8bc34a!important}
|
||||
.w3-indigo,.w3-hover-indigo:hover{color:#fff!important;background-color:#3f51b5!important}
|
||||
.w3-khaki,.w3-hover-khaki:hover{color:#000!important;background-color:#f0e68c!important}
|
||||
.w3-lime,.w3-hover-lime:hover{color:#000!important;background-color:#cddc39!important}
|
||||
.w3-orange,.w3-hover-orange:hover{color:#000!important;background-color:#ff9800!important}
|
||||
.w3-deep-orange,.w3-hover-deep-orange:hover{color:#fff!important;background-color:#ff5722!important}
|
||||
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
||||
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
||||
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
||||
.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important}
|
||||
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
||||
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
||||
.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important}
|
||||
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
||||
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||
.w3-pale-blue,.w3-hover-pale-blue:hover{color:#000!important;background-color:#ddffff!important}
|
||||
.w3-text-amber,.w3-hover-text-amber:hover{color:#ffc107!important}
|
||||
.w3-text-aqua,.w3-hover-text-aqua:hover{color:#00ffff!important}
|
||||
.w3-text-blue,.w3-hover-text-blue:hover{color:#2196F3!important}
|
||||
.w3-text-light-blue,.w3-hover-text-light-blue:hover{color:#87CEEB!important}
|
||||
.w3-text-brown,.w3-hover-text-brown:hover{color:#795548!important}
|
||||
.w3-text-cyan,.w3-hover-text-cyan:hover{color:#00bcd4!important}
|
||||
.w3-text-blue-grey,.w3-hover-text-blue-grey:hover,.w3-text-blue-gray,.w3-hover-text-blue-gray:hover{color:#607d8b!important}
|
||||
.w3-text-green,.w3-hover-text-green:hover{color:#4CAF50!important}
|
||||
.w3-text-light-green,.w3-hover-text-light-green:hover{color:#8bc34a!important}
|
||||
.w3-text-indigo,.w3-hover-text-indigo:hover{color:#3f51b5!important}
|
||||
.w3-text-khaki,.w3-hover-text-khaki:hover{color:#b4aa50!important}
|
||||
.w3-text-lime,.w3-hover-text-lime:hover{color:#cddc39!important}
|
||||
.w3-text-orange,.w3-hover-text-orange:hover{color:#ff9800!important}
|
||||
.w3-text-deep-orange,.w3-hover-text-deep-orange:hover{color:#ff5722!important}
|
||||
.w3-text-pink,.w3-hover-text-pink:hover{color:#e91e63!important}
|
||||
.w3-text-purple,.w3-hover-text-purple:hover{color:#9c27b0!important}
|
||||
.w3-text-deep-purple,.w3-hover-text-deep-purple:hover{color:#673ab7!important}
|
||||
.w3-text-red,.w3-hover-text-red:hover{color:#f44336!important}
|
||||
.w3-text-sand,.w3-hover-text-sand:hover{color:#fdf5e6!important}
|
||||
.w3-text-teal,.w3-hover-text-teal:hover{color:#009688!important}
|
||||
.w3-text-yellow,.w3-hover-text-yellow:hover{color:#d2be0e!important}
|
||||
.w3-text-white,.w3-hover-text-white:hover{color:#fff!important}
|
||||
.w3-text-black,.w3-hover-text-black:hover{color:#000!important}
|
||||
.w3-text-grey,.w3-hover-text-grey:hover,.w3-text-gray,.w3-hover-text-gray:hover{color:#757575!important}
|
||||
.w3-text-light-grey,.w3-hover-text-light-grey:hover,.w3-text-light-gray,.w3-hover-text-light-gray:hover{color:#f1f1f1!important}
|
||||
.w3-text-dark-grey,.w3-hover-text-dark-grey:hover,.w3-text-dark-gray,.w3-hover-text-dark-gray:hover{color:#3a3a3a!important}
|
||||
.w3-border-amber,.w3-hover-border-amber:hover{border-color:#ffc107!important}
|
||||
.w3-border-aqua,.w3-hover-border-aqua:hover{border-color:#00ffff!important}
|
||||
.w3-border-blue,.w3-hover-border-blue:hover{border-color:#2196F3!important}
|
||||
.w3-border-light-blue,.w3-hover-border-light-blue:hover{border-color:#87CEEB!important}
|
||||
.w3-border-brown,.w3-hover-border-brown:hover{border-color:#795548!important}
|
||||
.w3-border-cyan,.w3-hover-border-cyan:hover{border-color:#00bcd4!important}
|
||||
.w3-border-blue-grey,.w3-hover-border-blue-grey:hover,.w3-border-blue-gray,.w3-hover-border-blue-gray:hover{border-color:#607d8b!important}
|
||||
.w3-border-green,.w3-hover-border-green:hover{border-color:#4CAF50!important}
|
||||
.w3-border-light-green,.w3-hover-border-light-green:hover{border-color:#8bc34a!important}
|
||||
.w3-border-indigo,.w3-hover-border-indigo:hover{border-color:#3f51b5!important}
|
||||
.w3-border-khaki,.w3-hover-border-khaki:hover{border-color:#f0e68c!important}
|
||||
.w3-border-lime,.w3-hover-border-lime:hover{border-color:#cddc39!important}
|
||||
.w3-border-orange,.w3-hover-border-orange:hover{border-color:#ff9800!important}
|
||||
.w3-border-deep-orange,.w3-hover-border-deep-orange:hover{border-color:#ff5722!important}
|
||||
.w3-border-pink,.w3-hover-border-pink:hover{border-color:#e91e63!important}
|
||||
.w3-border-purple,.w3-hover-border-purple:hover{border-color:#9c27b0!important}
|
||||
.w3-border-deep-purple,.w3-hover-border-deep-purple:hover{border-color:#673ab7!important}
|
||||
.w3-border-red,.w3-hover-border-red:hover{border-color:#f44336!important}
|
||||
.w3-border-sand,.w3-hover-border-sand:hover{border-color:#fdf5e6!important}
|
||||
.w3-border-teal,.w3-hover-border-teal:hover{border-color:#009688!important}
|
||||
.w3-border-yellow,.w3-hover-border-yellow:hover{border-color:#ffeb3b!important}
|
||||
.w3-border-white,.w3-hover-border-white:hover{border-color:#fff!important}
|
||||
.w3-border-black,.w3-hover-border-black:hover{border-color:#000!important}
|
||||
.w3-border-grey,.w3-hover-border-grey:hover,.w3-border-gray,.w3-hover-border-gray:hover{border-color:#9e9e9e!important}
|
||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
4
apps/blog/lit-all.min.js
vendored
4
apps/blog/lit-all.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🪪",
|
||||
"previous": "&kgukkyDk1RxgfzgMH6H/0QeDPIuwPZypLuAFax21ljk=.sha256"
|
||||
"previous": "&de7q4A59auHP/34bXgeNH05JZoxsGr5TjwXPvehWH30=.sha256"
|
||||
}
|
||||
|
@ -19,7 +19,36 @@ tfrpc.register(async function reload() {
|
||||
async function main() {
|
||||
let ids = await ssb.getIdentities();
|
||||
await app.setDocument(
|
||||
`<body style="color: #fff">
|
||||
`
|
||||
<head>
|
||||
<link rel="stylesheet" href="w3.css"></link>
|
||||
<style>
|
||||
/* "2018 Sargasso Sea" */
|
||||
.w3-theme-l5 {color:#000 !important; background-color:#f3f4f7 !important}
|
||||
.w3-theme-l4 {color:#000 !important; background-color:#d7dbe3 !important}
|
||||
.w3-theme-l3 {color:#000 !important; background-color:#b0b6c8 !important}
|
||||
.w3-theme-l2 {color:#fff !important; background-color:#8892ac !important}
|
||||
.w3-theme-l1 {color:#fff !important; background-color:#636f8e !important}
|
||||
.w3-theme-d1 {color:#fff !important; background-color:#40485c !important}
|
||||
.w3-theme-d2 {color:#fff !important; background-color:#394052 !important}
|
||||
.w3-theme-d3 {color:#fff !important; background-color:#323848 !important}
|
||||
.w3-theme-d4 {color:#fff !important; background-color:#2b303d !important}
|
||||
.w3-theme-d5 {color:#fff !important; background-color:#242833 !important}
|
||||
|
||||
.w3-theme-light {color:#000 !important; background-color:#f3f4f7 !important}
|
||||
.w3-theme-dark {color:#fff !important; background-color:#242833 !important}
|
||||
.w3-theme-action {color:#fff !important; background-color:#242833 !important}
|
||||
|
||||
.w3-theme {color:#fff !important; background-color:#485167 !important}
|
||||
.w3-text-theme {color:#485167 !important}
|
||||
.w3-border-theme {border-color:#485167 !important}
|
||||
|
||||
.w3-hover-theme:hover {color:#fff !important; background-color:#485167 !important}
|
||||
.w3-hover-text-theme:hover {color:#485167 !important}
|
||||
.w3-hover-border-theme:hover {border-color:#485167 !important}
|
||||
</style>
|
||||
</head>
|
||||
<body class="w3-theme-l3">
|
||||
<script>const handler = {};</script>
|
||||
<script type="module">
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
@ -27,7 +56,8 @@ async function main() {
|
||||
let id = event.srcElement.dataset.id;
|
||||
let element = document.createElement('textarea');
|
||||
element.value = await tfrpc.rpc.get_private_key(id);
|
||||
element.style = 'width: 100%; read-only: true';
|
||||
element.style = 'width: 100%; height: auto; read-only: true; resize: none';
|
||||
element.classList.add('w3-input');
|
||||
element.readOnly = true;
|
||||
event.srcElement.parentElement.appendChild(element);
|
||||
event.srcElement.onclick = event => handler.hide_id(event, element);
|
||||
@ -69,23 +99,34 @@ async function main() {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<h1>SSB Identity Management</h1>
|
||||
<h2>Create a new identity</h2>
|
||||
<button id="create_id" onclick="handler.create_id()">Create Identity</button>
|
||||
<h2>Import an SSB Identity from 12 BIP39 English Words</h2>
|
||||
<textarea id="add_id" style="width: 100%" rows="4"></textarea><button id="add" onclick="handler.add_id(event)">Import Identity</button>
|
||||
<h2>Identities</h2>
|
||||
<ul>` +
|
||||
ids
|
||||
.map(
|
||||
(id) => `<li>
|
||||
<button onclick="handler.export_id(event)" data-id="${id}">Export Identity</button>
|
||||
<button onclick="handler.delete_id(event)" data-id="${id}">Delete Identity</button>
|
||||
${id}
|
||||
</li>`
|
||||
)
|
||||
.join('\n') +
|
||||
` </ul>
|
||||
<header class="w3-theme w3-padding"><h1>SSB Identity Management</h1></header>
|
||||
<div class="w3-card-4 w3-margin">
|
||||
<header class="w3-container w3-theme-l2"><h2>Create a new identity</h2></header>
|
||||
<footer class="w3-padding">
|
||||
<button id="create_id" onclick="handler.create_id()" class="w3-button w3-theme">Create Identity</button>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="w3-card-4 w3-margin">
|
||||
<header class="w3-container w3-theme-l2"><h2>Import an SSB Identity from 12 BIP39 English Words</h2></header>
|
||||
<textarea id="add_id" style="width: 100%" rows="4" class="w3-input"></textarea>
|
||||
<footer class="w3-padding">
|
||||
<button id="add" onclick="handler.add_id(event)" class="w3-button w3-theme">Import Identity</button>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="w3-card-4 w3-margin">
|
||||
<header class="w3-container w3-theme-l2"><h2>Identities</h2></header>
|
||||
<ul class="w3-ul">` +
|
||||
ids
|
||||
.map(
|
||||
(id) => `<li style="overflow: hidden; text-wrap: nowrap; text-overflow: ellipsis">
|
||||
<button onclick="handler.export_id(event)" data-id="${id}" class="w3-button w3-theme">Export Identity</button>
|
||||
<button onclick="handler.delete_id(event)" data-id="${id}" class="w3-button w3-theme">Delete Identity</button>
|
||||
${id}
|
||||
</li>`
|
||||
)
|
||||
.join('\n') +
|
||||
` </ul>
|
||||
</div>
|
||||
</body>`
|
||||
);
|
||||
}
|
||||
|
235
apps/identity/w3.css
Normal file
235
apps/identity/w3.css
Normal file
@ -0,0 +1,235 @@
|
||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}summary{display:list-item}
|
||||
audio,canvas,progress,video{display:inline-block}progress{vertical-align:baseline}
|
||||
audio:not([controls]){display:none;height:0}[hidden],template{display:none}
|
||||
a{background-color:transparent}a:active,a:hover{outline-width:0}
|
||||
abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}
|
||||
b,strong{font-weight:bolder}dfn{font-style:italic}mark{background:#ff0;color:#000}
|
||||
small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
|
||||
sub{bottom:-0.25em}sup{top:-0.5em}figure{margin:1em 40px}img{border-style:none}
|
||||
code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}hr{box-sizing:content-box;height:0;overflow:visible}
|
||||
button,input,select,textarea,optgroup{font:inherit;margin:0}optgroup{font-weight:bold}
|
||||
button,input{overflow:visible}button,select{text-transform:none}
|
||||
button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}
|
||||
button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}
|
||||
button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}
|
||||
fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}
|
||||
legend{color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}
|
||||
[type=checkbox],[type=radio]{padding:0}
|
||||
[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}
|
||||
[type=search]{-webkit-appearance:textfield;outline-offset:-2px}
|
||||
[type=search]::-webkit-search-decoration{-webkit-appearance:none}
|
||||
::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}
|
||||
/* End extract */
|
||||
html,body{font-family:Verdana,sans-serif;font-size:15px;line-height:1.5}html{overflow-x:hidden}
|
||||
h1{font-size:36px}h2{font-size:30px}h3{font-size:24px}h4{font-size:20px}h5{font-size:18px}h6{font-size:16px}
|
||||
.w3-serif{font-family:serif}.w3-sans-serif{font-family:sans-serif}.w3-cursive{font-family:cursive}.w3-monospace{font-family:monospace}
|
||||
h1,h2,h3,h4,h5,h6{font-family:"Segoe UI",Arial,sans-serif;font-weight:400;margin:10px 0}.w3-wide{letter-spacing:4px}
|
||||
hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-image{max-width:100%;height:auto}img{vertical-align:middle}a{color:inherit}
|
||||
.w3-table,.w3-table-all{border-collapse:collapse;border-spacing:0;width:100%;display:table}.w3-table-all{border:1px solid #ccc}
|
||||
.w3-bordered tr,.w3-table-all tr{border-bottom:1px solid #ddd}.w3-striped tbody tr:nth-child(even){background-color:#f1f1f1}
|
||||
.w3-table-all tr:nth-child(odd){background-color:#fff}.w3-table-all tr:nth-child(even){background-color:#f1f1f1}
|
||||
.w3-hoverable tbody tr:hover,.w3-ul.w3-hoverable li:hover{background-color:#ccc}.w3-centered tr th,.w3-centered tr td{text-align:center}
|
||||
.w3-table td,.w3-table th,.w3-table-all td,.w3-table-all th{padding:8px 8px;display:table-cell;text-align:left;vertical-align:top}
|
||||
.w3-table th:first-child,.w3-table td:first-child,.w3-table-all th:first-child,.w3-table-all td:first-child{padding-left:16px}
|
||||
.w3-btn,.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap}
|
||||
.w3-btn:hover{box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)}
|
||||
.w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
||||
.w3-disabled,.w3-btn:disabled,.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none}
|
||||
.w3-btn.w3-disabled:hover,.w3-btn:disabled:hover{box-shadow:none}
|
||||
.w3-badge,.w3-tag{background-color:#000;color:#fff;display:inline-block;padding-left:8px;padding-right:8px;text-align:center}.w3-badge{border-radius:50%}
|
||||
.w3-ul{list-style-type:none;padding:0;margin:0}.w3-ul li{padding:8px 16px;border-bottom:1px solid #ddd}.w3-ul li:last-child{border-bottom:none}
|
||||
.w3-tooltip,.w3-display-container{position:relative}.w3-tooltip .w3-text{display:none}.w3-tooltip:hover .w3-text{display:inline-block}
|
||||
.w3-ripple:active{opacity:0.5}.w3-ripple{transition:opacity 0s}
|
||||
.w3-input{padding:8px;display:block;border:none;border-bottom:1px solid #ccc;width:100%}
|
||||
.w3-select{padding:9px 0;width:100%;border:none;border-bottom:1px solid #ccc}
|
||||
.w3-dropdown-click,.w3-dropdown-hover{position:relative;display:inline-block;cursor:pointer}
|
||||
.w3-dropdown-hover:hover .w3-dropdown-content{display:block}
|
||||
.w3-dropdown-hover:first-child,.w3-dropdown-click:hover{background-color:#ccc;color:#000}
|
||||
.w3-dropdown-hover:hover > .w3-button:first-child,.w3-dropdown-click:hover > .w3-button:first-child{background-color:#ccc;color:#000}
|
||||
.w3-dropdown-content{cursor:auto;color:#000;background-color:#fff;display:none;position:absolute;min-width:160px;margin:0;padding:0;z-index:1}
|
||||
.w3-check,.w3-radio{width:24px;height:24px;position:relative;top:6px}
|
||||
.w3-sidebar{height:100%;width:200px;background-color:#fff;position:fixed!important;z-index:1;overflow:auto}
|
||||
.w3-bar-block .w3-dropdown-hover,.w3-bar-block .w3-dropdown-click{width:100%}
|
||||
.w3-bar-block .w3-dropdown-hover .w3-dropdown-content,.w3-bar-block .w3-dropdown-click .w3-dropdown-content{min-width:100%}
|
||||
.w3-bar-block .w3-dropdown-hover .w3-button,.w3-bar-block .w3-dropdown-click .w3-button{width:100%;text-align:left;padding:8px 16px}
|
||||
.w3-main,#main{transition:margin-left .4s}
|
||||
.w3-modal{z-index:3;display:none;padding-top:100px;position:fixed;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:rgb(0,0,0);background-color:rgba(0,0,0,0.4)}
|
||||
.w3-modal-content{margin:auto;background-color:#fff;position:relative;padding:0;outline:0;width:600px}
|
||||
.w3-bar{width:100%;overflow:hidden}.w3-center .w3-bar{display:inline-block;width:auto}
|
||||
.w3-bar .w3-bar-item{padding:8px 16px;float:left;width:auto;border:none;display:block;outline:0}
|
||||
.w3-bar .w3-dropdown-hover,.w3-bar .w3-dropdown-click{position:static;float:left}
|
||||
.w3-bar .w3-button{white-space:normal}
|
||||
.w3-bar-block .w3-bar-item{width:100%;display:block;padding:8px 16px;text-align:left;border:none;white-space:normal;float:none;outline:0}
|
||||
.w3-bar-block.w3-center .w3-bar-item{text-align:center}.w3-block{display:block;width:100%}
|
||||
.w3-responsive{display:block;overflow-x:auto}
|
||||
.w3-container:after,.w3-container:before,.w3-panel:after,.w3-panel:before,.w3-row:after,.w3-row:before,.w3-row-padding:after,.w3-row-padding:before,
|
||||
.w3-cell-row:before,.w3-cell-row:after,.w3-clear:after,.w3-clear:before,.w3-bar:before,.w3-bar:after{content:"";display:table;clear:both}
|
||||
.w3-col,.w3-half,.w3-third,.w3-twothird,.w3-threequarter,.w3-quarter{float:left;width:100%}
|
||||
.w3-col.s1{width:8.33333%}.w3-col.s2{width:16.66666%}.w3-col.s3{width:24.99999%}.w3-col.s4{width:33.33333%}
|
||||
.w3-col.s5{width:41.66666%}.w3-col.s6{width:49.99999%}.w3-col.s7{width:58.33333%}.w3-col.s8{width:66.66666%}
|
||||
.w3-col.s9{width:74.99999%}.w3-col.s10{width:83.33333%}.w3-col.s11{width:91.66666%}.w3-col.s12{width:99.99999%}
|
||||
@media (min-width:601px){.w3-col.m1{width:8.33333%}.w3-col.m2{width:16.66666%}.w3-col.m3,.w3-quarter{width:24.99999%}.w3-col.m4,.w3-third{width:33.33333%}
|
||||
.w3-col.m5{width:41.66666%}.w3-col.m6,.w3-half{width:49.99999%}.w3-col.m7{width:58.33333%}.w3-col.m8,.w3-twothird{width:66.66666%}
|
||||
.w3-col.m9,.w3-threequarter{width:74.99999%}.w3-col.m10{width:83.33333%}.w3-col.m11{width:91.66666%}.w3-col.m12{width:99.99999%}}
|
||||
@media (min-width:993px){.w3-col.l1{width:8.33333%}.w3-col.l2{width:16.66666%}.w3-col.l3{width:24.99999%}.w3-col.l4{width:33.33333%}
|
||||
.w3-col.l5{width:41.66666%}.w3-col.l6{width:49.99999%}.w3-col.l7{width:58.33333%}.w3-col.l8{width:66.66666%}
|
||||
.w3-col.l9{width:74.99999%}.w3-col.l10{width:83.33333%}.w3-col.l11{width:91.66666%}.w3-col.l12{width:99.99999%}}
|
||||
.w3-rest{overflow:hidden}.w3-stretch{margin-left:-16px;margin-right:-16px}
|
||||
.w3-content,.w3-auto{margin-left:auto;margin-right:auto}.w3-content{max-width:980px}.w3-auto{max-width:1140px}
|
||||
.w3-cell-row{display:table;width:100%}.w3-cell{display:table-cell}
|
||||
.w3-cell-top{vertical-align:top}.w3-cell-middle{vertical-align:middle}.w3-cell-bottom{vertical-align:bottom}
|
||||
.w3-hide{display:none!important}.w3-show-block,.w3-show{display:block!important}.w3-show-inline-block{display:inline-block!important}
|
||||
@media (max-width:1205px){.w3-auto{max-width:95%}}
|
||||
@media (max-width:600px){.w3-modal-content{margin:0 10px;width:auto!important}.w3-modal{padding-top:30px}
|
||||
.w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative}
|
||||
.w3-hide-small{display:none!important}.w3-mobile{display:block;width:100%!important}.w3-bar-item.w3-mobile,.w3-dropdown-hover.w3-mobile,.w3-dropdown-click.w3-mobile{text-align:center}
|
||||
.w3-dropdown-hover.w3-mobile,.w3-dropdown-hover.w3-mobile .w3-btn,.w3-dropdown-hover.w3-mobile .w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}}
|
||||
@media (max-width:768px){.w3-modal-content{width:500px}.w3-modal{padding-top:50px}}
|
||||
@media (min-width:993px){.w3-modal-content{width:900px}.w3-hide-large{display:none!important}.w3-sidebar.w3-collapse{display:block!important}}
|
||||
@media (max-width:992px) and (min-width:601px){.w3-hide-medium{display:none!important}}
|
||||
@media (max-width:992px){.w3-sidebar.w3-collapse{display:none}.w3-main{margin-left:0!important;margin-right:0!important}.w3-auto{max-width:100%}}
|
||||
.w3-top,.w3-bottom{position:fixed;width:100%;z-index:1}.w3-top{top:0}.w3-bottom{bottom:0}
|
||||
.w3-overlay{position:fixed;display:none;width:100%;height:100%;top:0;left:0;right:0;bottom:0;background-color:rgba(0,0,0,0.5);z-index:2}
|
||||
.w3-display-topleft{position:absolute;left:0;top:0}.w3-display-topright{position:absolute;right:0;top:0}
|
||||
.w3-display-bottomleft{position:absolute;left:0;bottom:0}.w3-display-bottomright{position:absolute;right:0;bottom:0}
|
||||
.w3-display-middle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%)}
|
||||
.w3-display-left{position:absolute;top:50%;left:0%;transform:translate(0%,-50%);-ms-transform:translate(-0%,-50%)}
|
||||
.w3-display-right{position:absolute;top:50%;right:0%;transform:translate(0%,-50%);-ms-transform:translate(0%,-50%)}
|
||||
.w3-display-topmiddle{position:absolute;left:50%;top:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
|
||||
.w3-display-bottommiddle{position:absolute;left:50%;bottom:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
|
||||
.w3-display-container:hover .w3-display-hover{display:block}.w3-display-container:hover span.w3-display-hover{display:inline-block}.w3-display-hover{display:none}
|
||||
.w3-display-position{position:absolute}
|
||||
.w3-circle{border-radius:50%}
|
||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||
.w3-card,.w3-card-2{box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12)}
|
||||
.w3-card-4,.w3-hover-shadow:hover{box-shadow:0 4px 10px 0 rgba(0,0,0,0.2),0 4px 20px 0 rgba(0,0,0,0.19)}
|
||||
.w3-spin{animation:w3-spin 2s infinite linear}@keyframes w3-spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}
|
||||
.w3-animate-fading{animation:fading 10s infinite}@keyframes fading{0%{opacity:0}50%{opacity:1}100%{opacity:0}}
|
||||
.w3-animate-opacity{animation:opac 0.8s}@keyframes opac{from{opacity:0} to{opacity:1}}
|
||||
.w3-animate-top{position:relative;animation:animatetop 0.4s}@keyframes animatetop{from{top:-300px;opacity:0} to{top:0;opacity:1}}
|
||||
.w3-animate-left{position:relative;animation:animateleft 0.4s}@keyframes animateleft{from{left:-300px;opacity:0} to{left:0;opacity:1}}
|
||||
.w3-animate-right{position:relative;animation:animateright 0.4s}@keyframes animateright{from{right:-300px;opacity:0} to{right:0;opacity:1}}
|
||||
.w3-animate-bottom{position:relative;animation:animatebottom 0.4s}@keyframes animatebottom{from{bottom:-300px;opacity:0} to{bottom:0;opacity:1}}
|
||||
.w3-animate-zoom {animation:animatezoom 0.6s}@keyframes animatezoom{from{transform:scale(0)} to{transform:scale(1)}}
|
||||
.w3-animate-input{transition:width 0.4s ease-in-out}.w3-animate-input:focus{width:100%!important}
|
||||
.w3-opacity,.w3-hover-opacity:hover{opacity:0.60}.w3-opacity-off,.w3-hover-opacity-off:hover{opacity:1}
|
||||
.w3-opacity-max{opacity:0.25}.w3-opacity-min{opacity:0.75}
|
||||
.w3-greyscale-max,.w3-grayscale-max,.w3-hover-greyscale:hover,.w3-hover-grayscale:hover{filter:grayscale(100%)}
|
||||
.w3-greyscale,.w3-grayscale{filter:grayscale(75%)}.w3-greyscale-min,.w3-grayscale-min{filter:grayscale(50%)}
|
||||
.w3-sepia{filter:sepia(75%)}.w3-sepia-max,.w3-hover-sepia:hover{filter:sepia(100%)}.w3-sepia-min{filter:sepia(50%)}
|
||||
.w3-tiny{font-size:10px!important}.w3-small{font-size:12px!important}.w3-medium{font-size:15px!important}.w3-large{font-size:18px!important}
|
||||
.w3-xlarge{font-size:24px!important}.w3-xxlarge{font-size:36px!important}.w3-xxxlarge{font-size:48px!important}.w3-jumbo{font-size:64px!important}
|
||||
.w3-left-align{text-align:left!important}.w3-right-align{text-align:right!important}.w3-justify{text-align:justify!important}.w3-center{text-align:center!important}
|
||||
.w3-border-0{border:0!important}.w3-border{border:1px solid #ccc!important}
|
||||
.w3-border-top{border-top:1px solid #ccc!important}.w3-border-bottom{border-bottom:1px solid #ccc!important}
|
||||
.w3-border-left{border-left:1px solid #ccc!important}.w3-border-right{border-right:1px solid #ccc!important}
|
||||
.w3-topbar{border-top:6px solid #ccc!important}.w3-bottombar{border-bottom:6px solid #ccc!important}
|
||||
.w3-leftbar{border-left:6px solid #ccc!important}.w3-rightbar{border-right:6px solid #ccc!important}
|
||||
.w3-section,.w3-code{margin-top:16px!important;margin-bottom:16px!important}
|
||||
.w3-margin{margin:16px!important}.w3-margin-top{margin-top:16px!important}.w3-margin-bottom{margin-bottom:16px!important}
|
||||
.w3-margin-left{margin-left:16px!important}.w3-margin-right{margin-right:16px!important}
|
||||
.w3-padding-small{padding:4px 8px!important}.w3-padding{padding:8px 16px!important}.w3-padding-large{padding:12px 24px!important}
|
||||
.w3-padding-16{padding-top:16px!important;padding-bottom:16px!important}.w3-padding-24{padding-top:24px!important;padding-bottom:24px!important}
|
||||
.w3-padding-32{padding-top:32px!important;padding-bottom:32px!important}.w3-padding-48{padding-top:48px!important;padding-bottom:48px!important}
|
||||
.w3-padding-64{padding-top:64px!important;padding-bottom:64px!important}
|
||||
.w3-padding-top-64{padding-top:64px!important}.w3-padding-top-48{padding-top:48px!important}
|
||||
.w3-padding-top-32{padding-top:32px!important}.w3-padding-top-24{padding-top:24px!important}
|
||||
.w3-left{float:left!important}.w3-right{float:right!important}
|
||||
.w3-button:hover{color:#000!important;background-color:#ccc!important}
|
||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||
.w3-hover-none:hover{box-shadow:none!important}
|
||||
/* Colors */
|
||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||
.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important}
|
||||
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
||||
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
||||
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
||||
.w3-blue-grey,.w3-hover-blue-grey:hover,.w3-blue-gray,.w3-hover-blue-gray:hover{color:#fff!important;background-color:#607d8b!important}
|
||||
.w3-green,.w3-hover-green:hover{color:#fff!important;background-color:#4CAF50!important}
|
||||
.w3-light-green,.w3-hover-light-green:hover{color:#000!important;background-color:#8bc34a!important}
|
||||
.w3-indigo,.w3-hover-indigo:hover{color:#fff!important;background-color:#3f51b5!important}
|
||||
.w3-khaki,.w3-hover-khaki:hover{color:#000!important;background-color:#f0e68c!important}
|
||||
.w3-lime,.w3-hover-lime:hover{color:#000!important;background-color:#cddc39!important}
|
||||
.w3-orange,.w3-hover-orange:hover{color:#000!important;background-color:#ff9800!important}
|
||||
.w3-deep-orange,.w3-hover-deep-orange:hover{color:#fff!important;background-color:#ff5722!important}
|
||||
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
||||
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
||||
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
||||
.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important}
|
||||
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
||||
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
||||
.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important}
|
||||
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
||||
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||
.w3-pale-blue,.w3-hover-pale-blue:hover{color:#000!important;background-color:#ddffff!important}
|
||||
.w3-text-amber,.w3-hover-text-amber:hover{color:#ffc107!important}
|
||||
.w3-text-aqua,.w3-hover-text-aqua:hover{color:#00ffff!important}
|
||||
.w3-text-blue,.w3-hover-text-blue:hover{color:#2196F3!important}
|
||||
.w3-text-light-blue,.w3-hover-text-light-blue:hover{color:#87CEEB!important}
|
||||
.w3-text-brown,.w3-hover-text-brown:hover{color:#795548!important}
|
||||
.w3-text-cyan,.w3-hover-text-cyan:hover{color:#00bcd4!important}
|
||||
.w3-text-blue-grey,.w3-hover-text-blue-grey:hover,.w3-text-blue-gray,.w3-hover-text-blue-gray:hover{color:#607d8b!important}
|
||||
.w3-text-green,.w3-hover-text-green:hover{color:#4CAF50!important}
|
||||
.w3-text-light-green,.w3-hover-text-light-green:hover{color:#8bc34a!important}
|
||||
.w3-text-indigo,.w3-hover-text-indigo:hover{color:#3f51b5!important}
|
||||
.w3-text-khaki,.w3-hover-text-khaki:hover{color:#b4aa50!important}
|
||||
.w3-text-lime,.w3-hover-text-lime:hover{color:#cddc39!important}
|
||||
.w3-text-orange,.w3-hover-text-orange:hover{color:#ff9800!important}
|
||||
.w3-text-deep-orange,.w3-hover-text-deep-orange:hover{color:#ff5722!important}
|
||||
.w3-text-pink,.w3-hover-text-pink:hover{color:#e91e63!important}
|
||||
.w3-text-purple,.w3-hover-text-purple:hover{color:#9c27b0!important}
|
||||
.w3-text-deep-purple,.w3-hover-text-deep-purple:hover{color:#673ab7!important}
|
||||
.w3-text-red,.w3-hover-text-red:hover{color:#f44336!important}
|
||||
.w3-text-sand,.w3-hover-text-sand:hover{color:#fdf5e6!important}
|
||||
.w3-text-teal,.w3-hover-text-teal:hover{color:#009688!important}
|
||||
.w3-text-yellow,.w3-hover-text-yellow:hover{color:#d2be0e!important}
|
||||
.w3-text-white,.w3-hover-text-white:hover{color:#fff!important}
|
||||
.w3-text-black,.w3-hover-text-black:hover{color:#000!important}
|
||||
.w3-text-grey,.w3-hover-text-grey:hover,.w3-text-gray,.w3-hover-text-gray:hover{color:#757575!important}
|
||||
.w3-text-light-grey,.w3-hover-text-light-grey:hover,.w3-text-light-gray,.w3-hover-text-light-gray:hover{color:#f1f1f1!important}
|
||||
.w3-text-dark-grey,.w3-hover-text-dark-grey:hover,.w3-text-dark-gray,.w3-hover-text-dark-gray:hover{color:#3a3a3a!important}
|
||||
.w3-border-amber,.w3-hover-border-amber:hover{border-color:#ffc107!important}
|
||||
.w3-border-aqua,.w3-hover-border-aqua:hover{border-color:#00ffff!important}
|
||||
.w3-border-blue,.w3-hover-border-blue:hover{border-color:#2196F3!important}
|
||||
.w3-border-light-blue,.w3-hover-border-light-blue:hover{border-color:#87CEEB!important}
|
||||
.w3-border-brown,.w3-hover-border-brown:hover{border-color:#795548!important}
|
||||
.w3-border-cyan,.w3-hover-border-cyan:hover{border-color:#00bcd4!important}
|
||||
.w3-border-blue-grey,.w3-hover-border-blue-grey:hover,.w3-border-blue-gray,.w3-hover-border-blue-gray:hover{border-color:#607d8b!important}
|
||||
.w3-border-green,.w3-hover-border-green:hover{border-color:#4CAF50!important}
|
||||
.w3-border-light-green,.w3-hover-border-light-green:hover{border-color:#8bc34a!important}
|
||||
.w3-border-indigo,.w3-hover-border-indigo:hover{border-color:#3f51b5!important}
|
||||
.w3-border-khaki,.w3-hover-border-khaki:hover{border-color:#f0e68c!important}
|
||||
.w3-border-lime,.w3-hover-border-lime:hover{border-color:#cddc39!important}
|
||||
.w3-border-orange,.w3-hover-border-orange:hover{border-color:#ff9800!important}
|
||||
.w3-border-deep-orange,.w3-hover-border-deep-orange:hover{border-color:#ff5722!important}
|
||||
.w3-border-pink,.w3-hover-border-pink:hover{border-color:#e91e63!important}
|
||||
.w3-border-purple,.w3-hover-border-purple:hover{border-color:#9c27b0!important}
|
||||
.w3-border-deep-purple,.w3-hover-border-deep-purple:hover{border-color:#673ab7!important}
|
||||
.w3-border-red,.w3-hover-border-red:hover{border-color:#f44336!important}
|
||||
.w3-border-sand,.w3-hover-border-sand:hover{border-color:#fdf5e6!important}
|
||||
.w3-border-teal,.w3-hover-border-teal:hover{border-color:#009688!important}
|
||||
.w3-border-yellow,.w3-hover-border-yellow:hover{border-color:#ffeb3b!important}
|
||||
.w3-border-white,.w3-hover-border-white:hover{border-color:#fff!important}
|
||||
.w3-border-black,.w3-hover-border-black:hover{border-color:#000!important}
|
||||
.w3-border-grey,.w3-hover-border-grey:hover,.w3-border-gray,.w3-hover-border-gray:hover{border-color:#9e9e9e!important}
|
||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🦟",
|
||||
"previous": "&TegdzvFE+im94shygaHkgDYSaSrwY2h0OKUXSRPBQDM=.sha256"
|
||||
"previous": "&cUqvSDUls3jn0haD85LPFAGdkc8wFuy347TtATNcJgg=.sha256"
|
||||
}
|
||||
|
@ -85,6 +85,9 @@ tfrpc.register(async function store_message(message) {
|
||||
tfrpc.register(function apps() {
|
||||
return core.apps();
|
||||
});
|
||||
tfrpc.register(function getActiveIdentity() {
|
||||
return ssb.getActiveIdentity();
|
||||
});
|
||||
tfrpc.register(async function try_decrypt(id, content) {
|
||||
return await ssb.privateMessageDecrypt(id, content);
|
||||
});
|
||||
|
4
apps/issues/lit-all.min.js
vendored
4
apps/issues/lit-all.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -4,48 +4,6 @@ import * as tfutils from './tf-utils.js';
|
||||
|
||||
const k_project = '%Hr+4xEVtjplidSKBlRWi4Aw/0Tfw7B+1OR9BzlDKmOI=.sha256';
|
||||
|
||||
class TfIdPickerElement extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
ids: {type: Array},
|
||||
selected: {type: String},
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.load();
|
||||
}
|
||||
|
||||
async load() {
|
||||
this.selected = await tfrpc.rpc.localStorageGet('whoami');
|
||||
this.ids = (await tfrpc.rpc.getIdentities()) || [];
|
||||
}
|
||||
|
||||
changed(event) {
|
||||
this.selected = event.srcElement.value;
|
||||
tfrpc.rpc.localStorageSet('whoami', this.selected);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.ids) {
|
||||
return html`
|
||||
<select @change=${this.changed} style="max-width: 100%">
|
||||
${this.ids.map(
|
||||
(id) =>
|
||||
html`<option ?selected=${id == this.selected} value=${id}>
|
||||
${id}
|
||||
</option>`
|
||||
)}
|
||||
</select>
|
||||
`;
|
||||
} else {
|
||||
return html`<div>Loading...</div>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
customElements.define('tf-id-picker', TfIdPickerElement);
|
||||
|
||||
class TfComposeElement extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
@ -105,10 +63,10 @@ class TfIssuesAppElement extends LitElement {
|
||||
let issues = {};
|
||||
let messages = await tfrpc.rpc.query(
|
||||
`
|
||||
WITH issues AS (SELECT messages.* FROM messages_refs JOIN messages ON
|
||||
WITH issues AS (SELECT messages.id, json(messages.content) AS content, messages.author, messages.timestamp FROM messages_refs JOIN messages ON
|
||||
messages.id = messages_refs.message
|
||||
WHERE messages_refs.ref = ? AND json_extract(messages.content, '$.type') = 'issue'),
|
||||
edits AS (SELECT messages.* FROM issues JOIN messages_refs ON
|
||||
edits AS (SELECT messages.id, json(messages.content) AS content, messages.author, messages.timestamp FROM issues JOIN messages_refs ON
|
||||
issues.id = messages_refs.ref JOIN messages ON
|
||||
messages.id = messages_refs.message
|
||||
WHERE json_extract(messages.content, '$.type') IN ('issue-edit', 'post'))
|
||||
@ -206,7 +164,7 @@ class TfIssuesAppElement extends LitElement {
|
||||
if (
|
||||
confirm(`Are you sure you want to ${open ? 'open' : 'close'} this issue?`)
|
||||
) {
|
||||
let whoami = this.shadowRoot.getElementById('picker').selected;
|
||||
let whoami = await tfrpc.rpc.getActiveIdentity();
|
||||
await tfrpc.rpc.appendMessage(whoami, {
|
||||
type: 'issue-edit',
|
||||
issues: [
|
||||
@ -221,7 +179,7 @@ class TfIssuesAppElement extends LitElement {
|
||||
}
|
||||
|
||||
async create_issue(event) {
|
||||
let whoami = this.shadowRoot.getElementById('picker').selected;
|
||||
let whoami = await tfrpc.rpc.getActiveIdentity();
|
||||
await tfrpc.rpc.appendMessage(whoami, {
|
||||
type: 'issue',
|
||||
project: k_project,
|
||||
@ -231,7 +189,7 @@ class TfIssuesAppElement extends LitElement {
|
||||
}
|
||||
|
||||
async reply_to_issue(event) {
|
||||
let whoami = this.shadowRoot.getElementById('picker').selected;
|
||||
let whoami = await tfrpc.rpc.getActiveIdentity();
|
||||
await tfrpc.rpc.appendMessage(whoami, {
|
||||
type: 'post',
|
||||
text: event.detail.value,
|
||||
@ -249,10 +207,7 @@ class TfIssuesAppElement extends LitElement {
|
||||
}
|
||||
|
||||
render() {
|
||||
let header = html`
|
||||
<h1>Tilde Friends Issues</h1>
|
||||
<tf-id-picker id="picker"></tf-id-picker>
|
||||
`;
|
||||
let header = html` <h1>Tilde Friends Issues</h1> `;
|
||||
if (this.selected) {
|
||||
return html`
|
||||
${header}
|
||||
|
4
apps/journal/lit-all.min.js
vendored
4
apps/journal/lit-all.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "📦",
|
||||
"previous": "&IU+TwyM7TznD8NBfnw7tgW2zxVlMqTVxSqWFjuosLwo=.sha256"
|
||||
"emoji": "🚪",
|
||||
"previous": "&HXCdDG8gGYXElTyEFbg85jqa6lDXNL2ENPIA9UoJNbI=.sha256"
|
||||
}
|
||||
|
4
apps/sneaker/lit-all.min.js
vendored
4
apps/sneaker/lit-all.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🐌",
|
||||
"previous": "&Xs1X5TzLCk6KVr+5IDc80JAHYxJyoD10cXKBUYpFqWQ=.sha256"
|
||||
"previous": "&vEaOZjrNb0u9rhNqrQ8eU9TlOFlo4HsgW6hbI7VdIT0=.sha256"
|
||||
}
|
||||
|
@ -100,6 +100,9 @@ tfrpc.register(async function try_decrypt(id, content) {
|
||||
tfrpc.register(async function encrypt(id, recipients, content) {
|
||||
return await ssb.privateMessageEncrypt(id, recipients, content);
|
||||
});
|
||||
tfrpc.register(async function getActiveIdentity() {
|
||||
return await ssb.getActiveIdentity();
|
||||
});
|
||||
ssb.addEventListener('broadcasts', async function () {
|
||||
await tfrpc.rpc.set('broadcasts', await ssb.getBroadcasts());
|
||||
});
|
||||
@ -107,6 +110,9 @@ ssb.addEventListener('broadcasts', async function () {
|
||||
core.register('onConnectionsChanged', async function () {
|
||||
await tfrpc.rpc.set('connections', await ssb.connections());
|
||||
});
|
||||
core.register('setActiveIdentity', async function (id) {
|
||||
await tfrpc.rpc.set('identity', id);
|
||||
});
|
||||
|
||||
async function main() {
|
||||
if (typeof database !== 'undefined') {
|
||||
|
@ -1,90 +1,94 @@
|
||||
function textNode(text) {
|
||||
const node = new commonmark.Node("text", undefined);
|
||||
node.literal = text;
|
||||
return node;
|
||||
const node = new commonmark.Node('text', undefined);
|
||||
node.literal = text;
|
||||
return node;
|
||||
}
|
||||
|
||||
function linkNode(text, link) {
|
||||
const linkNode = new commonmark.Node("link", undefined);
|
||||
linkNode.destination = `#q=${encodeURIComponent(link)}`;
|
||||
linkNode.appendChild(textNode(text));
|
||||
return linkNode;
|
||||
const linkNode = new commonmark.Node('link', undefined);
|
||||
if (link.startsWith('#')) {
|
||||
linkNode.destination = `#q=${encodeURIComponent(link)}`;
|
||||
} else {
|
||||
linkNode.destination = link;
|
||||
}
|
||||
linkNode.appendChild(textNode(text));
|
||||
return linkNode;
|
||||
}
|
||||
|
||||
function splitMatches(text, regexp) {
|
||||
// Regexp must be sticky.
|
||||
regexp = new RegExp(regexp, "gm");
|
||||
// Regexp must be sticky.
|
||||
regexp = new RegExp(regexp, 'gm');
|
||||
|
||||
let i = 0;
|
||||
const result = [];
|
||||
let i = 0;
|
||||
const result = [];
|
||||
|
||||
let match = regexp.exec(text);
|
||||
while (match) {
|
||||
const matchText = match[0];
|
||||
let match = regexp.exec(text);
|
||||
while (match) {
|
||||
const matchText = match[0];
|
||||
|
||||
if (match.index > i) {
|
||||
result.push([text.substring(i, match.index), false]);
|
||||
}
|
||||
if (match.index > i) {
|
||||
result.push([text.substring(i, match.index), false]);
|
||||
}
|
||||
|
||||
result.push([matchText, true]);
|
||||
i = match.index + matchText.length;
|
||||
result.push([matchText, true]);
|
||||
i = match.index + matchText.length;
|
||||
|
||||
match = regexp.exec(text);
|
||||
}
|
||||
match = regexp.exec(text);
|
||||
}
|
||||
|
||||
if (i < text.length) {
|
||||
result.push([text.substring(i, text.length), false]);
|
||||
}
|
||||
if (i < text.length) {
|
||||
result.push([text.substring(i, text.length), false]);
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
const regex = new RegExp("(?<!\\w)#[\\w-]+");
|
||||
const regex = new RegExp('(?:https?://[^ ]+[^ .,])|(?:(?<!\\w)#[\\w-]+)|(?:@[A-Za-z0-9+/]+=.ed25519)|(?:[%&][A-Za-z0-9+/]+=.sha256)');
|
||||
|
||||
function split(textNodes) {
|
||||
const text = textNodes.map(n => n.literal).join("");
|
||||
const parts = splitMatches(text, regex);
|
||||
const text = textNodes.map((n) => n.literal).join('');
|
||||
const parts = splitMatches(text, regex);
|
||||
|
||||
return parts.map(part => {
|
||||
if (part[1]) {
|
||||
return linkNode(part[0], part[0]);
|
||||
} else {
|
||||
return textNode(part[0]);
|
||||
}
|
||||
});
|
||||
return parts.map((part) => {
|
||||
if (part[1]) {
|
||||
return linkNode(part[0], part[0]);
|
||||
} else {
|
||||
return textNode(part[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function transform(parsed) {
|
||||
const walker = parsed.walker();
|
||||
let event;
|
||||
const walker = parsed.walker();
|
||||
let event;
|
||||
|
||||
let nodes = [];
|
||||
while ((event = walker.next())) {
|
||||
const node = event.node;
|
||||
if (event.entering && node.type === "text") {
|
||||
nodes.push(node);
|
||||
} else {
|
||||
if (nodes.length > 0) {
|
||||
split(nodes)
|
||||
.reverse()
|
||||
.forEach(newNode => {
|
||||
nodes[0].insertAfter(newNode);
|
||||
});
|
||||
let nodes = [];
|
||||
while ((event = walker.next())) {
|
||||
const node = event.node;
|
||||
if (event.entering && node.type === 'text') {
|
||||
nodes.push(node);
|
||||
} else {
|
||||
if (nodes.length > 0) {
|
||||
split(nodes)
|
||||
.reverse()
|
||||
.forEach((newNode) => {
|
||||
nodes[0].insertAfter(newNode);
|
||||
});
|
||||
|
||||
nodes.forEach(n => n.unlink());
|
||||
nodes = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
nodes.forEach((n) => n.unlink());
|
||||
nodes = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nodes.length > 0) {
|
||||
split(nodes)
|
||||
.reverse()
|
||||
.forEach(newNode => {
|
||||
nodes[0].insertAfter(newNode);
|
||||
});
|
||||
nodes.forEach(n => n.unlink());
|
||||
}
|
||||
if (nodes.length > 0) {
|
||||
split(nodes)
|
||||
.reverse()
|
||||
.forEach((newNode) => {
|
||||
nodes[0].insertAfter(newNode);
|
||||
});
|
||||
nodes.forEach((n) => n.unlink());
|
||||
}
|
||||
|
||||
return parsed;
|
||||
return parsed;
|
||||
}
|
||||
|
@ -1,91 +0,0 @@
|
||||
function textNode(text) {
|
||||
const node = new commonmark.Node("text", undefined);
|
||||
node.literal = text;
|
||||
return node;
|
||||
}
|
||||
|
||||
function linkNode(text, url) {
|
||||
const urlNode = new commonmark.Node("link", undefined);
|
||||
urlNode.destination = url;
|
||||
urlNode.appendChild(textNode(text));
|
||||
|
||||
return urlNode;
|
||||
}
|
||||
|
||||
function splitMatches(text, regexp) {
|
||||
// Regexp must be sticky.
|
||||
regexp = new RegExp(regexp, "gm");
|
||||
|
||||
let i = 0;
|
||||
const result = [];
|
||||
|
||||
let match = regexp.exec(text);
|
||||
while (match) {
|
||||
const matchText = match[0];
|
||||
|
||||
if (match.index > i) {
|
||||
result.push([text.substring(i, match.index), false]);
|
||||
}
|
||||
|
||||
result.push([matchText, true]);
|
||||
i = match.index + matchText.length;
|
||||
|
||||
match = regexp.exec(text);
|
||||
}
|
||||
|
||||
if (i < text.length) {
|
||||
result.push([text.substring(i, text.length), false]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const urlRegexp = new RegExp("https?://[^ ]+[^ .,]");
|
||||
|
||||
function splitURLs(textNodes) {
|
||||
const text = textNodes.map(n => n.literal).join("");
|
||||
const parts = splitMatches(text, urlRegexp);
|
||||
|
||||
return parts.map(part => {
|
||||
if (part[1]) {
|
||||
return linkNode(part[0], part[0]);
|
||||
} else {
|
||||
return textNode(part[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function transform(parsed) {
|
||||
const walker = parsed.walker();
|
||||
let event;
|
||||
|
||||
let nodes = [];
|
||||
while ((event = walker.next())) {
|
||||
const node = event.node;
|
||||
if (event.entering && node.type === "text") {
|
||||
nodes.push(node);
|
||||
} else {
|
||||
if (nodes.length > 0) {
|
||||
splitURLs(nodes)
|
||||
.reverse()
|
||||
.forEach(newNode => {
|
||||
nodes[0].insertAfter(newNode);
|
||||
});
|
||||
|
||||
nodes.forEach(n => n.unlink());
|
||||
nodes = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nodes.length > 0) {
|
||||
splitURLs(nodes)
|
||||
.reverse()
|
||||
.forEach(newNode => {
|
||||
nodes[0].insertAfter(newNode);
|
||||
});
|
||||
nodes.forEach(n => n.unlink());
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
|
||||
let g_emojis;
|
||||
|
||||
function get_emojis() {
|
||||
@ -10,105 +12,154 @@ function get_emojis() {
|
||||
});
|
||||
}
|
||||
|
||||
export function picker(callback, anchor) {
|
||||
get_emojis().then(function (json) {
|
||||
let div = document.createElement('div');
|
||||
div.id = 'emoji_picker';
|
||||
div.style.color = '#000';
|
||||
div.style.background = '#fff';
|
||||
div.style.border = '1px solid #000';
|
||||
div.style.display = 'block';
|
||||
div.style.position = 'absolute';
|
||||
div.style.minWidth = 'min(16em, 90vw)';
|
||||
div.style.width = 'min(16em, 90vw)';
|
||||
div.style.maxWidth = 'min(16em, 90vw)';
|
||||
div.style.maxHeight = '16em';
|
||||
div.style.overflow = 'scroll';
|
||||
div.style.fontWeight = 'bold';
|
||||
div.style.fontSize = 'xx-large';
|
||||
let input = document.createElement('input');
|
||||
input.type = 'text';
|
||||
input.style.display = 'block';
|
||||
input.style.boxSizing = 'border-box';
|
||||
input.style.width = '100%';
|
||||
input.style.margin = '0';
|
||||
input.style.position = 'relative';
|
||||
div.appendChild(input);
|
||||
let list = document.createElement('div');
|
||||
div.appendChild(list);
|
||||
div.addEventListener('mousedown', function (event) {
|
||||
event.stopPropagation();
|
||||
});
|
||||
async function get_recent(author) {
|
||||
let recent = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT DISTINCT content ->> '$.vote.expression' AS value
|
||||
FROM messages
|
||||
WHERE author = ? AND
|
||||
content ->> '$.type' = 'vote'
|
||||
ORDER BY timestamp DESC LIMIT 10
|
||||
`,
|
||||
[author]
|
||||
);
|
||||
return recent.map((x) => x.value);
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
console.log('emoji cleanup');
|
||||
div.parentElement.removeChild(div);
|
||||
window.removeEventListener('keydown', key_down);
|
||||
console.log('removing click');
|
||||
document.body.removeEventListener('mousedown', cleanup);
|
||||
}
|
||||
export async function picker(callback, anchor, author) {
|
||||
let json = await get_emojis();
|
||||
let recent = await get_recent(author);
|
||||
|
||||
function key_down(event) {
|
||||
if (event.key == 'Escape') {
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
let div = document.createElement('div');
|
||||
div.id = 'emoji_picker';
|
||||
div.style.color = '#000';
|
||||
div.style.background = '#fff';
|
||||
div.style.border = '1px solid #000';
|
||||
div.style.display = 'block';
|
||||
div.style.position = 'absolute';
|
||||
div.style.minWidth = 'min(16em, 90vw)';
|
||||
div.style.width = 'min(16em, 90vw)';
|
||||
div.style.maxWidth = 'min(16em, 90vw)';
|
||||
div.style.maxHeight = '16em';
|
||||
div.style.overflow = 'scroll';
|
||||
div.style.fontWeight = 'bold';
|
||||
div.style.fontSize = 'xx-large';
|
||||
let input = document.createElement('input');
|
||||
input.type = 'text';
|
||||
input.style.display = 'block';
|
||||
input.style.boxSizing = 'border-box';
|
||||
input.style.width = '100%';
|
||||
input.style.margin = '0';
|
||||
input.style.position = 'relative';
|
||||
div.appendChild(input);
|
||||
let list = document.createElement('div');
|
||||
div.appendChild(list);
|
||||
div.addEventListener('mousedown', function (event) {
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
function chosen(event) {
|
||||
console.log(event.srcElement.innerText);
|
||||
callback(event.srcElement.innerText);
|
||||
function cleanup() {
|
||||
console.log('emoji cleanup');
|
||||
div.parentElement.removeChild(div);
|
||||
window.removeEventListener('keydown', key_down);
|
||||
console.log('removing click');
|
||||
document.body.removeEventListener('mousedown', cleanup);
|
||||
}
|
||||
|
||||
function key_down(event) {
|
||||
if (event.key == 'Escape') {
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
while (list.firstChild) {
|
||||
list.removeChild(list.firstChild);
|
||||
}
|
||||
let search = input.value.toLowerCase();
|
||||
let any_at_all = false;
|
||||
for (let row of Object.entries(json)) {
|
||||
let header = document.createElement('div');
|
||||
header.appendChild(document.createTextNode(row[0]));
|
||||
list.appendChild(header);
|
||||
let any = false;
|
||||
for (let entry of Object.entries(row[1])) {
|
||||
if (
|
||||
search &&
|
||||
search.length &&
|
||||
entry[0].toLowerCase().indexOf(search) == -1
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
let emoji = document.createElement('span');
|
||||
const k_size = '1.25em';
|
||||
emoji.style.display = 'inline-block';
|
||||
emoji.style.overflow = 'hidden';
|
||||
emoji.style.cursor = 'pointer';
|
||||
emoji.onclick = chosen;
|
||||
emoji.title = entry[0];
|
||||
emoji.appendChild(document.createTextNode(entry[1]));
|
||||
list.appendChild(emoji);
|
||||
any = true;
|
||||
any_at_all = true;
|
||||
}
|
||||
if (!any) {
|
||||
list.removeChild(header);
|
||||
function chosen(event) {
|
||||
console.log(event.srcElement.innerText);
|
||||
callback(event.srcElement.innerText);
|
||||
cleanup();
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
while (list.firstChild) {
|
||||
list.removeChild(list.firstChild);
|
||||
}
|
||||
let search = input.value.toLowerCase();
|
||||
let any_at_all = false;
|
||||
if (recent) {
|
||||
let emoji_to_name = {};
|
||||
for (let row of Object.values(json)) {
|
||||
for (let entry of Object.entries(row)) {
|
||||
emoji_to_name[entry[1]] = entry[0];
|
||||
}
|
||||
}
|
||||
if (!any_at_all) {
|
||||
list.appendChild(document.createTextNode('No matches found.'));
|
||||
let header = document.createElement('div');
|
||||
header.appendChild(document.createTextNode('Recent'));
|
||||
list.appendChild(header);
|
||||
let any = false;
|
||||
for (let entry of recent) {
|
||||
if (
|
||||
search &&
|
||||
search.length &&
|
||||
(emoji_to_name[entry] || '').toLowerCase().indexOf(search) == -1
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
let emoji = document.createElement('span');
|
||||
const k_size = '1.25em';
|
||||
emoji.style.display = 'inline-block';
|
||||
emoji.style.overflow = 'hidden';
|
||||
emoji.style.cursor = 'pointer';
|
||||
emoji.onclick = chosen;
|
||||
emoji.title = emoji_to_name[entry] || entry;
|
||||
emoji.appendChild(document.createTextNode(entry));
|
||||
list.appendChild(emoji);
|
||||
any = true;
|
||||
}
|
||||
if (!any) {
|
||||
list.removeChild(header);
|
||||
}
|
||||
}
|
||||
refresh();
|
||||
input.oninput = refresh;
|
||||
document.body.appendChild(div);
|
||||
div.style.position = 'fixed';
|
||||
div.style.top = '50%';
|
||||
div.style.left = '50%';
|
||||
div.style.transform = 'translate(-50%, -50%)';
|
||||
input.focus();
|
||||
console.log('adding click');
|
||||
document.body.addEventListener('mousedown', cleanup);
|
||||
window.addEventListener('keydown', key_down);
|
||||
});
|
||||
for (let row of Object.entries(json)) {
|
||||
let header = document.createElement('div');
|
||||
header.appendChild(document.createTextNode(row[0]));
|
||||
list.appendChild(header);
|
||||
let any = false;
|
||||
for (let entry of Object.entries(row[1])) {
|
||||
if (
|
||||
search &&
|
||||
search.length &&
|
||||
entry[0].toLowerCase().indexOf(search) == -1
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
let emoji = document.createElement('span');
|
||||
const k_size = '1.25em';
|
||||
emoji.style.display = 'inline-block';
|
||||
emoji.style.overflow = 'hidden';
|
||||
emoji.style.cursor = 'pointer';
|
||||
emoji.onclick = chosen;
|
||||
emoji.title = entry[0];
|
||||
emoji.appendChild(document.createTextNode(entry[1]));
|
||||
list.appendChild(emoji);
|
||||
any = true;
|
||||
any_at_all = true;
|
||||
}
|
||||
if (!any) {
|
||||
list.removeChild(header);
|
||||
}
|
||||
}
|
||||
if (!any_at_all) {
|
||||
list.appendChild(document.createTextNode('No matches found.'));
|
||||
}
|
||||
}
|
||||
refresh();
|
||||
input.oninput = refresh;
|
||||
document.body.appendChild(div);
|
||||
div.style.position = 'fixed';
|
||||
div.style.top = '50%';
|
||||
div.style.left = '50%';
|
||||
div.style.transform = 'translate(-50%, -50%)';
|
||||
input.focus();
|
||||
console.log('adding click');
|
||||
document.body.addEventListener('mousedown', cleanup);
|
||||
window.addEventListener('keydown', key_down);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!doctype html>
|
||||
<html style="color: #fff">
|
||||
<html>
|
||||
<head>
|
||||
<title>Tilde Friends</title>
|
||||
<base target="_top" />
|
||||
@ -10,14 +10,14 @@
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="background-color: #223a5e">
|
||||
<tf-app class="w3-deep-purple" />
|
||||
<body style="margin: 0; padding: 0">
|
||||
<tf-app></tf-app>
|
||||
<tf-reactions-modal id="reactions_modal"></tf-reactions-modal>
|
||||
<script>
|
||||
window.litDisableBundleWarning = true;
|
||||
</script>
|
||||
<script src="filesaver.min.js"></script>
|
||||
<script src="commonmark.min.js"></script>
|
||||
<script src="commonmark-linkify.js" type="module"></script>
|
||||
<script src="commonmark-hashtag.js" type="module"></script>
|
||||
<script src="script.js" type="module"></script>
|
||||
</body>
|
||||
|
4
apps/ssb/lit-all.min.js
vendored
4
apps/ssb/lit-all.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,13 +1,13 @@
|
||||
import {LitElement, html} from './lit-all.min.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
|
||||
import * as tf_id_picker from './tf-id-picker.js';
|
||||
import * as tf_app from './tf-app.js';
|
||||
import * as tf_message from './tf-message.js';
|
||||
import * as tf_user from './tf-user.js';
|
||||
import * as tf_compose from './tf-compose.js';
|
||||
import * as tf_news from './tf-news.js';
|
||||
import * as tf_profile from './tf-profile.js';
|
||||
import * as tf_reactions_modal from './tf-reactions-modal.js';
|
||||
import * as tf_tab_mentions from './tf-tab-mentions.js';
|
||||
import * as tf_tab_news from './tf-tab-news.js';
|
||||
import * as tf_tab_news_feed from './tf-tab-news-feed.js';
|
||||
|
@ -52,13 +52,15 @@ class TfElement extends LitElement {
|
||||
self.broadcasts = value;
|
||||
} else if (name === 'connections') {
|
||||
self.connections = value;
|
||||
} else if (name === 'identity') {
|
||||
self.whoami = value;
|
||||
}
|
||||
});
|
||||
this.initial_load();
|
||||
}
|
||||
|
||||
async initial_load() {
|
||||
let whoami = await tfrpc.rpc.localStorageGet('whoami');
|
||||
let whoami = await tfrpc.rpc.getActiveIdentity();
|
||||
let ids = (await tfrpc.rpc.getIdentities()) || [];
|
||||
this.whoami = whoami ?? (ids.length ? ids[0] : undefined);
|
||||
this.ids = ids;
|
||||
@ -193,29 +195,6 @@ class TfElement extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
render_id_picker() {
|
||||
return html`
|
||||
<div style="display: flex; gap: 8px">
|
||||
<tf-id-picker
|
||||
id="picker"
|
||||
style="flex: 1 1 auto"
|
||||
selected=${this.whoami}
|
||||
.ids=${this.ids}
|
||||
.users=${this.users}
|
||||
@change=${this._handle_whoami_changed}
|
||||
></tf-id-picker>
|
||||
<button
|
||||
class="w3-button w3-dark-grey w3-border"
|
||||
style="flex: 0 0 auto"
|
||||
@click=${this.create_identity}
|
||||
id="create_identity"
|
||||
>
|
||||
Create Identity
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
async load_recent_tags() {
|
||||
let start = new Date();
|
||||
this.tags = await tfrpc.rpc.query(
|
||||
@ -255,7 +234,15 @@ class TfElement extends LitElement {
|
||||
by_count.push({count: v.of, id: id});
|
||||
}
|
||||
console.log(by_count.sort((x, y) => y.count - x.count).slice(0, 20));
|
||||
let start_time = new Date();
|
||||
users = await this.fetch_about(Object.keys(following).sort(), users);
|
||||
console.log(
|
||||
'about took',
|
||||
(new Date() - start_time) / 1000.0,
|
||||
'seconds for',
|
||||
Object.keys(users).length,
|
||||
'users'
|
||||
);
|
||||
this.following = Object.keys(following);
|
||||
this.users = users;
|
||||
await tags;
|
||||
@ -352,18 +339,26 @@ class TfElement extends LitElement {
|
||||
};
|
||||
|
||||
let tabs = html`
|
||||
<div class="w3-bar w3-black">
|
||||
<style>
|
||||
@media only screen and (max-width: 650px) {
|
||||
.hide-on-small-screens {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div class="w3-bar w3-theme-l1">
|
||||
${Object.entries(k_tabs).map(
|
||||
([k, v]) => html`
|
||||
<button
|
||||
title=${v}
|
||||
class="w3-bar-item w3-padding-large w3-hover-gray tab ${self.tab ==
|
||||
class="w3-bar-item w3-padding-large w3-hover-theme tab ${self.tab ==
|
||||
v
|
||||
? 'w3-red'
|
||||
: 'w3-black'}"
|
||||
? 'w3-theme-l2'
|
||||
: 'w3-theme-l1'}"
|
||||
@click=${() => self.set_tab(v)}
|
||||
>
|
||||
${k}
|
||||
<span class="hide-on-small-screens">${v}</span>
|
||||
</button>
|
||||
`
|
||||
)}
|
||||
@ -371,15 +366,25 @@ class TfElement extends LitElement {
|
||||
`;
|
||||
let contents = !this.loaded
|
||||
? this.loading
|
||||
? html`<div>Loading...</div>`
|
||||
? html`<div class="w3-panel w3-theme-l5 w3-card-4 w3-padding-large w3-round-xlarge">
|
||||
Loading...
|
||||
</div>
|
||||
${this.render_tab()}`
|
||||
: html`<div>Select or create an identity.</div>`
|
||||
: this.render_tab();
|
||||
return html`
|
||||
${this.render_id_picker()} ${tabs}
|
||||
${this.tags.map(
|
||||
(x) => html`<tf-tag tag=${x.tag} count=${x.count}></tf-tag>`
|
||||
)}
|
||||
${contents}
|
||||
<div
|
||||
style="width: 100vw; min-height: 100vh; height: 100%"
|
||||
class="w3-theme-dark"
|
||||
>
|
||||
${tabs}
|
||||
<div style="padding: 8px">
|
||||
${this.tags.map(
|
||||
(x) => html`<tf-tag tag=${x.tag} count=${x.count}></tf-tag>`
|
||||
)}
|
||||
${contents}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
|
||||
import {LitElement, html, unsafeHTML, live} from './lit-all.min.js';
|
||||
import * as tfutils from './tf-utils.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
import {styles} from './tf-styles.js';
|
||||
@ -13,6 +13,7 @@ class TfComposeElement extends LitElement {
|
||||
branch: {type: String},
|
||||
apps: {type: Object},
|
||||
drafts: {type: Object},
|
||||
author: {type: String},
|
||||
};
|
||||
}
|
||||
|
||||
@ -25,6 +26,7 @@ class TfComposeElement extends LitElement {
|
||||
this.branch = undefined;
|
||||
this.apps = undefined;
|
||||
this.drafts = {};
|
||||
this.author = undefined;
|
||||
}
|
||||
|
||||
process_text(text) {
|
||||
@ -64,7 +66,7 @@ class TfComposeElement extends LitElement {
|
||||
updated = true;
|
||||
}
|
||||
if (updated) {
|
||||
this.requestUpdate();
|
||||
setTimeout(() => this.notify(draft), 0);
|
||||
}
|
||||
return tfutils.markdown(text);
|
||||
}
|
||||
@ -72,7 +74,7 @@ class TfComposeElement extends LitElement {
|
||||
input(event) {
|
||||
let edit = this.renderRoot.getElementById('edit');
|
||||
let preview = this.renderRoot.getElementById('preview');
|
||||
preview.innerHTML = this.process_text(edit.value);
|
||||
preview.innerHTML = this.process_text(edit.innerText);
|
||||
let content_warning = this.renderRoot.getElementById('content_warning');
|
||||
let content_warning_preview = this.renderRoot.getElementById(
|
||||
'content_warning_preview'
|
||||
@ -80,6 +82,10 @@ class TfComposeElement extends LitElement {
|
||||
if (content_warning && content_warning_preview) {
|
||||
content_warning_preview.innerText = content_warning.value;
|
||||
}
|
||||
let draft = this.get_draft();
|
||||
draft.text = edit.innerText;
|
||||
draft.content_warning = content_warning?.innerText;
|
||||
setTimeout(() => this.notify(draft), 0);
|
||||
}
|
||||
|
||||
notify(draft) {
|
||||
@ -95,14 +101,6 @@ class TfComposeElement extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
change() {
|
||||
let draft = this.get_draft();
|
||||
draft.text = this.renderRoot.getElementById('edit')?.value;
|
||||
draft.content_warning =
|
||||
this.renderRoot.getElementById('content_warning')?.value;
|
||||
this.notify(draft);
|
||||
}
|
||||
|
||||
convert_to_format(buffer, type, mime_type) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
let img = new Image();
|
||||
@ -169,8 +167,7 @@ class TfComposeElement extends LitElement {
|
||||
size: buffer.length ?? buffer.byteLength,
|
||||
};
|
||||
let edit = self.renderRoot.getElementById('edit');
|
||||
edit.value += `\n`;
|
||||
self.change();
|
||||
edit.innerText += `\n`;
|
||||
self.input();
|
||||
} catch (e) {
|
||||
alert(e?.message);
|
||||
@ -197,7 +194,7 @@ class TfComposeElement extends LitElement {
|
||||
let edit = this.renderRoot.getElementById('edit');
|
||||
let message = {
|
||||
type: 'post',
|
||||
text: edit.value,
|
||||
text: edit.innerText,
|
||||
};
|
||||
if (this.root || this.branch) {
|
||||
message.root = this.root;
|
||||
@ -225,8 +222,8 @@ class TfComposeElement extends LitElement {
|
||||
}
|
||||
try {
|
||||
await tfrpc.rpc.appendMessage(this.whoami, message).then(function () {
|
||||
edit.value = '';
|
||||
self.change();
|
||||
edit.innerText = '';
|
||||
self.input();
|
||||
self.notify(undefined);
|
||||
self.requestUpdate();
|
||||
});
|
||||
@ -236,17 +233,11 @@ class TfComposeElement extends LitElement {
|
||||
}
|
||||
|
||||
discard() {
|
||||
let edit = this.renderRoot.getElementById('edit');
|
||||
edit.value = '';
|
||||
this.change();
|
||||
let preview = this.renderRoot.getElementById('preview');
|
||||
preview.innerHTML = '';
|
||||
this.notify(undefined);
|
||||
}
|
||||
|
||||
attach() {
|
||||
let self = this;
|
||||
let edit = this.renderRoot.getElementById('edit');
|
||||
let input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.onchange = function (event) {
|
||||
@ -284,22 +275,34 @@ class TfComposeElement extends LitElement {
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
let values = Object.entries(this.users).map((x) => ({
|
||||
key: x[1].name ?? x[0],
|
||||
value: x[0],
|
||||
}));
|
||||
if (this.author) {
|
||||
values = [].concat(
|
||||
[
|
||||
{
|
||||
key: this.users[this.author]?.name,
|
||||
value: this.author,
|
||||
},
|
||||
],
|
||||
values
|
||||
);
|
||||
}
|
||||
let tribute = new Tribute({
|
||||
collection: [
|
||||
{
|
||||
values: Object.entries(this.users).map((x) => ({
|
||||
key: x[1].name,
|
||||
value: x[0],
|
||||
})),
|
||||
values: values,
|
||||
selectTemplate: function (item) {
|
||||
return `[@${item.original.key}](${item.original.value})`;
|
||||
return item ? `[@${item.original.key}](${item.original.value})` : undefined;
|
||||
},
|
||||
},
|
||||
{
|
||||
trigger: '&',
|
||||
values: this.autocomplete,
|
||||
selectTemplate: function (item) {
|
||||
return ``;
|
||||
return item ? `` : undefined;
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -310,10 +313,10 @@ class TfComposeElement extends LitElement {
|
||||
updated() {
|
||||
super.updated();
|
||||
let edit = this.renderRoot.getElementById('edit');
|
||||
if (this.last_updated_text !== edit.value) {
|
||||
if (this.last_updated_text !== edit.innerText) {
|
||||
let preview = this.renderRoot.getElementById('preview');
|
||||
preview.innerHTML = this.process_text(edit.value);
|
||||
this.last_updated_text = edit.value;
|
||||
preview.innerHTML = this.process_text(edit.innerText);
|
||||
this.last_updated_text = edit.innerText;
|
||||
}
|
||||
let encrypt = this.renderRoot.getElementById('encrypt_to');
|
||||
if (encrypt) {
|
||||
@ -333,8 +336,7 @@ class TfComposeElement extends LitElement {
|
||||
remove_mention(id) {
|
||||
let draft = this.get_draft();
|
||||
delete draft.mentions[id];
|
||||
this.notify(draft);
|
||||
this.requestUpdate();
|
||||
setTimeout(() => this.notify(), 0);
|
||||
}
|
||||
|
||||
render_mention(mention) {
|
||||
@ -342,7 +344,7 @@ class TfComposeElement extends LitElement {
|
||||
return html` <div style="display: flex; flex-direction: row">
|
||||
<div style="align-self: center; margin: 0.5em">
|
||||
<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-button w3-theme-d1"
|
||||
title="Remove ${mention.name} mention"
|
||||
@click=${() => self.remove_mention(mention.link)}
|
||||
>
|
||||
@ -396,16 +398,16 @@ class TfComposeElement extends LitElement {
|
||||
if (this.apps) {
|
||||
return html`
|
||||
<div class="w3-card-4 w3-margin w3-padding">
|
||||
<select id="select" class="w3-select w3-dark-grey">
|
||||
<select id="select" class="w3-select w3-theme-d1">
|
||||
${Object.keys(self.apps).map(
|
||||
(app) => html`<option value=${app}>${app}</option>`
|
||||
)}
|
||||
</select>
|
||||
<button class="w3-button w3-dark-grey" @click=${attach_selected_app}>
|
||||
<button class="w3-button w3-theme-d1" @click=${attach_selected_app}>
|
||||
Attach
|
||||
</button>
|
||||
<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => (this.apps = null)}
|
||||
>
|
||||
Cancel
|
||||
@ -421,12 +423,12 @@ class TfComposeElement extends LitElement {
|
||||
self.apps = await tfrpc.rpc.apps();
|
||||
}
|
||||
if (!this.apps) {
|
||||
return html`<button class="w3-button w3-dark-grey" @click=${attach_app}>
|
||||
return html`<button class="w3-button w3-theme-d1" @click=${attach_app}>
|
||||
Attach App
|
||||
</button>`;
|
||||
} else {
|
||||
return html`<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => (this.apps = null)}
|
||||
>
|
||||
Discard App
|
||||
@ -448,15 +450,15 @@ class TfComposeElement extends LitElement {
|
||||
return html`
|
||||
<div class="w3-container w3-padding">
|
||||
<p>
|
||||
<input type="checkbox" class="w3-check w3-dark-grey" id="cw" @change=${() => self.set_content_warning(undefined)} checked="checked"></input>
|
||||
<input type="checkbox" class="w3-check w3-theme-d1" id="cw" @change=${() => self.set_content_warning(undefined)} checked="checked"></input>
|
||||
<label for="cw">CW</label>
|
||||
</p>
|
||||
<input type="text" class="w3-input w3-border w3-dark-grey" id="content_warning" placeholder="Enter a content warning here." @input=${this.input} @change=${this.change} value=${draft.content_warning}></input>
|
||||
<input type="text" class="w3-input w3-border w3-theme-d1" id="content_warning" placeholder="Enter a content warning here." @input=${this.input} @change=${this.change} value=${draft.content_warning}></input>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
return html`
|
||||
<input type="checkbox" class="w3-check w3-dark-grey" id="cw" @change=${() => self.set_content_warning('')}></input>
|
||||
<input type="checkbox" class="w3-check w3-theme-d1" id="cw" @change=${() => self.set_content_warning('')}></input>
|
||||
<label for="cw">CW</label>
|
||||
`;
|
||||
}
|
||||
@ -486,14 +488,14 @@ class TfComposeElement extends LitElement {
|
||||
<div style="display: flex; flex-direction: row; width: 100%">
|
||||
<label for="encrypt_to">🔐 To:</label>
|
||||
<input type="text" id="encrypt_to" style="display: flex; flex: 1 1" @input=${this.update_encrypt}></input>
|
||||
<button class="w3-button w3-dark-grey" @click=${() => this.set_encrypt(undefined)}>🚮</button>
|
||||
<button class="w3-button w3-theme-d1" @click=${() => this.set_encrypt(undefined)}>🚮</button>
|
||||
</div>
|
||||
<ul>
|
||||
${draft.encrypt_to.map(
|
||||
(x) => html`
|
||||
<li>
|
||||
<tf-user id=${x} .users=${this.users}></tf-user>
|
||||
<input type="button" class="w3-button w3-dark-grey" value="🚮" @click=${() => this.set_encrypt(draft.encrypt_to.filter((id) => id != x))}></input>
|
||||
<input type="button" class="w3-button w3-theme-d1" value="🚮" @click=${() => this.set_encrypt(draft.encrypt_to.filter((id) => id != x))}></input>
|
||||
</li>`
|
||||
)}
|
||||
</ul>
|
||||
@ -512,7 +514,7 @@ class TfComposeElement extends LitElement {
|
||||
let draft = self.get_draft();
|
||||
let content_warning =
|
||||
draft.content_warning !== undefined
|
||||
? html`<div class="w3-panel w3-round-xlarge w3-blue">
|
||||
? html`<div class="w3-panel w3-round-xlarge w3-theme-d2">
|
||||
<p id="content_warning_preview">${draft.content_warning}</p>
|
||||
</div>`
|
||||
: undefined;
|
||||
@ -520,34 +522,31 @@ class TfComposeElement extends LitElement {
|
||||
draft.encrypt_to !== undefined
|
||||
? undefined
|
||||
: html`<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => this.set_encrypt([])}
|
||||
>
|
||||
🔐
|
||||
</button>`;
|
||||
let result = html`
|
||||
<div
|
||||
class="w3-card-4 w3-blue-grey w3-padding"
|
||||
class="w3-card-4 w3-theme-d4 w3-padding-small"
|
||||
style="box-sizing: border-box"
|
||||
>
|
||||
${this.render_encrypt()}
|
||||
<div style="display: flex; flex-direction: row; width: 100%; gap: 4px">
|
||||
<div style="flex: 1 0 50%">
|
||||
<p>
|
||||
<textarea
|
||||
class="w3-input w3-dark-grey w3-border"
|
||||
style="resize: vertical"
|
||||
placeholder="Write a post here."
|
||||
id="edit"
|
||||
@input=${this.input}
|
||||
@change=${this.change}
|
||||
@paste=${this.paste}
|
||||
>
|
||||
${draft.text}</textarea
|
||||
>
|
||||
</p>
|
||||
<div class="w3-container w3-padding-small">
|
||||
<div class="w3-half">
|
||||
<span
|
||||
class="w3-input w3-theme-d1 w3-border"
|
||||
style="resize: vertical; width: 100%; overflow: hidden; white-space: pre-wrap"
|
||||
placeholder="Write a post here."
|
||||
id="edit"
|
||||
@input=${this.input}
|
||||
@paste=${this.paste}
|
||||
contenteditable
|
||||
.innerText=${live(draft.text ?? '')}
|
||||
></span>
|
||||
</div>
|
||||
<div style="flex: 1 0 50%">
|
||||
<div class="w3-half w3-padding">
|
||||
${content_warning}
|
||||
<div id="preview"></div>
|
||||
</div>
|
||||
@ -556,18 +555,14 @@ ${draft.text}</textarea
|
||||
self.render_mention(x)
|
||||
)}
|
||||
${this.render_attach_app()} ${this.render_content_warning()}
|
||||
<button
|
||||
class="w3-button w3-dark-grey"
|
||||
id="submit"
|
||||
@click=${this.submit}
|
||||
>
|
||||
<button class="w3-button w3-theme-d1" id="submit" @click=${this.submit}>
|
||||
Submit
|
||||
</button>
|
||||
<button class="w3-button w3-dark-grey" @click=${this.attach}>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.attach}>
|
||||
Attach
|
||||
</button>
|
||||
${this.render_attach_app_button()} ${encrypt}
|
||||
<button class="w3-button w3-dark-grey" @click=${this.discard}>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.discard}>
|
||||
Discard
|
||||
</button>
|
||||
</div>
|
||||
|
@ -1,54 +0,0 @@
|
||||
import {LitElement, html} from './lit-all.min.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
import {styles} from './tf-styles.js';
|
||||
|
||||
/*
|
||||
** Provide a list of IDs, and this lets the user pick one.
|
||||
*/
|
||||
class TfIdentityPickerElement extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
ids: {type: Array},
|
||||
selected: {type: String},
|
||||
users: {type: Object},
|
||||
};
|
||||
}
|
||||
|
||||
static styles = styles;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.ids = [];
|
||||
this.users = {};
|
||||
}
|
||||
|
||||
changed(event) {
|
||||
this.selected = event.srcElement.value;
|
||||
this.dispatchEvent(
|
||||
new Event('change', {
|
||||
srcElement: this,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<select
|
||||
class="w3-select w3-dark-grey w3-padding w3-border"
|
||||
@change=${this.changed}
|
||||
style="max-width: 100%; overflow: hidden"
|
||||
>
|
||||
${(this.ids ?? []).map(
|
||||
(id) =>
|
||||
html`<option ?selected=${id == this.selected} value=${id}>
|
||||
${this.users[id]?.name
|
||||
? this.users[id]?.name + ' - '
|
||||
: undefined}<small>${id}</small>
|
||||
</option>`
|
||||
)}
|
||||
</select>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('tf-id-picker', TfIdentityPickerElement);
|
@ -1,4 +1,4 @@
|
||||
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
|
||||
import {LitElement, html, render, unsafeHTML} from './lit-all.min.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
import * as tfutils from './tf-utils.js';
|
||||
import * as emojis from './emojis.js';
|
||||
@ -54,6 +54,12 @@ class TfMessageElement extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
show_reactions() {
|
||||
let modal = document.getElementById('reactions_modal');
|
||||
modal.users = this.users;
|
||||
modal.votes = this.message?.votes || [];
|
||||
}
|
||||
|
||||
render_votes() {
|
||||
function normalize_expression(expression) {
|
||||
if (expression === 'Like' || !expression) {
|
||||
@ -66,19 +72,21 @@ class TfMessageElement extends LitElement {
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
return html`<div>
|
||||
${(this.message.votes || []).map(
|
||||
(vote) => html`
|
||||
<span
|
||||
title="${this.users[vote.author]?.name ?? vote.author} ${new Date(
|
||||
vote.timestamp
|
||||
)}"
|
||||
>
|
||||
${normalize_expression(vote.content.vote.expression)}
|
||||
</span>
|
||||
`
|
||||
)}
|
||||
</div>`;
|
||||
if (this.message?.votes?.length) {
|
||||
return html`<div class="w3-button" @click=${this.show_reactions}>
|
||||
${(this.message.votes || []).map(
|
||||
(vote) => html`
|
||||
<span
|
||||
title="${this.users[vote.author]?.name ?? vote.author} ${new Date(
|
||||
vote.timestamp
|
||||
)}"
|
||||
>
|
||||
${normalize_expression(vote.content.vote.expression)}
|
||||
</span>
|
||||
`
|
||||
)}
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
render_raw() {
|
||||
@ -125,7 +133,7 @@ class TfMessageElement extends LitElement {
|
||||
}
|
||||
|
||||
react(event) {
|
||||
emojis.picker((x) => this.vote(x));
|
||||
emojis.picker((x) => this.vote(x), null, this.whoami);
|
||||
}
|
||||
|
||||
show_image(link) {
|
||||
@ -240,7 +248,7 @@ ${JSON.stringify(mention, null, 2)}</pre
|
||||
let self = this;
|
||||
return html`
|
||||
<fieldset
|
||||
style="background-color: rgba(0, 0, 0, 0.1); padding: 0.5em; border: 1px solid black"
|
||||
style="padding: 0.5em; border: 1px solid black"
|
||||
>
|
||||
<legend>Mentions</legend>
|
||||
${mentions.map((x) => self.render_mention(x))}
|
||||
@ -282,14 +290,14 @@ ${JSON.stringify(mention, null, 2)}</pre
|
||||
if (this.message.child_messages?.length) {
|
||||
if (!this.expanded[this.message.id]) {
|
||||
return html`<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => self.set_expanded(true)}
|
||||
>
|
||||
+ ${this.total_child_messages(this.message) + ' More'}
|
||||
</button>`;
|
||||
} else {
|
||||
return html`<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => self.set_expanded(false)}
|
||||
>
|
||||
Collapse</button
|
||||
@ -331,20 +339,23 @@ ${JSON.stringify(mention, null, 2)}</pre
|
||||
if (this.message?.decrypted?.type == 'post') {
|
||||
content = this.message.decrypted;
|
||||
}
|
||||
let class_background = this.message?.decrypted
|
||||
? 'w3-pale-red'
|
||||
: 'w3-theme-d4';
|
||||
let self = this;
|
||||
let raw_button;
|
||||
switch (this.format) {
|
||||
case 'raw':
|
||||
if (content?.type == 'post' || content?.type == 'blog') {
|
||||
raw_button = html`<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => (self.format = 'md')}
|
||||
>
|
||||
Markdown
|
||||
</button>`;
|
||||
} else {
|
||||
raw_button = html`<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => (self.format = 'message')}
|
||||
>
|
||||
Message
|
||||
@ -353,7 +364,7 @@ ${JSON.stringify(mention, null, 2)}</pre
|
||||
break;
|
||||
case 'md':
|
||||
raw_button = html`<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => (self.format = 'message')}
|
||||
>
|
||||
Message
|
||||
@ -361,7 +372,7 @@ ${JSON.stringify(mention, null, 2)}</pre
|
||||
break;
|
||||
case 'decrypted':
|
||||
raw_button = html`<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => (self.format = 'raw')}
|
||||
>
|
||||
Raw
|
||||
@ -370,14 +381,14 @@ ${JSON.stringify(mention, null, 2)}</pre
|
||||
default:
|
||||
if (this.message.decrypted) {
|
||||
raw_button = html`<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => (self.format = 'decrypted')}
|
||||
>
|
||||
Decrypted
|
||||
</button>`;
|
||||
} else {
|
||||
raw_button = html`<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => (self.format = 'raw')}
|
||||
>
|
||||
Raw
|
||||
@ -389,8 +400,8 @@ ${JSON.stringify(mention, null, 2)}</pre
|
||||
let body;
|
||||
return html`
|
||||
<div
|
||||
class="w3-card-4"
|
||||
style="background-color: rgba(255, 255, 255, 0.1); margin-top: 8px; padding: 16px; display: inline-block; overflow-wrap: anywhere"
|
||||
class="w3-card-4 w3-theme-d4 w3-border-theme"
|
||||
style="margin-top: 8px; padding: 16px; display: inline-block; overflow-wrap: anywhere"
|
||||
>
|
||||
<tf-user id=${self.message.author} .users=${self.users}></tf-user>
|
||||
<span style="padding-right: 8px"
|
||||
@ -400,13 +411,24 @@ ${JSON.stringify(mention, null, 2)}</pre
|
||||
>
|
||||
${raw_button} ${self.format == 'raw' ? self.render_raw() : inner}
|
||||
${self.render_votes()}
|
||||
${(self.message.child_messages || []).map(
|
||||
(x) => html`
|
||||
<tf-message
|
||||
.message=${x}
|
||||
whoami=${self.whoami}
|
||||
.users=${self.users}
|
||||
.drafts=${self.drafts}
|
||||
.expanded=${self.expanded}
|
||||
></tf-message>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
if (this.message?.type === 'contact_group') {
|
||||
return html` <div
|
||||
class="w3-card-4"
|
||||
style="border: 1px solid black; background-color: rgba(255, 255, 255, 0.1); margin-top: 8px; padding: 16px; overflow-wrap: anywhere"
|
||||
class="w3-card-4 w3-theme-d4 w3-border-theme"
|
||||
style="margin-top: 8px; padding: 16px; overflow-wrap: anywhere"
|
||||
>
|
||||
${this.message.messages.map(
|
||||
(x) =>
|
||||
@ -421,8 +443,8 @@ ${JSON.stringify(mention, null, 2)}</pre
|
||||
</div>`;
|
||||
} else if (this.message.placeholder) {
|
||||
return html` <div
|
||||
class="w3-card-4"
|
||||
style="border: 1px solid black; background-color: rgba(255, 255, 255, 0.1); margin-top: 8px; padding: 16px; overflow-wrap: anywhere"
|
||||
class="w3-card-4 w3-theme-d4 w3-border-theme"
|
||||
style="margin-top: 8px; padding: 16px; overflow-wrap: anywhere"
|
||||
>
|
||||
<a target="_top" href=${'#' + this.message.id}>${this.message.id}</a>
|
||||
(placeholder)
|
||||
@ -498,13 +520,11 @@ ${JSON.stringify(mention, null, 2)}</pre
|
||||
branch=${this.message.id}
|
||||
.drafts=${this.drafts}
|
||||
@tf-discard=${this.discard_reply}
|
||||
author=${this.message.author}
|
||||
></tf-compose>
|
||||
`
|
||||
: html`
|
||||
<button
|
||||
class="w3-button w3-dark-grey"
|
||||
@click=${this.show_reply}
|
||||
>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.show_reply}>
|
||||
Reply
|
||||
</button>
|
||||
`;
|
||||
@ -533,7 +553,7 @@ ${JSON.stringify(content, null, 2)}</pre
|
||||
}
|
||||
let content_warning = html`
|
||||
<div
|
||||
class="w3-panel w3-round-xlarge w3-blue"
|
||||
class="w3-panel w3-round-xlarge w3-theme-l4"
|
||||
style="cursor: pointer"
|
||||
@click=${(x) => this.toggle_expanded(':cw')}
|
||||
>
|
||||
@ -553,9 +573,6 @@ ${JSON.stringify(content, null, 2)}</pre
|
||||
let is_encrypted = this.message?.decrypted
|
||||
? html`<span style="align-self: center">🔓</span>`
|
||||
: undefined;
|
||||
let style_background = this.message?.decrypted
|
||||
? 'rgba(255, 0, 0, 0.2)'
|
||||
: 'rgba(255, 255, 255, 0.1)';
|
||||
return html`
|
||||
<style>
|
||||
code {
|
||||
@ -572,8 +589,8 @@ ${JSON.stringify(content, null, 2)}</pre
|
||||
}
|
||||
</style>
|
||||
<div
|
||||
class="w3-card-4"
|
||||
style="border: 1px solid black; background-color: ${style_background}; margin-top: 8px; padding: 16px"
|
||||
class="w3-card-4 ${class_background} w3-border-theme"
|
||||
style="margin-top: 8px; padding: 16px"
|
||||
>
|
||||
<div style="display: flex; flex-direction: row">
|
||||
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
||||
@ -588,7 +605,7 @@ ${JSON.stringify(content, null, 2)}</pre
|
||||
${payload} ${this.render_votes()}
|
||||
<p>
|
||||
${reply}
|
||||
<button class="w3-button w3-dark-grey" @click=${this.react}>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.react}>
|
||||
React
|
||||
</button>
|
||||
</p>
|
||||
@ -599,9 +616,6 @@ ${JSON.stringify(content, null, 2)}</pre
|
||||
let is_encrypted = this.message?.decrypted
|
||||
? html`<span style="align-self: center">🔓</span>`
|
||||
: undefined;
|
||||
let style_background = this.message?.decrypted
|
||||
? 'rgba(255, 0, 0, 0.2)'
|
||||
: 'rgba(255, 255, 255, 0.1)';
|
||||
return html`
|
||||
<style>
|
||||
code {
|
||||
@ -618,8 +632,8 @@ ${JSON.stringify(content, null, 2)}</pre
|
||||
}
|
||||
</style>
|
||||
<div
|
||||
class="w3-card-4"
|
||||
style="border: 1px solid black; background-color: ${style_background}; margin-top: 8px; padding: 16px"
|
||||
class="w3-card-4 ${class_background} w3-border-theme"
|
||||
style="margin-top: 8px; padding: 16px"
|
||||
>
|
||||
<div style="display: flex; flex-direction: row">
|
||||
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
||||
@ -633,7 +647,7 @@ ${JSON.stringify(content, null, 2)}</pre
|
||||
</div>
|
||||
${content.text} ${this.render_votes()}
|
||||
<p>
|
||||
<button class="w3-button w3-dark-grey" @click=${this.react}>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.react}>
|
||||
React
|
||||
</button>
|
||||
</p>
|
||||
@ -685,13 +699,11 @@ ${JSON.stringify(content, null, 2)}</pre
|
||||
branch=${this.message.id}
|
||||
.drafts=${this.drafts}
|
||||
@tf-discard=${this.discard_reply}
|
||||
author=${this.message.author}
|
||||
></tf-compose>
|
||||
`
|
||||
: html`
|
||||
<button
|
||||
class="w3-button w3-dark-grey"
|
||||
@click=${this.show_reply}
|
||||
>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.show_reply}>
|
||||
Reply
|
||||
</button>
|
||||
`;
|
||||
@ -711,8 +723,8 @@ ${JSON.stringify(content, null, 2)}</pre
|
||||
}
|
||||
</style>
|
||||
<div
|
||||
class="w3-card-4"
|
||||
style="border: 1px solid black; background-color: rgba(255, 255, 255, 0.1); margin-top: 8px; padding: 16px"
|
||||
class="w3-card-4 w3-theme-d4 w3-border-theme"
|
||||
style="margin-top: 8px; padding: 16px"
|
||||
>
|
||||
<div style="display: flex; flex-direction: row">
|
||||
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
||||
@ -728,7 +740,7 @@ ${JSON.stringify(content, null, 2)}</pre
|
||||
${this.render_mentions()}
|
||||
<div>
|
||||
${reply}
|
||||
<button class="w3-button w3-dark-grey" @click=${this.react}>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.react}>
|
||||
React
|
||||
</button>
|
||||
</div>
|
||||
|
@ -215,49 +215,49 @@ class TfProfileElement extends LitElement {
|
||||
let server_follow;
|
||||
if (this.server_follows_me === true) {
|
||||
server_follow = html`<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => this.server_follow_me(false)}
|
||||
>
|
||||
Server, Stop Following Me
|
||||
</button>`;
|
||||
} else if (this.server_follows_me === false) {
|
||||
server_follow = html`<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => this.server_follow_me(true)}
|
||||
>
|
||||
Server, Follow Me
|
||||
</button>`;
|
||||
}
|
||||
edit = html`
|
||||
<button class="w3-button w3-dark-grey" @click=${this.save_edits}>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.save_edits}>
|
||||
Save Profile
|
||||
</button>
|
||||
<button class="w3-button w3-dark-grey" @click=${this.discard_edits}>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.discard_edits}>
|
||||
Discard
|
||||
</button>
|
||||
${server_follow}
|
||||
`;
|
||||
} else {
|
||||
edit = html`<button class="w3-button w3-dark-grey" @click=${this.edit}>
|
||||
edit = html`<button class="w3-button w3-theme-d1" @click=${this.edit}>
|
||||
Edit Profile
|
||||
</button>`;
|
||||
}
|
||||
}
|
||||
if (this.id !== this.whoami && this.following !== undefined) {
|
||||
follow = this.following
|
||||
? html`<button class="w3-button w3-dark-grey" @click=${this.unfollow}>
|
||||
? html`<button class="w3-button w3-theme-d1" @click=${this.unfollow}>
|
||||
Unfollow
|
||||
</button>`
|
||||
: html`<button class="w3-button w3-dark-grey" @click=${this.follow}>
|
||||
: html`<button class="w3-button w3-theme-d1" @click=${this.follow}>
|
||||
Follow
|
||||
</button>`;
|
||||
}
|
||||
if (this.id !== this.whoami && this.blocking !== undefined) {
|
||||
block = this.blocking
|
||||
? html`<button class="w3-button w3-dark-grey" @click=${this.unblock}>
|
||||
? html`<button class="w3-button w3-theme-d1" @click=${this.unblock}>
|
||||
Unblock
|
||||
</button>`
|
||||
: html`<button class="w3-button w3-dark-grey" @click=${this.block}>
|
||||
: html`<button class="w3-button w3-theme-d1" @click=${this.block}>
|
||||
Block
|
||||
</button>`;
|
||||
}
|
||||
@ -267,16 +267,16 @@ class TfProfileElement extends LitElement {
|
||||
<div class="w3-container">
|
||||
<div>
|
||||
<label for="name">Name:</label>
|
||||
<input class="w3-input w3-dark-grey" type="text" id="name" value=${this.editing.name} @input=${(event) => (this.editing = Object.assign({}, this.editing, {name: event.srcElement.value}))}></input>
|
||||
<input class="w3-input w3-theme-d1" type="text" id="name" value=${this.editing.name} @input=${(event) => (this.editing = Object.assign({}, this.editing, {name: event.srcElement.value}))}></input>
|
||||
</div>
|
||||
<div><label for="description">Description:</label></div>
|
||||
<textarea class="w3-input w3-dark-grey" style="resize: vertical" rows="8" id="description" @input=${(event) => (this.editing = Object.assign({}, this.editing, {description: event.srcElement.value}))}>${this.editing.description}</textarea>
|
||||
<textarea class="w3-input w3-theme-d1" style="resize: vertical" rows="8" id="description" @input=${(event) => (this.editing = Object.assign({}, this.editing, {description: event.srcElement.value}))}>${this.editing.description}</textarea>
|
||||
<div>
|
||||
<label for="public_web_hosting">Public Web Hosting:</label>
|
||||
<input class="w3-check w3-dark-grey" type="checkbox" id="public_web_hosting" ?checked=${this.editing.publicWebHosting} @input=${(event) => (self.editing = Object.assign({}, self.editing, {publicWebHosting: event.srcElement.checked}))}></input>
|
||||
<input class="w3-check w3-theme-d1" type="checkbox" id="public_web_hosting" ?checked=${this.editing.publicWebHosting} @input=${(event) => (self.editing = Object.assign({}, self.editing, {publicWebHosting: event.srcElement.checked}))}></input>
|
||||
</div>
|
||||
<div>
|
||||
<button class="w3-button w3-dark-grey" @click=${this.attach_image}>Attach Image</button>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.attach_image}>Attach Image</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
|
68
apps/ssb/tf-reactions-modal.js
Normal file
68
apps/ssb/tf-reactions-modal.js
Normal file
@ -0,0 +1,68 @@
|
||||
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
|
||||
import {styles} from './tf-styles.js';
|
||||
|
||||
class TfReactionsModalElement extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
users: {type: Object},
|
||||
votes: {type: Array},
|
||||
};
|
||||
}
|
||||
|
||||
static styles = styles;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.votes = [];
|
||||
this.users = {};
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.votes = [];
|
||||
}
|
||||
|
||||
render() {
|
||||
let self = this;
|
||||
return this.votes?.length
|
||||
? html` <div
|
||||
class="w3-modal w3-animate-opacity"
|
||||
style="display: block; box-sizing: border-box"
|
||||
>
|
||||
<div class="w3-modal-content w3-card-4 w3-theme-d1">
|
||||
<div class="w3-container w3-padding">
|
||||
<header class="w3-container">
|
||||
<h2>Reactions</h2>
|
||||
<span class="w3-button w3-display-topright" @click=${this.clear}
|
||||
>×</span
|
||||
>
|
||||
</header>
|
||||
<ul class="w3-theme-dark w3-container w3-ul">
|
||||
${this.votes.map(
|
||||
(x) => html`
|
||||
<li class="w3-bar">
|
||||
<span class="w3-bar-item"
|
||||
>${x?.content?.vote?.expression}</span
|
||||
>
|
||||
<tf-user
|
||||
class="w3-bar-item"
|
||||
id=${x.author}
|
||||
.users=${this.users}
|
||||
></tf-user>
|
||||
<span class="w3-bar-item w3-right"
|
||||
>${new Date(x?.timestamp).toLocaleString()}</span
|
||||
>
|
||||
</li>
|
||||
`
|
||||
)}
|
||||
</ul>
|
||||
<footer class="w3-container w3-padding">
|
||||
<button class="w3-button" @click=${this.clear}>Close</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('tf-reactions-modal', TfReactionsModalElement);
|
File diff suppressed because it is too large
Load Diff
@ -33,9 +33,11 @@ class TfTabConnectionsElement extends LitElement {
|
||||
|
||||
render_connection_summary(connection) {
|
||||
if (connection.address && connection.port) {
|
||||
return html`(<small>${connection.address}:${connection.port}</small>)`;
|
||||
return html`<div>
|
||||
<small>${connection.address}:${connection.port}</small>
|
||||
</div>`;
|
||||
} else if (connection.tunnel) {
|
||||
return html`(room peer)`;
|
||||
return html`<div>room peer</div>`;
|
||||
} else {
|
||||
return JSON.stringify(connection);
|
||||
}
|
||||
@ -61,7 +63,7 @@ class TfTabConnectionsElement extends LitElement {
|
||||
return html`
|
||||
<li>
|
||||
<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => self._tunnel(connection.tunnel.id, connection.pubkey)}
|
||||
>
|
||||
Connect
|
||||
@ -73,15 +75,17 @@ class TfTabConnectionsElement extends LitElement {
|
||||
|
||||
render_broadcast(connection) {
|
||||
return html`
|
||||
<li>
|
||||
<li class="w3-bar" style="overflow: hidden; overflow-wrap: nowrap">
|
||||
<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-bar-item w3-button w3-theme-d1"
|
||||
@click=${() => tfrpc.rpc.connect(connection)}
|
||||
>
|
||||
Connect
|
||||
</button>
|
||||
<tf-user id=${connection.pubkey} .users=${this.users}></tf-user>
|
||||
${this.render_connection_summary(connection)}
|
||||
<div class="w3-bar-item">
|
||||
<tf-user id=${connection.pubkey} .users=${this.users}></tf-user>
|
||||
${this.render_connection_summary(connection)}
|
||||
</div>
|
||||
</li>
|
||||
`;
|
||||
}
|
||||
@ -94,7 +98,7 @@ class TfTabConnectionsElement extends LitElement {
|
||||
render_connection(connection) {
|
||||
return html`
|
||||
<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => tfrpc.rpc.closeConnection(connection.id)}
|
||||
>
|
||||
Close
|
||||
@ -103,6 +107,9 @@ class TfTabConnectionsElement extends LitElement {
|
||||
${connection.tunnel !== undefined
|
||||
? '🚇'
|
||||
: html`(${connection.host}:${connection.port})`}
|
||||
<div>${connection.requests.map(x => html`
|
||||
<span class="w3-tag w3-small">${x.request_number > 0 ? '🟩' : '🟥'} ${x.name}</span>
|
||||
`)}</div>
|
||||
<ul>
|
||||
${this.connections
|
||||
.filter((x) => x.tunnel === this.connections.indexOf(connection))
|
||||
@ -115,56 +122,64 @@ class TfTabConnectionsElement extends LitElement {
|
||||
render() {
|
||||
let self = this;
|
||||
return html`
|
||||
<div class="w3-container">
|
||||
<div class="w3-container" style="box-sizing: border-box">
|
||||
<h2>New Connection</h2>
|
||||
<textarea class="w3-input w3-dark-grey" id="code"></textarea>
|
||||
<textarea class="w3-input w3-theme-d1" id="code"></textarea>
|
||||
<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() =>
|
||||
tfrpc.rpc.connect(self.renderRoot.getElementById('code').value)}
|
||||
>
|
||||
Connect
|
||||
</button>
|
||||
<h2>Broadcasts</h2>
|
||||
<ul>
|
||||
<ul class="w3-ul w3-border">
|
||||
${this.broadcasts
|
||||
.filter((x) => x.address)
|
||||
.map((x) => self.render_broadcast(x))}
|
||||
</ul>
|
||||
<h2>Connections</h2>
|
||||
<ul>
|
||||
<ul class="w3-ul w3-border">
|
||||
${this.connections
|
||||
.filter((x) => x.tunnel === undefined)
|
||||
.map((x) => html` <li>${this.render_connection(x)}</li> `)}
|
||||
.map(
|
||||
(x) => html`
|
||||
<li class="w3-bar">${this.render_connection(x)}</li>
|
||||
`
|
||||
)}
|
||||
</ul>
|
||||
<h2>Stored Connections (WIP)</h2>
|
||||
<ul>
|
||||
<h2>Stored Connections</h2>
|
||||
<ul class="w3-ul w3-border">
|
||||
${this.stored_connections.map(
|
||||
(x) => html`
|
||||
<li>
|
||||
<li class="w3-bar">
|
||||
<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-bar-item w3-button w3-theme-d1"
|
||||
@click=${() => self.forget_stored_connection(x)}
|
||||
>
|
||||
Forget
|
||||
</button>
|
||||
<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-bar-item w3-button w3-theme-d1"
|
||||
@click=${() => tfrpc.rpc.connect(x)}
|
||||
>
|
||||
Connect
|
||||
</button>
|
||||
${x.address}:${x.port}
|
||||
<tf-user id=${x.pubkey} .users=${self.users}></tf-user>
|
||||
<div class="w3-bar-item">
|
||||
<tf-user id=${x.pubkey} .users=${self.users}></tf-user>
|
||||
<div><small>${x.address}:${x.port}</small></div>
|
||||
</div>
|
||||
</li>
|
||||
`
|
||||
)}
|
||||
</ul>
|
||||
<h2>Local Accounts</h2>
|
||||
<ul>
|
||||
<ul class="w3-ul w3-border">
|
||||
${this.identities.map(
|
||||
(x) =>
|
||||
html`<li><tf-user id=${x} .users=${this.users}></tf-user></li>`
|
||||
html`<li class="w3-bar">
|
||||
<tf-user id=${x} .users=${this.users}></tf-user>
|
||||
</li>`
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -187,7 +187,7 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
if (!this.hash.startsWith('#@') && !this.hash.startsWith('#%')) {
|
||||
more = html`
|
||||
<p>
|
||||
<button class="w3-button w3-dark-grey" @click=${this.load_more}>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.load_more}>
|
||||
Load More
|
||||
</button>
|
||||
</p>
|
||||
|
@ -84,10 +84,7 @@ class TfTabNewsElement extends LitElement {
|
||||
} else {
|
||||
delete this.drafts[id];
|
||||
}
|
||||
/* Only trigger a re-render if we're creating a new draft or discarding an old one. */
|
||||
if ((previous !== undefined) != (event.detail.draft !== undefined)) {
|
||||
this.drafts = Object.assign({}, this.drafts);
|
||||
}
|
||||
this.drafts = Object.assign({}, this.drafts);
|
||||
tfrpc.rpc.localStorageSet('drafts', JSON.stringify(this.drafts));
|
||||
}
|
||||
|
||||
@ -119,7 +116,7 @@ class TfTabNewsElement extends LitElement {
|
||||
return html`
|
||||
<p class="w3-bar">
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-dark-grey"
|
||||
class="w3-bar-item w3-button w3-theme-d1"
|
||||
@click=${this.show_more}
|
||||
>
|
||||
${this.new_messages_text()}
|
||||
|
@ -110,14 +110,14 @@ class TfTabQueryElement extends LitElement {
|
||||
<textarea
|
||||
id="search"
|
||||
rows="8"
|
||||
class="w3-input w3-dark-grey"
|
||||
class="w3-input w3-theme-d1"
|
||||
style="flex: 1; resize: vertical"
|
||||
@keydown=${this.search_keydown}
|
||||
>
|
||||
${this.query}</textarea
|
||||
>
|
||||
<button
|
||||
class="w3-button w3-dark-grey"
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${(event) =>
|
||||
self.search(self.renderRoot.getElementById('search').value)}
|
||||
>
|
||||
|
@ -78,8 +78,8 @@ class TfTabSearchElement extends LitElement {
|
||||
let self = this;
|
||||
return html`
|
||||
<div style="display: flex; flex-direction: row; gap: 4px">
|
||||
<input type="text" class="w3-input w3-dark-grey" id="search" value=${this.query} style="flex: 1" @keydown=${this.search_keydown}></input>
|
||||
<button class="w3-button w3-dark-grey" @click=${(event) => self.search(self.renderRoot.getElementById('search').value)}>Search</button>
|
||||
<input type="text" class="w3-input w3-theme-d1" id="search" value=${this.query} style="flex: 1" @keydown=${this.search_keydown}></input>
|
||||
<button class="w3-button w3-theme-d1" @click=${(event) => self.search(self.renderRoot.getElementById('search').value)}>Search</button>
|
||||
</div>
|
||||
<tf-news id="news" whoami=${this.whoami} .messages=${this.messages} .users=${this.users} .expanded=${this.expanded} @tf-expand=${this.on_expand}></tf-news>
|
||||
`;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as linkify from './commonmark-linkify.js';
|
||||
import * as hashtagify from './commonmark-hashtag.js';
|
||||
|
||||
const k_code_classes = 'w3-theme-l4 w3-theme-border w3-round';
|
||||
|
||||
function image(node, entering) {
|
||||
if (
|
||||
node.firstChild?.type === 'text' &&
|
||||
@ -61,13 +62,32 @@ function image(node, entering) {
|
||||
}
|
||||
}
|
||||
|
||||
function code(node) {
|
||||
let attrs = this.attrs(node);
|
||||
attrs.push(['class', k_code_classes]);
|
||||
this.tag('code', attrs);
|
||||
this.out(node.literal);
|
||||
this.tag('/code');
|
||||
}
|
||||
|
||||
function attrs(node) {
|
||||
let result = commonmark.HtmlRenderer.prototype.attrs.bind(this)(node);
|
||||
if (node.type == 'block_quote') {
|
||||
result.push(['class', 'w3-theme-d1']);
|
||||
} else if (node.type == 'code_block') {
|
||||
result.push(['class', k_code_classes]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function markdown(md) {
|
||||
let reader = new commonmark.Parser({safe: true});
|
||||
let writer = new commonmark.HtmlRenderer();
|
||||
writer.image = image;
|
||||
writer.code = code;
|
||||
writer.attrs = attrs;
|
||||
let parsed = reader.parse(md || '');
|
||||
parsed = hashtagify.transform(parsed);
|
||||
parsed = linkify.transform(parsed);
|
||||
let walker = parsed.walker();
|
||||
let event, node;
|
||||
while ((event = walker.next())) {
|
||||
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "⚙️"
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
import * as tfrpc from '/tfrpc.js';
|
||||
|
||||
tfrpc.register(async function getIdentities() {
|
||||
return ssb.getIdentities();
|
||||
});
|
||||
tfrpc.register(async function createID(id) {
|
||||
return await ssb.createIdentity();
|
||||
});
|
||||
tfrpc.register(async function getPrivateKey(id) {
|
||||
return bip39Words(await ssb.getPrivateKey(id));
|
||||
});
|
||||
tfrpc.register(async function getThemes() {
|
||||
// TODO
|
||||
return ['solarized', 'gruvbox', 'light'];
|
||||
});
|
||||
tfrpc.register(async function setTheme() {
|
||||
// TODO
|
||||
console.warn("setTheme called - not implemented")
|
||||
return null;
|
||||
});
|
||||
tfrpc.register(async function reload() {
|
||||
await main();
|
||||
});
|
||||
|
||||
async function main() {
|
||||
// Get body.html
|
||||
const body = utf8Decode(await getFile('body.html'));
|
||||
|
||||
// Build the document
|
||||
const document = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="/static/tildefriends-v1.css"/>
|
||||
<script src="tf-theme-picker.js" type="module"></script>
|
||||
<script src="tf-password-form.js" type="module"></script>
|
||||
<script src="tf-delete-account-btn.js" type="module"></script>
|
||||
<script src="tf-identity-manager.js" type="module"></script>
|
||||
</head>
|
||||
|
||||
<body class="flex-column">
|
||||
${body}
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
// Send it to the browser
|
||||
app.setDocument(document);
|
||||
}
|
||||
|
||||
main();
|
@ -1,20 +0,0 @@
|
||||
<h1>Your settings</h1>
|
||||
|
||||
<div class="box flex-column">
|
||||
<h2>Appearance</h2>
|
||||
|
||||
<tf-theme-picker></tf-theme-picker>
|
||||
</div>
|
||||
|
||||
<div class="box flex-column">
|
||||
<h2>Danger Zone</h2>
|
||||
|
||||
<h3>Manage your identities</h3>
|
||||
<tf-identity-manager></tf-identity-manager>
|
||||
|
||||
<h3>Change my password</h3>
|
||||
<tf-password-form></tf-password-form>
|
||||
|
||||
<h3>Delete your account</h3>
|
||||
<tf-delete-account-btn></tf-delete-account-btn>
|
||||
</div>
|
120
apps/user_settings/lit-all.min.js
vendored
120
apps/user_settings/lit-all.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
||||
/* */
|
@ -1 +0,0 @@
|
||||
/* */
|
@ -1,36 +0,0 @@
|
||||
import {LitElement, html} from './lit-all.min.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
|
||||
class TfDeleteAccountButtonElement extends LitElement {
|
||||
static get properties() {
|
||||
return {};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
deleteAccount() {
|
||||
const res = confirm(
|
||||
'Are you really sure you want to delete your account ?'
|
||||
);
|
||||
|
||||
if (!res) return;
|
||||
|
||||
console.warn('TODO');
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<link rel="stylesheet" href="/static/tildefriends-v1.css"/>
|
||||
|
||||
<span>This action is irreversible !</span>
|
||||
|
||||
<button class="red" @click=${this.deleteAccount}>
|
||||
[Not implemented] Delete my Tilde Friends account
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('tf-delete-account-btn', TfDeleteAccountButtonElement);
|
@ -1,71 +0,0 @@
|
||||
import {LitElement, html} from './lit-all.min.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
|
||||
class TfIdentityManagerElement extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
ids: {type: Array},
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.ids = [];
|
||||
this.load();
|
||||
}
|
||||
|
||||
async load() {
|
||||
this.ids = await tfrpc.rpc.getIdentities();
|
||||
}
|
||||
|
||||
async createIdentity() {
|
||||
try {
|
||||
let id = await tfrpc.rpc.createID();
|
||||
alert('Successfully created: ' + id);
|
||||
await tfrpc.rpc.reload();
|
||||
} catch (err) {
|
||||
alert('Error creating identity: ' + err);
|
||||
}
|
||||
}
|
||||
|
||||
async exportIdentity(id) {
|
||||
alert('Your private key is:\n' + (await tfrpc.rpc.getPrivateKey(id)));
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<link rel="stylesheet" href="/static/tildefriends-v1.css"/>
|
||||
<style>
|
||||
.id-span {
|
||||
font-family: monospace;
|
||||
margin-left: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h4>Create a new identity</h4>
|
||||
<button id="create-id" class="green" @click=${this.createIdentity}>Create Identity</button>
|
||||
|
||||
<h4>Import an SSB Identity from 12 BIP39 English Words</h4>
|
||||
<textarea id="add-id" style="width: 100%" rows="4"></textarea>
|
||||
<button class="green">[Not implemented] Import Identity</button>
|
||||
|
||||
<h4>Warning !</h4>
|
||||
<strong>Anybody that has access to your private key can gain total access over your account.</strong>
|
||||
<br><br>
|
||||
Tilde Friends' contributors will never ask you for your private key !
|
||||
|
||||
<ul>
|
||||
${this.ids.map(
|
||||
(id) =>
|
||||
html`
|
||||
<li>
|
||||
<button class="blue" @click=${() => this.exportIdentity(id)}>Export Identity</button>
|
||||
<button class="red">[Not implemented] Delete Identity</button>
|
||||
<span class="id-span">${id}</span>
|
||||
</li>`
|
||||
)}
|
||||
</ul>`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('tf-identity-manager', TfIdentityManagerElement);
|
@ -1,68 +0,0 @@
|
||||
import {LitElement, html} from './lit-all.min.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
|
||||
class TfPasswordFormElement extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
//selected: {type: String},
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a password against different requirements
|
||||
* @param {string} password the password to validate
|
||||
* @returns
|
||||
*/
|
||||
validatePassword(password) {
|
||||
// TODO(tasiaiso)
|
||||
return true;
|
||||
}
|
||||
|
||||
submitPassword() {
|
||||
const currentPwd = this.shadowRoot.getElementById('current').value;
|
||||
const newPwd = this.shadowRoot.getElementById('new').value;
|
||||
const repeatPwd = this.shadowRoot.getElementById('Repeat').value;
|
||||
|
||||
if (!(newPwd === repeatPwd)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO
|
||||
// tfrpc.changePassword()
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<link rel="stylesheet" href="/static/tildefriends-v1.css"/>
|
||||
|
||||
<style>
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="grid">
|
||||
<label for="current">Current password:</label>
|
||||
<input type="password" id="current" name="current" autocomplete="current-password" />
|
||||
|
||||
<label for="new">Enter new password:</label>
|
||||
<input type="password" id="new" name="new" autocomplete="new-password" />
|
||||
|
||||
<label for="repeat">Repeat new password:</label>
|
||||
<input type="password" id="repeat" name="repeat" autocomplete="new-password" />
|
||||
</div>
|
||||
|
||||
<button @click=${this.submitPassword} class="red">
|
||||
[Not implemented] Change my password
|
||||
</button>
|
||||
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('tf-password-form', TfPasswordFormElement);
|
@ -1,40 +0,0 @@
|
||||
import {LitElement, html} from './lit-all.min.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
|
||||
class TfThemePickerElement extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
selected: {type: String},
|
||||
themes: {type: Array},
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.load();
|
||||
}
|
||||
|
||||
async load() {
|
||||
this.themes = await tfrpc.rpc.getThemes();
|
||||
}
|
||||
|
||||
changed(event) {
|
||||
this.selected = event.srcElement.value;
|
||||
console.log('selected theme', this.selected);
|
||||
// TODO
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<link rel="stylesheet" href="/static/tildefriends-v1.css"/>
|
||||
|
||||
<label for="theme">[Not implemented] Choose your theme:</label>
|
||||
|
||||
<select name="theme" ?hidden=${!this.themes?.length} @change=${this.changed}>
|
||||
${(this.themes ?? []).map((id) => html`<option value=${id}>${id}</option>`)}
|
||||
</select>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('tf-theme-picker', TfThemePickerElement);
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "👋",
|
||||
"previous": "&zFISmRDAv+SXFonfZ9/sHNhrmMe+poTU22gwZzuSkT4=.sha256"
|
||||
"previous": "&W5aJp2DgOW5rQ0AOIC9Ut3DpsahPrO6PjkJ1PQbNRdM=.sha256"
|
||||
}
|
||||
|
@ -55,7 +55,7 @@
|
||||
</p>
|
||||
<a
|
||||
class="w3-button w3-black w3-padding-large"
|
||||
href="https://www.tildefriends.net/~cory/releases/"
|
||||
href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
||||
><i class="fa fa-download"></i> Download</a
|
||||
>
|
||||
<a
|
||||
@ -63,6 +63,11 @@
|
||||
href="https://www.tildefriends.net/~cory/apps/"
|
||||
><i class="fa fa-link"></i> Try It</a
|
||||
>
|
||||
<a
|
||||
class="w3-button w3-black w3-padding-large"
|
||||
href="https://dev.tildefriends.net/"
|
||||
><i class="fa fa-mug-hot"></i> Code</a
|
||||
>
|
||||
</div>
|
||||
<div class="w3-col l4 m6">
|
||||
<img src="tildefriends.png" class="w3-image w3-right w3-hide-small" />
|
||||
@ -70,6 +75,60 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Getting Starting Section -->
|
||||
<div class="w3-indigo w3-center">
|
||||
<div class="w3-row-padding w3-padding-64">
|
||||
<div class="w3-jumbo">
|
||||
<i class="fa fa-rocket"></i> <b>Getting Started</b>
|
||||
</div>
|
||||
<div>
|
||||
<h2>First-time user checklist:</h2>
|
||||
<ol type="1" style="text-align: left">
|
||||
<li>
|
||||
<a href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
||||
>Download</a
|
||||
>
|
||||
Tilde Friends and run your own instance, or use
|
||||
<a href="https://www.tildefriends.net/"
|
||||
>https://www.tildefriends.net/</a
|
||||
>.
|
||||
</li>
|
||||
<li>
|
||||
Create an account to identify yourself with that instance by
|
||||
username and password.
|
||||
</li>
|
||||
<li>
|
||||
Create an SSB identity in the <b>ssb</b> app. This will generate a
|
||||
keypair used to identify yourself to other users and sign your
|
||||
messages so that they can be verified as from you.
|
||||
</li>
|
||||
<li>
|
||||
Describe yourself in your profile in the <b>ssb</b> app. Give
|
||||
yourself a name and an avatar if you like.
|
||||
</li>
|
||||
<li>
|
||||
Connect to others. You will automatically discover peers on the
|
||||
same instance and same network if there are any. Or use
|
||||
<a href="https://github.com/staltz/ssb-room/blob/master/FAQ.md"
|
||||
>rooms</a
|
||||
>
|
||||
and pubs to reach more distant users.
|
||||
<a href="https://www.tildefriends.net/~cory/room/"
|
||||
>tildefriends.net itself</a
|
||||
>
|
||||
operates as a room, so you can connect and see who else is online
|
||||
and establish a connection.
|
||||
</li>
|
||||
<li>Follow people to grow your network.</li>
|
||||
<li>
|
||||
Use the <b>edit</b> link at the top of any page to start modifying
|
||||
and making apps.
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SSB Section -->
|
||||
<div class="w3-light-grey">
|
||||
<div class="w3-row-padding w3-padding-64">
|
||||
@ -199,7 +258,7 @@
|
||||
</div>
|
||||
|
||||
<div class="w3-row" style="margin-top: 64px">
|
||||
<a href="https://codemirror.net/5/" class="w3-col s3">
|
||||
<a href="https://codemirror.net/docs/changelog/" class="w3-col s3">
|
||||
<i class="fa fa-keyboard w3-text-indigo w3-jumbo"></i>
|
||||
<p>CodeMirror</p>
|
||||
</a>
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "📝",
|
||||
"previous": "&DnfuAUGzzalSh9NgZXnzDc9Ru5aM0omfRJ4h27jYw4k=.sha256"
|
||||
"previous": "&DaYqKHRBKhjFGaOzbKZ1+/pLspJeEkDJYTF2B50tH6k=.sha256"
|
||||
}
|
||||
|
@ -4,6 +4,9 @@ import * as utils from './utils.js';
|
||||
let g_hash;
|
||||
let g_collection_notifies = {};
|
||||
|
||||
tfrpc.register(async function getActiveIdentity() {
|
||||
return ssb.getActiveIdentity();
|
||||
});
|
||||
tfrpc.register(async function getOwnerIdentities() {
|
||||
return ssb.getOwnerIdentities();
|
||||
});
|
||||
@ -54,6 +57,9 @@ core.register('message', async function message_handler(message) {
|
||||
await tfrpc.rpc.hash_changed(message.hash);
|
||||
}
|
||||
});
|
||||
core.register('setActiveIdentity', async function setActiveIdentityHandler(id) {
|
||||
await tfrpc.rpc.setActiveIdentity(id);
|
||||
});
|
||||
|
||||
tfrpc.register(function set_hash(hash) {
|
||||
if (g_hash != hash) {
|
||||
|
@ -10,7 +10,6 @@
|
||||
window.litDisableBundleWarning = true;
|
||||
</script>
|
||||
<script src="tf-collection.js" type="module"></script>
|
||||
<script src="tf-id-picker.js" type="module"></script>
|
||||
<script src="tf-wiki-doc.js" type="module"></script>
|
||||
<script src="tf-wiki-app.js" type="module"></script>
|
||||
</body>
|
||||
|
4
apps/wiki/lit-all.min.js
vendored
4
apps/wiki/lit-all.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,44 +0,0 @@
|
||||
import {LitElement, html} from './lit-all.min.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
|
||||
/*
|
||||
** Provide a list of IDs, and this lets the user pick one.
|
||||
*/
|
||||
class TfIdentityPickerElement extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
ids: {type: Array},
|
||||
selected: {type: String},
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.ids = [];
|
||||
}
|
||||
|
||||
changed(event) {
|
||||
this.selected = event.srcElement.value;
|
||||
this.dispatchEvent(
|
||||
new Event('change', {
|
||||
srcElement: this,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<link rel="stylesheet" href="tildefriends.css" />
|
||||
<select @change=${this.changed} style="max-width: 100%">
|
||||
${(this.ids ?? []).map(
|
||||
(id) =>
|
||||
html`<option ?selected=${id == this.selected} value=${id}>
|
||||
${id}
|
||||
</option>`
|
||||
)}
|
||||
</select>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('tf-id-picker', TfIdentityPickerElement);
|
@ -31,13 +31,16 @@ class TfCollectionsAppElement extends LitElement {
|
||||
tfrpc.register(function hash_changed(hash) {
|
||||
self.notify_hash_changed(hash);
|
||||
});
|
||||
tfrpc.register(function setActiveIdentity(id) {
|
||||
self.whoami = id;
|
||||
});
|
||||
tfrpc.rpc.get_hash().then((hash) => self.notify_hash_changed(hash));
|
||||
}
|
||||
|
||||
async load() {
|
||||
this.ids = await tfrpc.rpc.getIdentities();
|
||||
this.owner_ids = await tfrpc.rpc.getOwnerIdentities();
|
||||
this.whoami = await tfrpc.rpc.localStorageGet('collections_whoami');
|
||||
this.whoami = await tfrpc.rpc.getActiveIdentity();
|
||||
let ids = [...new Set([...this.owner_ids, this.whoami])].sort();
|
||||
this.following = Object.keys(await tfrpc.rpc.following(ids, 1)).sort();
|
||||
|
||||
@ -273,9 +276,6 @@ class TfCollectionsAppElement extends LitElement {
|
||||
margin-right: 16px;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
<tf-id-picker .ids=${this.ids} selected=${this.whoami} @change=${this.on_whoami_changed} ?hidden=${!this.ids?.length}></tf-id-picker>
|
||||
</div>
|
||||
<div>
|
||||
${keyed(
|
||||
this.whoami,
|
||||
|
@ -96,7 +96,7 @@ export async function collection(
|
||||
let rows = [];
|
||||
await ssb.sqlAsync(
|
||||
`
|
||||
SELECT messages.id, author, content, timestamp
|
||||
SELECT messages.id, author, json(content) AS content, timestamp
|
||||
FROM messages
|
||||
JOIN json_each(?1) AS id ON messages.author = id.value
|
||||
WHERE
|
||||
|
BIN
bleh.tar.xz
BIN
bleh.tar.xz
Binary file not shown.
38
core/app.js
38
core/app.js
@ -1,4 +1,3 @@
|
||||
import * as auth from './auth.js';
|
||||
import * as core from './core.js';
|
||||
|
||||
let g_next_id = 1;
|
||||
@ -87,8 +86,7 @@ App.prototype.send = function (message) {
|
||||
function socket(request, response, client) {
|
||||
let process;
|
||||
let options = {};
|
||||
let credentials = auth.query(request.headers);
|
||||
let refresh = auth.makeRefresh(credentials);
|
||||
let credentials = httpd.auth_query(request.headers);
|
||||
|
||||
response.onClose = async function () {
|
||||
if (process && process.task) {
|
||||
@ -143,12 +141,21 @@ function socket(request, response, client) {
|
||||
}
|
||||
}
|
||||
response.send(
|
||||
JSON.stringify({
|
||||
action: 'session',
|
||||
credentials: credentials,
|
||||
parentApp: parentApp,
|
||||
id: blobId,
|
||||
}),
|
||||
JSON.stringify(
|
||||
Object.assign(
|
||||
{
|
||||
action: 'session',
|
||||
credentials: credentials,
|
||||
parentApp: parentApp,
|
||||
id: blobId,
|
||||
},
|
||||
await ssb.getIdentityInfo(
|
||||
credentials?.session?.name,
|
||||
packageOwner,
|
||||
packageName
|
||||
)
|
||||
)
|
||||
),
|
||||
0x1
|
||||
);
|
||||
|
||||
@ -212,6 +219,10 @@ function socket(request, response, client) {
|
||||
if (process) {
|
||||
process.resetPermission(message.permission);
|
||||
}
|
||||
} else if (message.action == 'setActiveIdentity') {
|
||||
process.setActiveIdentity(message.identity);
|
||||
} else if (message.action == 'createIdentity') {
|
||||
process.createIdentity();
|
||||
} else if (message.message == 'tfrpc') {
|
||||
if (message.id && g_calls[message.id]) {
|
||||
if (message.error !== undefined) {
|
||||
@ -241,14 +252,7 @@ function socket(request, response, client) {
|
||||
}
|
||||
};
|
||||
|
||||
response.upgrade(
|
||||
100,
|
||||
refresh
|
||||
? {
|
||||
'Set-Cookie': `session=${refresh.token}; path=/; Max-Age=${refresh.interval}; Secure; SameSite=Strict`,
|
||||
}
|
||||
: {}
|
||||
);
|
||||
response.upgrade(100, {});
|
||||
}
|
||||
|
||||
export {socket, App};
|
||||
|
@ -19,8 +19,11 @@
|
||||
Object.assign(app, g_data);
|
||||
|
||||
class TfAuthElement extends LitElement {
|
||||
static get_properties() {
|
||||
static get properties() {
|
||||
return {
|
||||
code_of_conduct: {type: String},
|
||||
error: {type: String},
|
||||
have_administrator: {type: Boolean},
|
||||
name: {type: String},
|
||||
tab: {type: String},
|
||||
};
|
||||
@ -31,11 +34,6 @@
|
||||
this.tab = 'login';
|
||||
}
|
||||
|
||||
tab_changed(name) {
|
||||
this.tab = name;
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
render() {
|
||||
let self = this;
|
||||
return html`
|
||||
@ -83,16 +81,16 @@
|
||||
<h1 ?hidden=${this.name === undefined}>Welcome, ${this.name}.</h1>
|
||||
|
||||
<div style="display: flex; flex-direction: row; width: 100%">
|
||||
<input type="radio" name="tab" id="login" value="Login" ?checked=${this.tab == 'login'} @change=${() => self.tab_changed('login')}></input>
|
||||
<input type="radio" name="tab" id="login" value="Login" ?checked=${this.tab == 'login'} @change=${() => (self.tab = 'login')}></input>
|
||||
<label for="login" id="login_label">Login</label>
|
||||
|
||||
<input type="radio" name="tab" id="register" value="Register" ?checked=${this.tab == 'register'} @change=${() => self.tab_changed('register')}></input>
|
||||
<input type="radio" name="tab" id="register" value="Register" ?checked=${this.tab == 'register'} @change=${() => (self.tab = 'register')}></input>
|
||||
<label for="register" id="register_label">Register</label>
|
||||
|
||||
<input type="radio" name="tab" id="guest" value="Guest" ?checked=${this.tab == 'guest'} @change=${() => self.tab_changed('guest')}></input>
|
||||
<input type="radio" name="tab" id="guest" value="Guest" ?checked=${this.tab == 'guest'} @change=${() => (self.tab = 'guest')}></input>
|
||||
<label for="guest" id="guest_label">Guest</label>
|
||||
|
||||
<input type="radio" name="tab" id="change" value="Change Password" ?checked=${this.tab == 'change'} @change=${() => self.tab_changed('change')}></input>
|
||||
<input type="radio" name="tab" id="change" value="Change Password" ?checked=${this.tab == 'change'} @change=${() => (self.tab = 'change')}></input>
|
||||
<label for="change" id="change_label">Change Password</label>
|
||||
</div>
|
||||
|
||||
|
420
core/auth.js
420
core/auth.js
@ -1,420 +0,0 @@
|
||||
import * as core from './core.js';
|
||||
import * as form from './form.js';
|
||||
|
||||
let gDatabase = new Database('auth');
|
||||
|
||||
const kRefreshInterval = 1 * 7 * 24 * 60 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* Makes a Base64 value URL safe
|
||||
* @param {string} value
|
||||
* @returns TODOC
|
||||
*/
|
||||
function b64url(value) {
|
||||
value = value.replaceAll('+', '-').replaceAll('/', '_');
|
||||
let equals = value.indexOf('=');
|
||||
|
||||
if (equals !== -1) {
|
||||
return value.substring(0, equals);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {string} value
|
||||
* @returns
|
||||
*/
|
||||
function unb64url(value) {
|
||||
value = value.replaceAll('-', '+').replaceAll('_', '/');
|
||||
let remainder = value.length % 4;
|
||||
|
||||
if (remainder == 3) {
|
||||
return value + '=';
|
||||
} else if (remainder == 2) {
|
||||
return value + '==';
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON Web Token
|
||||
* @param {object} payload Object: {"name": "username"}
|
||||
* @returns the JWT
|
||||
*/
|
||||
function makeJwt(payload) {
|
||||
const ids = ssb.getIdentities(':auth');
|
||||
let id;
|
||||
|
||||
if (ids?.length) {
|
||||
id = ids[0];
|
||||
} else {
|
||||
id = ssb.createIdentity(':auth');
|
||||
}
|
||||
|
||||
const final_payload = b64url(
|
||||
base64Encode(
|
||||
JSON.stringify(
|
||||
Object.assign({}, payload, {
|
||||
exp: new Date().valueOf() + kRefreshInterval,
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
const jwt = [
|
||||
b64url(base64Encode(JSON.stringify({alg: 'HS256', typ: 'JWT'}))),
|
||||
final_payload,
|
||||
b64url(ssb.hmacsha256sign(final_payload, ':auth', id)),
|
||||
].join('.');
|
||||
return jwt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a JWT ?
|
||||
* @param {*} session TODOC
|
||||
* @returns
|
||||
*/
|
||||
function readSession(session) {
|
||||
let jwt_parts = session?.split('.');
|
||||
|
||||
if (jwt_parts?.length === 3) {
|
||||
let [header, payload, signature] = jwt_parts;
|
||||
header = JSON.parse(utf8Decode(base64Decode(unb64url(header))));
|
||||
|
||||
if (header.typ === 'JWT' && header.alg === 'HS256') {
|
||||
signature = unb64url(signature);
|
||||
let id = ssb.getIdentities(':auth');
|
||||
|
||||
if (id?.length && ssb.hmacsha256verify(id[0], payload, signature)) {
|
||||
const result = JSON.parse(utf8Decode(base64Decode(unb64url(payload))));
|
||||
const now = new Date().valueOf();
|
||||
|
||||
if (now < result.exp) {
|
||||
print(`JWT valid for another ${(result.exp - now) / 1000} seconds.`);
|
||||
return result;
|
||||
} else {
|
||||
print(`JWT expired by ${(now - result.exp) / 1000} seconds.`);
|
||||
}
|
||||
} else {
|
||||
print('JWT verification failed.');
|
||||
}
|
||||
} else {
|
||||
print('Invalid JWT header.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the provided password matches the hash
|
||||
* @param {string} password
|
||||
* @param {string} hash bcrypt hash
|
||||
* @returns true if the password matches the hash
|
||||
*/
|
||||
function verifyPassword(password, hash) {
|
||||
return bCrypt.hashpw(password, hash) === hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashes a password
|
||||
* @param {string} password
|
||||
* @returns {string} TODOC
|
||||
*/
|
||||
function hashPassword(password) {
|
||||
let salt = bCrypt.gensalt(12);
|
||||
return bCrypt.hashpw(password, salt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is an administrator on the instance
|
||||
* @returns TODOC
|
||||
*/
|
||||
function noAdministrator() {
|
||||
return (
|
||||
!core.globalSettings ||
|
||||
!core.globalSettings.permissions ||
|
||||
!Object.keys(core.globalSettings.permissions).some(function (name) {
|
||||
return (
|
||||
core.globalSettings.permissions[name].indexOf('administration') != -1
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a user an administrator
|
||||
* @param {string} name the user's name
|
||||
*/
|
||||
function makeAdministrator(name) {
|
||||
if (!core.globalSettings.permissions) {
|
||||
core.globalSettings.permissions = {};
|
||||
}
|
||||
if (!core.globalSettings.permissions[name]) {
|
||||
core.globalSettings.permissions[name] = [];
|
||||
}
|
||||
if (core.globalSettings.permissions[name].indexOf('administration') == -1) {
|
||||
core.globalSettings.permissions[name].push('administration');
|
||||
}
|
||||
|
||||
core.setGlobalSettings(core.globalSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} headers most likely an object
|
||||
* @returns
|
||||
*/
|
||||
function getCookies(headers) {
|
||||
let cookies = {};
|
||||
|
||||
if (headers.cookie) {
|
||||
let parts = headers.cookie.split(/,|;/);
|
||||
for (let i in parts) {
|
||||
let equals = parts[i].indexOf('=');
|
||||
let name = parts[i].substring(0, equals).trim();
|
||||
let value = parts[i].substring(equals + 1).trim();
|
||||
cookies[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return cookies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a username
|
||||
* @param {string} name
|
||||
* @returns false | boolean[] ?
|
||||
*/
|
||||
function isNameValid(name) {
|
||||
// TODO(tasiaiso): convert this into a regex
|
||||
let c = name.charAt(0);
|
||||
return (
|
||||
((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) &&
|
||||
name
|
||||
.split()
|
||||
.map(
|
||||
(x) =>
|
||||
x >= ('a' && x <= 'z') ||
|
||||
x >= ('A' && x <= 'Z') ||
|
||||
x >= ('0' && x <= '9')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Request handler ?
|
||||
* @param {*} request TODOC
|
||||
* @param {*} response
|
||||
* @returns
|
||||
*/
|
||||
function handler(request, response) {
|
||||
// TODO(tasiaiso): split this function
|
||||
let session = getCookies(request.headers).session;
|
||||
if (request.uri == '/login') {
|
||||
let formData = form.decodeForm(request.query);
|
||||
if (query(request.headers)?.permissions?.authenticated) {
|
||||
if (formData.return) {
|
||||
response.writeHead(303, {Location: formData.return});
|
||||
} else {
|
||||
response.writeHead(303, {
|
||||
Location:
|
||||
(request.client.tls ? 'https://' : 'http://') +
|
||||
request.headers.host +
|
||||
'/',
|
||||
'Content-Length': '0',
|
||||
});
|
||||
}
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
|
||||
let sessionIsNew = false;
|
||||
let loginError;
|
||||
|
||||
if (request.method == 'POST' || formData.submit) {
|
||||
sessionIsNew = true;
|
||||
formData = form.decodeForm(utf8Decode(request.body), formData);
|
||||
if (formData.submit == 'Login') {
|
||||
let account = gDatabase.get('user:' + formData.name);
|
||||
account = account ? JSON.parse(account) : account;
|
||||
if (formData.register == '1') {
|
||||
if (
|
||||
!account &&
|
||||
isNameValid(formData.name) &&
|
||||
formData.password == formData.confirm
|
||||
) {
|
||||
let users = new Set();
|
||||
let users_original = gDatabase.get('users');
|
||||
try {
|
||||
users = new Set(JSON.parse(users_original));
|
||||
} catch {}
|
||||
if (!users.has(formData.name)) {
|
||||
users.add(formData.name);
|
||||
}
|
||||
users = JSON.stringify([...users].sort());
|
||||
if (users !== users_original) {
|
||||
gDatabase.set('users', users);
|
||||
}
|
||||
session = makeJwt({name: formData.name});
|
||||
account = {password: hashPassword(formData.password)};
|
||||
gDatabase.set('user:' + formData.name, JSON.stringify(account));
|
||||
if (noAdministrator()) {
|
||||
makeAdministrator(formData.name);
|
||||
}
|
||||
} else {
|
||||
loginError = 'Error registering account.';
|
||||
}
|
||||
} else if (formData.change == '1') {
|
||||
if (
|
||||
account &&
|
||||
isNameValid(formData.name) &&
|
||||
formData.new_password == formData.confirm &&
|
||||
verifyPassword(formData.password, account.password)
|
||||
) {
|
||||
session = makeJwt({name: formData.name});
|
||||
account = {password: hashPassword(formData.new_password)};
|
||||
gDatabase.set('user:' + formData.name, JSON.stringify(account));
|
||||
} else {
|
||||
loginError = 'Error changing password.';
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
account &&
|
||||
account.password &&
|
||||
verifyPassword(formData.password, account.password)
|
||||
) {
|
||||
session = makeJwt({name: formData.name});
|
||||
if (noAdministrator()) {
|
||||
makeAdministrator(formData.name);
|
||||
}
|
||||
} else {
|
||||
loginError = 'Invalid username or password.';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Proceed as Guest
|
||||
session = makeJwt({name: 'guest'});
|
||||
}
|
||||
}
|
||||
|
||||
let cookie = `session=${session}; path=/; Max-Age=${kRefreshInterval}; ${request.client.tls ? 'Secure; ' : ''}SameSite=Strict; HttpOnly`;
|
||||
let entry = readSession(session);
|
||||
if (entry && formData.return) {
|
||||
response.writeHead(303, {
|
||||
Location: formData.return,
|
||||
'Set-Cookie': cookie,
|
||||
});
|
||||
response.end();
|
||||
} else {
|
||||
File.readFile('core/auth.html')
|
||||
.then(function (data) {
|
||||
let html = utf8Decode(data);
|
||||
let auth_data = {
|
||||
session_is_new: sessionIsNew,
|
||||
name: entry?.name,
|
||||
error: loginError,
|
||||
code_of_conduct: core.globalSettings.code_of_conduct,
|
||||
have_administrator: !noAdministrator(),
|
||||
};
|
||||
html = utf8Encode(
|
||||
html.replace('$AUTH_DATA', JSON.stringify(auth_data))
|
||||
);
|
||||
response.writeHead(200, {
|
||||
'Content-Type': 'text/html; charset=utf-8',
|
||||
'Set-Cookie': cookie,
|
||||
'Content-Length': html.length,
|
||||
});
|
||||
response.end(html);
|
||||
})
|
||||
.catch(function (error) {
|
||||
response.writeHead(404, {
|
||||
'Content-Type': 'text/plain; charset=utf-8',
|
||||
Connection: 'close',
|
||||
});
|
||||
response.end('404 File not found');
|
||||
});
|
||||
}
|
||||
} else if (request.uri == '/login/logout') {
|
||||
response.writeHead(303, {
|
||||
'Set-Cookie': `session=; path=/; ${request.client.tls ? 'Secure; ' : ''}SameSite=Strict; expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly`,
|
||||
Location: '/login' + (request.query ? '?' + request.query : ''),
|
||||
});
|
||||
response.end();
|
||||
} else {
|
||||
response.writeHead(200, {
|
||||
'Content-Type': 'text/plain; charset=utf-8',
|
||||
Connection: 'close',
|
||||
});
|
||||
response.end('Hello, ' + request.client.peerName + '.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a user's permissions based on it's session ?
|
||||
* @param {*} session TODOC
|
||||
* @returns
|
||||
*/
|
||||
function getPermissions(session) {
|
||||
let permissions;
|
||||
let entry = readSession(session);
|
||||
if (entry) {
|
||||
permissions = getPermissionsForUser(entry.name);
|
||||
permissions.authenticated = entry.name !== 'guest';
|
||||
}
|
||||
return permissions || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a user's permissions ?
|
||||
* @param {string} userName TODOC
|
||||
* @returns
|
||||
*/
|
||||
function getPermissionsForUser(userName) {
|
||||
let permissions = {};
|
||||
if (
|
||||
core.globalSettings &&
|
||||
core.globalSettings.permissions &&
|
||||
core.globalSettings.permissions[userName]
|
||||
) {
|
||||
for (let i in core.globalSettings.permissions[userName]) {
|
||||
permissions[core.globalSettings.permissions[userName][i]] = true;
|
||||
}
|
||||
}
|
||||
return permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} headers
|
||||
* @returns
|
||||
*/
|
||||
function query(headers) {
|
||||
let session = getCookies(headers).session;
|
||||
let entry;
|
||||
let autologin = tildefriends.args.autologin;
|
||||
if ((entry = autologin ? {name: autologin} : readSession(session))) {
|
||||
return {
|
||||
session: entry,
|
||||
permissions: autologin
|
||||
? getPermissionsForUser(autologin)
|
||||
: getPermissions(session),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes a JWT ?
|
||||
* @param {*} credentials TODOC
|
||||
* @returns
|
||||
*/
|
||||
function makeRefresh(credentials) {
|
||||
if (credentials?.session?.name) {
|
||||
return {
|
||||
token: makeJwt({name: credentials.session.name}),
|
||||
interval: kRefreshInterval,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export {handler, query, makeRefresh};
|
209
core/client.js
209
core/client.js
@ -56,6 +56,9 @@ class TfNavigationElement extends LitElement {
|
||||
spark_lines: {type: Object},
|
||||
version: {type: Object},
|
||||
show_version: {type: Boolean},
|
||||
identity: {type: String},
|
||||
identities: {type: Array},
|
||||
names: {type: Object},
|
||||
};
|
||||
}
|
||||
|
||||
@ -65,6 +68,8 @@ class TfNavigationElement extends LitElement {
|
||||
this.show_permissions = false;
|
||||
this.status = {};
|
||||
this.spark_lines = {};
|
||||
this.identities = [];
|
||||
this.names = {};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,10 +102,10 @@ class TfNavigationElement extends LitElement {
|
||||
get_spark_line(key, options) {
|
||||
if (!this.spark_lines[key]) {
|
||||
let spark_line = document.createElement('tf-sparkline');
|
||||
spark_line.style.display = 'flex';
|
||||
spark_line.style.flexDirection = 'row';
|
||||
spark_line.style.flex = '0 50 5em';
|
||||
spark_line.title = key;
|
||||
spark_line.classList.add('w3-bar-item');
|
||||
spark_line.classList.add('w3-hide-small');
|
||||
spark_line.style.paddingRight = '0';
|
||||
if (options) {
|
||||
if (options.max) {
|
||||
spark_line.max = options.max;
|
||||
@ -118,16 +123,105 @@ class TfNavigationElement extends LitElement {
|
||||
*/
|
||||
render_login() {
|
||||
if (this?.credentials?.session?.name) {
|
||||
return html`<a id="login" href="/login/logout?return=${url() + hash()}"
|
||||
return html`<a
|
||||
class="w3-bar-item w3-right"
|
||||
id="login"
|
||||
href="/login/logout?return=${url() + hash()}"
|
||||
>logout ${this.credentials.session.name}</a
|
||||
>`;
|
||||
} else {
|
||||
return html`<a id="login" href="/login?return=${url() + hash()}"
|
||||
return html`<a
|
||||
class="w3-bar-item w3-right"
|
||||
id="login"
|
||||
href="/login?return=${url() + hash()}"
|
||||
>login</a
|
||||
>`;
|
||||
}
|
||||
}
|
||||
|
||||
set_active_identity(id) {
|
||||
send({action: 'setActiveIdentity', identity: id});
|
||||
this.renderRoot.getElementById('id_dropdown').classList.remove('w3-show');
|
||||
}
|
||||
|
||||
create_identity(event) {
|
||||
if (confirm('Are you sure you want to create a new identity?')) {
|
||||
send({action: 'createIdentity'});
|
||||
}
|
||||
}
|
||||
|
||||
toggle_id_dropdown() {
|
||||
this.renderRoot.getElementById('id_dropdown').classList.toggle('w3-show');
|
||||
}
|
||||
|
||||
edit_profile() {
|
||||
window.location.href = '/~core/ssb/#' + this.identity;
|
||||
}
|
||||
|
||||
render_identity() {
|
||||
let self = this;
|
||||
if (this.identities?.length) {
|
||||
return html`
|
||||
<link type="text/css" rel="stylesheet" href="/static/w3.css" />
|
||||
<div class="w3-dropdown-click w3-right" style="max-width: 100%">
|
||||
<button
|
||||
class="w3-button w3-rest w3-cyan"
|
||||
style="text-overflow: ellipsis; overflow: hidden; white-space: nowrap; max-width: 100%"
|
||||
@click=${self.toggle_id_dropdown}
|
||||
>
|
||||
${self.names[this.identity]}${self.names[this.identity] ===
|
||||
this.identity
|
||||
? ''
|
||||
: html` - ${this.identity}`}
|
||||
▾
|
||||
</button>
|
||||
<div
|
||||
id="id_dropdown"
|
||||
class="w3-dropdown-content w3-bar-block w3-card-4"
|
||||
style="max-width: 100%"
|
||||
>
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-border"
|
||||
@click=${() => (window.location.href = '/~core/identity')}
|
||||
>
|
||||
Manage Identities...
|
||||
</button>
|
||||
<button
|
||||
class="w3-bar-item w3-button w3-border"
|
||||
@click=${self.edit_profile}
|
||||
>
|
||||
Edit Profile...
|
||||
</button>
|
||||
${this.identities.map(
|
||||
(x) => html`
|
||||
<button
|
||||
class="w3-bar-item w3-button ${x === self.identity
|
||||
? 'w3-cyan'
|
||||
: ''}"
|
||||
@click=${() => self.set_active_identity(x)}
|
||||
style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap"
|
||||
>
|
||||
${self.names[x]}${self.names[x] === x ? '' : html` - ${x}`}
|
||||
</button>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
return html`
|
||||
<link type="text/css" rel="stylesheet" href="/static/w3.css" />
|
||||
<button
|
||||
id="create_identity"
|
||||
@click=${this.create_identity}
|
||||
class="w3-button w3-mobile w3-blue w3-right"
|
||||
>
|
||||
Create an Identity
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @returns
|
||||
@ -145,11 +239,17 @@ class TfNavigationElement extends LitElement {
|
||||
<div>This app has the following permissions:</div>
|
||||
${Object.keys(this.permissions).map(
|
||||
(key) => html`
|
||||
<div>
|
||||
<span>${key}</span>: ${this.permissions[key] ? '✅ Allowed' : '❌ Denied'}
|
||||
<button @click=${() => this.reset_permission(key)} class='w3-button w3-red">Reset</button>
|
||||
</div>
|
||||
`
|
||||
<div>
|
||||
<span>${key}</span>:
|
||||
${this.permissions[key] ? '✅ Allowed' : '❌ Denied'}
|
||||
<button
|
||||
@click=${() => this.reset_permission(key)}
|
||||
class="w3-button w3-red"
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
<button
|
||||
@click=${() => (this.show_permissions = false)}
|
||||
@ -163,6 +263,10 @@ class TfNavigationElement extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
clear_error() {
|
||||
this.status = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @returns
|
||||
@ -170,6 +274,7 @@ class TfNavigationElement extends LitElement {
|
||||
render() {
|
||||
let self = this;
|
||||
return html`
|
||||
<link type="text/css" rel="stylesheet" href="/static/w3.css" />
|
||||
<style>
|
||||
${k_global_style} .tooltip {
|
||||
position: absolute;
|
||||
@ -185,17 +290,17 @@ class TfNavigationElement extends LitElement {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
<div
|
||||
style="margin: 4px; display: flex; flex-direction: row; flex-wrap: nowrap; gap: 3px; align-items: center"
|
||||
>
|
||||
<div class="w3-black w3-bar">
|
||||
<span
|
||||
class="w3-bar-item"
|
||||
style="cursor: pointer"
|
||||
@click=${() => (this.show_version = !this.show_version)}
|
||||
>😎</span
|
||||
>
|
||||
<span
|
||||
?hidden=${!this.show_version}
|
||||
style="flex: 0 0; white-space: nowrap"
|
||||
class="w3-bar-item"
|
||||
style=${'white-space: nowrap' +
|
||||
(this.show_version ? '' : '; display: none')}
|
||||
title=${this.version?.name +
|
||||
' ' +
|
||||
Object.entries(this.version || {})
|
||||
@ -204,6 +309,7 @@ class TfNavigationElement extends LitElement {
|
||||
>${this.version?.number}</span
|
||||
>
|
||||
<a
|
||||
class="w3-bar-item"
|
||||
accesskey="h"
|
||||
@mouseover=${set_access_key_title}
|
||||
data-tip="Open home app."
|
||||
@ -212,6 +318,7 @@ class TfNavigationElement extends LitElement {
|
||||
>TF</a
|
||||
>
|
||||
<a
|
||||
class="w3-bar-item"
|
||||
accesskey="a"
|
||||
@mouseover=${set_access_key_title}
|
||||
data-tip="Open apps list."
|
||||
@ -219,6 +326,7 @@ class TfNavigationElement extends LitElement {
|
||||
>apps</a
|
||||
>
|
||||
<a
|
||||
class="w3-bar-item"
|
||||
accesskey="e"
|
||||
@mouseover=${set_access_key_title}
|
||||
data-tip="Toggle the app editor."
|
||||
@ -227,6 +335,7 @@ class TfNavigationElement extends LitElement {
|
||||
>edit</a
|
||||
>
|
||||
<a
|
||||
class="w3-bar-item"
|
||||
accesskey="p"
|
||||
@mouseover=${set_access_key_title}
|
||||
data-tip="View and change permissions."
|
||||
@ -234,27 +343,34 @@ class TfNavigationElement extends LitElement {
|
||||
@click=${() => (self.show_permissions = !self.show_permissions)}
|
||||
>🎛️</a
|
||||
>
|
||||
<span
|
||||
style="display: inline-block; vertical-align: top; white-space: pre; color: ${this
|
||||
.status.color ?? kErrorColor}"
|
||||
>${this.status.message}</span
|
||||
>
|
||||
<span id="requests"></span>
|
||||
${this.render_permissions()}
|
||||
<span
|
||||
style="flex: 1 1; display: flex; flex-direction: row; white-space: nowrap; margin: 0; padding: 0"
|
||||
>${Object.keys(this.spark_lines)
|
||||
.sort()
|
||||
.map((x) => this.spark_lines[x])
|
||||
.map((x) => [
|
||||
html`<span style="font-size: xx-small">${x.dataset.emoji}</span>`,
|
||||
x,
|
||||
])}</span
|
||||
>
|
||||
<span style="flex: 0 0; white-space: nowrap"
|
||||
>${this.render_login()}</span
|
||||
>
|
||||
${this.status?.message && !this.status.is_error
|
||||
? html`
|
||||
<link type="text/css" rel="stylesheet" href="/static/w3.css" />
|
||||
<div
|
||||
class="w3-bar-item"
|
||||
style="color: ${this.status.color ?? kStatusColor}"
|
||||
>
|
||||
${this.status.message}
|
||||
</div>
|
||||
`
|
||||
: undefined}
|
||||
${Object.keys(this.spark_lines)
|
||||
.sort()
|
||||
.map((x) => this.spark_lines[x])}
|
||||
${this.render_login()} ${this.render_identity()}
|
||||
</div>
|
||||
${this.status?.is_error
|
||||
? html`
|
||||
<link type="text/css" rel="stylesheet" href="/static/w3.css" />
|
||||
<div class="w3-model w3-animate-top" style="position: absolute; left: 50%; transform: translate(-50%); z-index: 1">
|
||||
<dijv class="w3-modal-content w3-card-4" style="display: block; padding: 1em">
|
||||
<span @click=${self.clear_error} class="w3-button w3-display-topright">×</span>
|
||||
<div style="color: ${this.status.color ?? kErrorColor}"><b>ERROR:</b><p style="white-space: pre">${this.status.message}</p></div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: undefined}
|
||||
`;
|
||||
}
|
||||
}
|
||||
@ -578,13 +694,13 @@ class TfSparkLineElement extends LitElement {
|
||||
) / 10.0;
|
||||
return html`
|
||||
<svg
|
||||
style="max-width: 7.5em; max-height: 1.5em; margin: 0; padding: 0; background: #000"
|
||||
style="max-width: 7.5em; margin: 0; padding: 0; background: #000; height: 1em"
|
||||
viewBox="0 0 50 10"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
${this.lines.map((x) => this.render_line(x))}
|
||||
<text x="0" y="1em" style="font: 8px sans-serif; fill: #fff">
|
||||
${max}
|
||||
${this.dataset.emoji}${max}
|
||||
</text>
|
||||
</svg>
|
||||
`;
|
||||
@ -1000,9 +1116,9 @@ function api_postMessage(message) {
|
||||
function api_error(error) {
|
||||
if (error) {
|
||||
if (typeof error == 'string') {
|
||||
setStatusMessage('⚠️ ' + error, '#f00');
|
||||
setStatusMessage('⚠️ ' + error, kErrorColor);
|
||||
} else {
|
||||
setStatusMessage('⚠️ ' + error.message + '\n' + error.stack, '#f00');
|
||||
setStatusMessage('⚠️ ' + error.message + '\n' + error.stack, kErrorColor);
|
||||
}
|
||||
}
|
||||
console.log('error', error);
|
||||
@ -1119,11 +1235,19 @@ function api_setHash(hash) {
|
||||
function _receive_websocket_message(message) {
|
||||
if (message && message.action == 'session') {
|
||||
setStatusMessage('🟢 Executing...', kStatusColor);
|
||||
document.getElementsByTagName('tf-navigation')[0].credentials =
|
||||
message.credentials;
|
||||
let navigation = document.getElementsByTagName('tf-navigation')[0];
|
||||
navigation.credentials = message.credentials;
|
||||
navigation.identities = message.identities;
|
||||
navigation.identity = message.identity;
|
||||
navigation.names = message.names;
|
||||
} else if (message && message.action == 'permissions') {
|
||||
document.getElementsByTagName('tf-navigation')[0].permissions =
|
||||
message.permissions ?? {};
|
||||
let navigation = document.getElementsByTagName('tf-navigation')[0];
|
||||
navigation.permissions = message.permissions ?? {};
|
||||
} else if (message && message.action == 'identities') {
|
||||
let navigation = document.getElementsByTagName('tf-navigation')[0];
|
||||
navigation.identities = message.identities;
|
||||
navigation.identity = message.identity;
|
||||
navigation.names = message.names;
|
||||
} else if (message && message.action == 'ready') {
|
||||
setStatusMessage(null);
|
||||
if (window.location.hash) {
|
||||
@ -1211,6 +1335,7 @@ function setStatusMessage(message, color) {
|
||||
document.getElementsByTagName('tf-navigation')[0].status = {
|
||||
message: message,
|
||||
color: color,
|
||||
is_error: color == kErrorColor,
|
||||
};
|
||||
}
|
||||
|
||||
|
156
core/core.js
156
core/core.js
@ -1,5 +1,4 @@
|
||||
import * as app from './app.js';
|
||||
import * as auth from './auth.js';
|
||||
import * as form from './form.js';
|
||||
import * as http from './http.js';
|
||||
|
||||
@ -245,6 +244,7 @@ function broadcastEvent(eventName, argv) {
|
||||
}
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} message
|
||||
@ -266,6 +266,34 @@ function broadcast(message) {
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {String} eventName
|
||||
* @param {*} argv
|
||||
* @returns
|
||||
*/
|
||||
function broadcastAppEventToUser(
|
||||
user,
|
||||
packageOwner,
|
||||
packageName,
|
||||
eventName,
|
||||
argv
|
||||
) {
|
||||
let promises = [];
|
||||
for (let process of Object.values(gProcesses)) {
|
||||
if (
|
||||
process.credentials?.session?.name === user &&
|
||||
process.packageOwner == packageOwner &&
|
||||
process.packageName == packageName
|
||||
) {
|
||||
if (process.eventHandlers[eventName]) {
|
||||
promises.push(invoke(process.eventHandlers[eventName], argv));
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} caller
|
||||
@ -361,6 +389,8 @@ async function getProcessBlob(blobId, key, options) {
|
||||
process.key = key;
|
||||
process.credentials = options.credentials || {};
|
||||
process.task = new Task();
|
||||
process.packageOwner = options.packageOwner;
|
||||
process.packageName = options.packageName;
|
||||
process.eventHandlers = {};
|
||||
if (!options?.script || options?.script === 'app.js') {
|
||||
process.app = new app.App();
|
||||
@ -509,6 +539,64 @@ async function getProcessBlob(blobId, key, options) {
|
||||
url: options?.url,
|
||||
},
|
||||
};
|
||||
process.sendIdentities = async function () {
|
||||
process.app.send(
|
||||
Object.assign(
|
||||
{
|
||||
action: 'identities',
|
||||
},
|
||||
await ssb.getIdentityInfo(
|
||||
process?.credentials?.session?.name,
|
||||
options?.packageOwner,
|
||||
options?.packageName
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
process.setActiveIdentity = async function (identity) {
|
||||
if (
|
||||
process?.credentials?.session?.name &&
|
||||
options.packageOwner &&
|
||||
options.packageName
|
||||
) {
|
||||
await new Database(process?.credentials?.session?.name).set(
|
||||
`id:${options.packageOwner}:${options.packageName}`,
|
||||
identity
|
||||
);
|
||||
}
|
||||
process.sendIdentities();
|
||||
broadcastAppEventToUser(
|
||||
process?.credentials?.session?.name,
|
||||
options.packageOwner,
|
||||
options.packageName,
|
||||
'setActiveIdentity',
|
||||
[identity]
|
||||
);
|
||||
};
|
||||
process.createIdentity = async function () {
|
||||
if (
|
||||
process.credentials &&
|
||||
process.credentials.session &&
|
||||
process.credentials.session.name
|
||||
) {
|
||||
let id = ssb.createIdentity(process.credentials.session.name);
|
||||
await process.sendIdentities();
|
||||
broadcastAppEventToUser(
|
||||
process?.credentials?.session?.name,
|
||||
options.packageOwner,
|
||||
options.packageName,
|
||||
'setActiveIdentity',
|
||||
[
|
||||
await ssb.getActiveIdentity(
|
||||
process.credentials?.session?.name,
|
||||
options.packageOwner,
|
||||
options.packageName
|
||||
),
|
||||
]
|
||||
);
|
||||
return id;
|
||||
}
|
||||
};
|
||||
if (process.credentials?.permissions?.administration) {
|
||||
imports.core.globalSettingsDescriptions = function () {
|
||||
let settings = Object.assign({}, k_global_settings);
|
||||
@ -579,15 +667,7 @@ async function getProcessBlob(blobId, key, options) {
|
||||
Object.keys(ssb).map((key) => [key, ssb[key].bind(ssb)])
|
||||
);
|
||||
imports.ssb.port = tildefriends.ssb_port;
|
||||
imports.ssb.createIdentity = function () {
|
||||
if (
|
||||
process.credentials &&
|
||||
process.credentials.session &&
|
||||
process.credentials.session.name
|
||||
) {
|
||||
return ssb.createIdentity(process.credentials.session.name);
|
||||
}
|
||||
};
|
||||
imports.ssb.createIdentity = () => process.createIdentity();
|
||||
imports.ssb.addIdentity = function (id) {
|
||||
if (
|
||||
process.credentials &&
|
||||
@ -614,6 +694,13 @@ async function getProcessBlob(blobId, key, options) {
|
||||
});
|
||||
}
|
||||
};
|
||||
imports.ssb.setActiveIdentity = (id) => process.setActiveIdentity(id);
|
||||
imports.ssb.getActiveIdentity = () =>
|
||||
ssb.getActiveIdentity(
|
||||
process.credentials?.session?.name,
|
||||
options.packageOwner,
|
||||
options.packageName
|
||||
);
|
||||
imports.ssb.getOwnerIdentities = function () {
|
||||
if (options.packageOwner) {
|
||||
return ssb.getIdentities(options.packageOwner);
|
||||
@ -698,6 +785,7 @@ async function getProcessBlob(blobId, key, options) {
|
||||
);
|
||||
}
|
||||
};
|
||||
imports.ssb.getIdentityInfo = undefined;
|
||||
imports.fetch = function (url, options) {
|
||||
return http.fetch(url, options, gGlobalSettings.fetch_hosts);
|
||||
};
|
||||
@ -967,7 +1055,7 @@ async function useAppHandler(
|
||||
},
|
||||
respond: do_resolve,
|
||||
},
|
||||
credentials: auth.query(headers),
|
||||
credentials: httpd.auth_query(headers),
|
||||
packageOwner: packageOwner,
|
||||
packageName: packageName,
|
||||
}
|
||||
@ -1098,7 +1186,7 @@ async function blobHandler(request, response, blobId, uri) {
|
||||
if ((match = /^\/\~(\w+)\/(\w+)$/.exec(blobId))) {
|
||||
let user = match[1];
|
||||
let appName = match[2];
|
||||
let credentials = auth.query(request.headers);
|
||||
let credentials = httpd.auth_query(request.headers);
|
||||
if (
|
||||
credentials &&
|
||||
credentials.session &&
|
||||
@ -1161,7 +1249,7 @@ async function blobHandler(request, response, blobId, uri) {
|
||||
if ((match = /^\/\~(\w+)\/(\w+)$/.exec(blobId))) {
|
||||
let user = match[1];
|
||||
let appName = match[2];
|
||||
let credentials = auth.query(request.headers);
|
||||
let credentials = httpd.auth_query(request.headers);
|
||||
if (
|
||||
credentials &&
|
||||
credentials.session &&
|
||||
@ -1334,39 +1422,10 @@ loadSettings()
|
||||
if (tildefriends.https_port && gGlobalSettings.http_redirect) {
|
||||
httpd.set_http_redirect(gGlobalSettings.http_redirect);
|
||||
}
|
||||
httpd.all('/login', auth.handler);
|
||||
httpd.all('/login/logout', auth.handler);
|
||||
httpd.all('/app/socket', app.socket);
|
||||
httpd.all('', function default_http_handler(request, response) {
|
||||
let match;
|
||||
if (request.uri === '/' || request.uri === '') {
|
||||
let host = request.headers['x-forwarded-host'] ?? request.headers.host;
|
||||
try {
|
||||
for (let line of (gGlobalSettings.index_map || '').split('\n')) {
|
||||
let parts = line.split('=');
|
||||
if (parts.length == 2 && host.match(new RegExp(parts[0], 'i'))) {
|
||||
response.writeHead(303, {
|
||||
Location:
|
||||
(request.client.tls ? 'https://' : 'http://') +
|
||||
host +
|
||||
parts[1],
|
||||
'Content-Length': '0',
|
||||
});
|
||||
return response.end();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
response.writeHead(303, {
|
||||
Location:
|
||||
(request.client.tls ? 'https://' : 'http://') +
|
||||
host +
|
||||
gGlobalSettings.index,
|
||||
'Content-Length': '0',
|
||||
});
|
||||
return response.end();
|
||||
} else if ((match = /^(\/~[^\/]+\/[^\/]+)(\/?.*)$/.exec(request.uri))) {
|
||||
if ((match = /^(\/~[^\/]+\/[^\/]+)(\/?.*)$/.exec(request.uri))) {
|
||||
return blobHandler(request, response, match[1], match[2]);
|
||||
} else if (
|
||||
(match = /^\/([&\%][^\.]{44}(?:\.\w+)?)(\/?.*)/.exec(request.uri))
|
||||
@ -1406,8 +1465,15 @@ loadSettings()
|
||||
async function start_tls() {
|
||||
const kCertificatePath = 'data/httpd/certificate.pem';
|
||||
const kPrivateKeyPath = 'data/httpd/privatekey.pem';
|
||||
let privateKey = utf8Decode(await File.readFile(kPrivateKeyPath));
|
||||
let certificate = utf8Decode(await File.readFile(kCertificatePath));
|
||||
let privateKey;
|
||||
let certificate;
|
||||
try {
|
||||
privateKey = utf8Decode(await File.readFile(kPrivateKeyPath));
|
||||
certificate = utf8Decode(await File.readFile(kCertificatePath));
|
||||
} catch (e) {
|
||||
print(`TLS disabled (${e.message}).`);
|
||||
return;
|
||||
}
|
||||
let context = new TlsContext();
|
||||
context.setPrivateKey(privateKey);
|
||||
context.setCertificate(certificate);
|
||||
|
@ -15,22 +15,6 @@ body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a:link {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #6c71c4;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #859900;
|
||||
}
|
||||
|
||||
a:active {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
#logo {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Tilde Friends core stylesheet
|
||||
*
|
||||
* This Software is an external library that is part of
|
||||
* Tilde Friends and is shared under the MIT license.
|
||||
*
|
||||
* Inject this file in your app at tildefriends.css
|
||||
* and use this tag to import it:
|
||||
* <link rel="stylesheet" href="/static/tildefriends-v1.css"/>
|
||||
*
|
||||
* v1.0.0 / 2024 M03 21
|
||||
*/
|
||||
|
||||
body {
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
button,
|
||||
.button,
|
||||
input[type=button],
|
||||
input[type=submit],
|
||||
select {
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 8px 12px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
margin: 4px;
|
||||
|
||||
&.red {
|
||||
background-color: #bd1e24;
|
||||
color: white;
|
||||
}
|
||||
|
||||
&.green {
|
||||
background-color: #18922d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
&.blue {
|
||||
background-color: #0067a7;
|
||||
color: white;
|
||||
}
|
||||
|
||||
&.yellow {
|
||||
background-color: #ee9600;
|
||||
color: black;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
filter: brightness(0.75);
|
||||
}
|
||||
}
|
||||
|
||||
a:link {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #6c71c4;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #859900;
|
||||
}
|
||||
|
||||
a:active {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
td, th {
|
||||
border: 1px solid #ffffff40;
|
||||
text-align: left;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background-color: #ffffff20;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.inline-flex-row {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.box {
|
||||
background-color: #00000020;
|
||||
border: 1px solid grey;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin: 4px;
|
||||
}
|
2
deps/codemirror/cm6.js
vendored
2
deps/codemirror/cm6.js
vendored
File diff suppressed because one or more lines are too long
197
deps/codemirror_src/package-lock.json
generated
vendored
197
deps/codemirror_src/package-lock.json
generated
vendored
@ -19,9 +19,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/autocomplete": {
|
||||
"version": "6.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.15.0.tgz",
|
||||
"integrity": "sha512-G2Zm0mXznxz97JhaaOdoEG2cVupn4JjPaS4AcNvZzhOsnnG9YVN68VzfoUw6dYTsIxT6a/cmoFEN47KAWhXaOg==",
|
||||
"version": "6.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.16.0.tgz",
|
||||
"integrity": "sha512-P/LeCTtZHRTCU4xQsa89vSKWecYv1ZqwzOd5topheGRf+qtacFgBeIMQi3eL8Kt/BUNvxUWkx+5qP2jlGoARrg==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
@ -36,9 +36,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/commands": {
|
||||
"version": "6.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.3.3.tgz",
|
||||
"integrity": "sha512-dO4hcF0fGT9tu1Pj1D2PvGvxjeGkbC6RGcZw6Qs74TH+Ed1gw98jmUgd2axWvIZEqTeTuFrg1lEB1KV6cK9h1A==",
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.5.0.tgz",
|
||||
"integrity": "sha512-rK+sj4fCAN/QfcY9BEzYMgp4wwL/q5aj/VfNSoH1RWPF9XS/dUwBkvlL3hpWgEjOqlpdN1uLC9UkjJ4tmyjJYg==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.4.0",
|
||||
@ -59,9 +59,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lang-html": {
|
||||
"version": "6.4.8",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.8.tgz",
|
||||
"integrity": "sha512-tE2YK7wDlb9ZpAH6mpTPiYm6rhfdQKVDa5r9IwIFlwwgvVaKsCfuKKZoJGWsmMZIf3FQAuJ5CHMPLymOtg1hXw==",
|
||||
"version": "6.4.9",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.9.tgz",
|
||||
"integrity": "sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/lang-css": "^6.0.0",
|
||||
@ -111,9 +111,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lint": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.5.0.tgz",
|
||||
"integrity": "sha512-+5YyicIaaAZKU8K43IQi8TBy6mF6giGeWAH7N96Z5LC30Wm5JMjqxOYIE9mxwMG1NbhT2mA3l9hA4uuKUM3E5g==",
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.7.0.tgz",
|
||||
"integrity": "sha512-LTLOL2nT41ADNSCCCCw8Q/UmdAFzB23OUYSjsHTdsVaH0XEo+orhuqbDNWzrzodm14w6FOxqxpmy4LF8Lixqjw==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
@ -147,9 +147,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/view": {
|
||||
"version": "6.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.25.1.tgz",
|
||||
"integrity": "sha512-2LXLxsQnHDdfGzDvjzAwZh2ZviNJm7im6tGpa0IONIDnFd8RZ80D2SNi8PDi6YjKcMoMRK20v6OmKIdsrwsyoQ==",
|
||||
"version": "6.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.3.tgz",
|
||||
"integrity": "sha512-gmqxkPALZjkgSxIeeweY/wGQXBfwTUaLs8h7OKtSwfbj9Ct3L11lD+u1sS7XHppxFQoMDiMDp07P9f3I2jWOHw==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.4.0",
|
||||
"style-mod": "^4.1.0",
|
||||
@ -248,9 +248,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/javascript": {
|
||||
"version": "1.4.13",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.13.tgz",
|
||||
"integrity": "sha512-5IBr8LIO3xJdJH1e9aj/ZNLE4LSbdsx25wFmGRAZsj2zSmwAYjx26JyU/BYOCpRQlu1jcv1z3vy4NB9+UkfRow==",
|
||||
"version": "1.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.15.tgz",
|
||||
"integrity": "sha512-B082ZdjI0vo2AgLqD834GlRTE9gwRX8NzHzKq5uDwEnQ9Dq+A/CEhd3nf68tiNA2f9O+8jS1NeSTUYT9IAqcTw==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.2.0",
|
||||
"@lezer/highlight": "^1.1.3",
|
||||
@ -343,9 +343,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz",
|
||||
"integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz",
|
||||
"integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@ -355,9 +355,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz",
|
||||
"integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz",
|
||||
"integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -367,9 +367,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz",
|
||||
"integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz",
|
||||
"integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -379,9 +379,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz",
|
||||
"integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz",
|
||||
"integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -391,9 +391,21 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz",
|
||||
"integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz",
|
||||
"integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz",
|
||||
"integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@ -403,9 +415,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz",
|
||||
"integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz",
|
||||
"integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -415,9 +427,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz",
|
||||
"integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz",
|
||||
"integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -426,10 +438,22 @@
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz",
|
||||
"integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz",
|
||||
"integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz",
|
||||
"integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@ -438,10 +462,22 @@
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz",
|
||||
"integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz",
|
||||
"integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz",
|
||||
"integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -451,9 +487,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz",
|
||||
"integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz",
|
||||
"integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -463,9 +499,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz",
|
||||
"integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz",
|
||||
"integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -475,9 +511,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz",
|
||||
"integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz",
|
||||
"integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@ -487,9 +523,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz",
|
||||
"integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz",
|
||||
"integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -679,9 +715,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz",
|
||||
"integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz",
|
||||
"integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==",
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.5"
|
||||
},
|
||||
@ -693,19 +729,22 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.13.0",
|
||||
"@rollup/rollup-android-arm64": "4.13.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.13.0",
|
||||
"@rollup/rollup-darwin-x64": "4.13.0",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.13.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.13.0",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.13.0",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.13.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.13.0",
|
||||
"@rollup/rollup-linux-x64-musl": "4.13.0",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.13.0",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.13.0",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.13.0",
|
||||
"@rollup/rollup-android-arm-eabi": "4.17.2",
|
||||
"@rollup/rollup-android-arm64": "4.17.2",
|
||||
"@rollup/rollup-darwin-arm64": "4.17.2",
|
||||
"@rollup/rollup-darwin-x64": "4.17.2",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.17.2",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.17.2",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.17.2",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.17.2",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.17.2",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.17.2",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.17.2",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.17.2",
|
||||
"@rollup/rollup-linux-x64-musl": "4.17.2",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.17.2",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.17.2",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.17.2",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
@ -739,9 +778,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/smob": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/smob/-/smob-1.4.1.tgz",
|
||||
"integrity": "sha512-9LK+E7Hv5R9u4g4C3p+jjLstaLe11MDsL21UpYaCNmapvMkYhqCV4A/f/3gyH8QjMyh6l68q9xC85vihY9ahMQ==",
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz",
|
||||
"integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
@ -780,9 +819,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.29.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.29.1.tgz",
|
||||
"integrity": "sha512-lZQ/fyaIGxsbGxApKmoPTODIzELy3++mXhS5hOqaAWZjQtpq/hFHAc+rm29NND1rYRxRWKcjuARNwULNXa5RtQ==",
|
||||
"version": "5.31.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.31.0.tgz",
|
||||
"integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
|
1
deps/crypt_blowfish
vendored
Submodule
1
deps/crypt_blowfish
vendored
Submodule
Submodule deps/crypt_blowfish added at 3354bb81ee
29
deps/crypt_blowfish/LINKS
vendored
29
deps/crypt_blowfish/LINKS
vendored
@ -1,29 +0,0 @@
|
||||
New versions of this package (crypt_blowfish):
|
||||
|
||||
http://www.openwall.com/crypt/
|
||||
|
||||
A paper on the algorithm that explains its design decisions:
|
||||
|
||||
http://www.usenix.org/events/usenix99/provos.html
|
||||
|
||||
Unix Seventh Edition Manual, Volume 2: the password scheme (1978):
|
||||
|
||||
http://plan9.bell-labs.com/7thEdMan/vol2/password
|
||||
|
||||
The Openwall GNU/*/Linux (Owl) tcb suite implementing the alternative
|
||||
password shadowing scheme. This includes a PAM module which
|
||||
supersedes pam_unix and uses the password hashing framework provided
|
||||
with crypt_blowfish when setting new passwords.
|
||||
|
||||
http://www.openwall.com/tcb/
|
||||
|
||||
pam_passwdqc, a password strength checking and policy enforcement
|
||||
module for PAM-aware password changing programs:
|
||||
|
||||
http://www.openwall.com/passwdqc/
|
||||
|
||||
John the Ripper password cracker:
|
||||
|
||||
http://www.openwall.com/john/
|
||||
|
||||
$Owl: Owl/packages/glibc/crypt_blowfish/LINKS,v 1.4 2005/11/16 13:09:47 solar Exp $
|
77
deps/crypt_blowfish/Makefile
vendored
77
deps/crypt_blowfish/Makefile
vendored
@ -1,77 +0,0 @@
|
||||
#
|
||||
# Written and revised by Solar Designer <solar at openwall.com> in 2000-2011.
|
||||
# No copyright is claimed, and the software is hereby placed in the public
|
||||
# domain. In case this attempt to disclaim copyright and place the software
|
||||
# in the public domain is deemed null and void, then the software is
|
||||
# Copyright (c) 2000-2011 Solar Designer and it is hereby released to the
|
||||
# general public under the following terms:
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted.
|
||||
#
|
||||
# There's ABSOLUTELY NO WARRANTY, express or implied.
|
||||
#
|
||||
# See crypt_blowfish.c for more information.
|
||||
#
|
||||
|
||||
CC = gcc
|
||||
AS = $(CC)
|
||||
LD = $(CC)
|
||||
RM = rm -f
|
||||
CFLAGS = -W -Wall -Wbad-function-cast -Wcast-align -Wcast-qual -Wmissing-prototypes -Wstrict-prototypes -Wshadow -Wundef -Wpointer-arith -O2 -fomit-frame-pointer -funroll-loops
|
||||
ASFLAGS = -c
|
||||
LDFLAGS = -s
|
||||
|
||||
BLOWFISH_OBJS = \
|
||||
crypt_blowfish.o x86.o
|
||||
|
||||
CRYPT_OBJS = \
|
||||
$(BLOWFISH_OBJS) crypt_gensalt.o wrapper.o
|
||||
|
||||
TEST_OBJS = \
|
||||
$(BLOWFISH_OBJS) crypt_gensalt.o crypt_test.o
|
||||
|
||||
TEST_THREADS_OBJS = \
|
||||
$(BLOWFISH_OBJS) crypt_gensalt.o crypt_test_threads.o
|
||||
|
||||
EXTRA_MANS = \
|
||||
crypt_r.3 crypt_rn.3 crypt_ra.3 \
|
||||
crypt_gensalt.3 crypt_gensalt_rn.3 crypt_gensalt_ra.3
|
||||
|
||||
all: $(CRYPT_OBJS) man
|
||||
|
||||
check: crypt_test
|
||||
./crypt_test
|
||||
|
||||
crypt_test: $(TEST_OBJS)
|
||||
$(LD) $(LDFLAGS) $(TEST_OBJS) -o $@
|
||||
|
||||
crypt_test.o: wrapper.c ow-crypt.h crypt_blowfish.h crypt_gensalt.h
|
||||
$(CC) -c $(CFLAGS) wrapper.c -DTEST -o $@
|
||||
|
||||
check_threads: crypt_test_threads
|
||||
./crypt_test_threads
|
||||
|
||||
crypt_test_threads: $(TEST_THREADS_OBJS)
|
||||
$(LD) $(LDFLAGS) $(TEST_THREADS_OBJS) -lpthread -o $@
|
||||
|
||||
crypt_test_threads.o: wrapper.c ow-crypt.h crypt_blowfish.h crypt_gensalt.h
|
||||
$(CC) -c $(CFLAGS) wrapper.c -DTEST -DTEST_THREADS=4 -o $@
|
||||
|
||||
man: $(EXTRA_MANS)
|
||||
|
||||
$(EXTRA_MANS):
|
||||
echo '.so man3/crypt.3' > $@
|
||||
|
||||
crypt_blowfish.o: crypt_blowfish.h
|
||||
crypt_gensalt.o: crypt_gensalt.h
|
||||
wrapper.o: crypt.h ow-crypt.h crypt_blowfish.h crypt_gensalt.h
|
||||
|
||||
.c.o:
|
||||
$(CC) -c $(CFLAGS) $*.c
|
||||
|
||||
.S.o:
|
||||
$(AS) $(ASFLAGS) $*.S
|
||||
|
||||
clean:
|
||||
$(RM) crypt_test crypt_test_threads *.o $(EXTRA_MANS) core
|
30
deps/crypt_blowfish/PERFORMANCE
vendored
30
deps/crypt_blowfish/PERFORMANCE
vendored
@ -1,30 +0,0 @@
|
||||
These numbers are for 32 iterations ("$2a$05"):
|
||||
|
||||
OpenBSD 3.0 bcrypt(*) crypt_blowfish 0.4.4
|
||||
Pentium III, 840 MHz 99 c/s 121 c/s (+22%)
|
||||
Alpha 21164PC, 533 MHz 55.5 c/s 76.9 c/s (+38%)
|
||||
UltraSparc IIi, 400 MHz 49.9 c/s 52.5 c/s (+5%)
|
||||
Pentium, 120 MHz 8.8 c/s 20.1 c/s (+128%)
|
||||
PA-RISC 7100LC, 80 MHz 8.5 c/s 16.3 c/s (+92%)
|
||||
|
||||
(*) built with -fomit-frame-pointer -funroll-loops, which I don't
|
||||
think happens for libcrypt.
|
||||
|
||||
Starting with version 1.1 released in June 2011, default builds of
|
||||
crypt_blowfish invoke a quick self-test on every hash computation.
|
||||
This has roughly a 4.8% performance impact at "$2a$05", but only a 0.6%
|
||||
impact at a more typical setting of "$2a$08".
|
||||
|
||||
The large speedup for the original Pentium is due to the assembly
|
||||
code and the weird optimizations this processor requires.
|
||||
|
||||
The numbers for password cracking are 2 to 10% higher than those for
|
||||
crypt_blowfish as certain things may be done out of the loop and the
|
||||
code doesn't need to be reentrant.
|
||||
|
||||
Recent versions of John the Ripper (1.6.25-dev and newer) achieve an
|
||||
additional 15% speedup on the Pentium Pro family of processors (which
|
||||
includes Pentium III) with a separate version of the assembly code and
|
||||
run-time CPU detection.
|
||||
|
||||
$Owl: Owl/packages/glibc/crypt_blowfish/PERFORMANCE,v 1.6 2011/06/21 12:09:20 solar Exp $
|
68
deps/crypt_blowfish/README
vendored
68
deps/crypt_blowfish/README
vendored
@ -1,68 +0,0 @@
|
||||
This is an implementation of a password hashing method, provided via the
|
||||
crypt(3) and a reentrant interface. It is fully compatible with
|
||||
OpenBSD's bcrypt.c for prefix "$2b$", originally by Niels Provos and
|
||||
David Mazieres. (Please refer to the included crypt(3) man page for
|
||||
information on minor compatibility issues for other bcrypt prefixes.)
|
||||
|
||||
I've placed this code in the public domain, with fallback to a
|
||||
permissive license. Please see the comment in crypt_blowfish.c for
|
||||
more information.
|
||||
|
||||
You can use the provided routines in your own packages, or link them
|
||||
into a C library. I've provided hooks for linking into GNU libc, but
|
||||
it shouldn't be too hard to get this into another C library. Note
|
||||
that simply adding this code into your libc is probably not enough to
|
||||
make your system use the new password hashing algorithm. Changes to
|
||||
passwd(1), PAM modules, or whatever else your system uses will likely
|
||||
be needed as well. These are not a part of this package, but see
|
||||
LINKS for a pointer to our tcb suite.
|
||||
|
||||
Instructions on using the routines in one of the two common ways are
|
||||
given below. It is recommended that you test the routines on your
|
||||
system before you start. Type "make check" or "make check_threads"
|
||||
(if you have the POSIX threads library), then "make clean".
|
||||
|
||||
|
||||
1. Using the routines in your programs.
|
||||
|
||||
The available interfaces are in ow-crypt.h, and this is the file you
|
||||
should include. You won't need crypt.h. When linking, add all of the
|
||||
C files and x86.S (you can compile and link it even on a non-x86, it
|
||||
will produce no code in this case).
|
||||
|
||||
|
||||
2. Building the routines into GNU C library.
|
||||
|
||||
For versions 2.13 and 2.14 (and likely other nearby ones), extract the
|
||||
library sources as usual. Apply the patch for glibc 2.14 provided in
|
||||
this package. Enter crypt/ and rename crypt.h to gnu-crypt.h within
|
||||
that directory. Copy the C sources, header, and assembly (x86.S) files
|
||||
from this package in there as well (but be sure you don't overwrite the
|
||||
Makefile). Configure, build, and install the library as usual.
|
||||
|
||||
For versions 2.2 to 2.3.6 (and likely also for some newer ones),
|
||||
extract the library sources and maybe its optional add-ons as usual.
|
||||
Apply the patch for glibc 2.3.6 provided in this package. Enter
|
||||
crypt/ and rename crypt.h to gnu-crypt.h within that directory. Copy
|
||||
the C sources, header, and assembly (x86.S) files from this package in
|
||||
there as well (but be sure you don't overwrite the Makefile).
|
||||
Configure, build, and install the library as usual.
|
||||
|
||||
For versions 2.1 to 2.1.3, extract the library sources and the crypt
|
||||
and linuxthreads add-ons as usual. Apply the patch for glibc 2.1.3
|
||||
provided in this package. Enter crypt/sysdeps/unix/, and rename
|
||||
crypt.h to gnu-crypt.h within that directory. Copy C sources, header,
|
||||
and assembly (x86.S) files from this package in there as well (but be
|
||||
sure you don't overwrite the Makefile). Configure, build, and install
|
||||
the library as usual.
|
||||
|
||||
Programs that want to use the provided interfaces will need to include
|
||||
crypt.h (but not ow-crypt.h directly). By default, prototypes for the
|
||||
new routines aren't defined (but the extra functionality of crypt(3)
|
||||
is indeed available). You need to define _OW_SOURCE to obtain the new
|
||||
routines as well.
|
||||
|
||||
--
|
||||
Solar Designer <solar at openwall.com>
|
||||
|
||||
$Owl: Owl/packages/glibc/crypt_blowfish/README,v 1.10 2014/07/07 15:19:04 solar Exp $
|
575
deps/crypt_blowfish/crypt.3
vendored
575
deps/crypt_blowfish/crypt.3
vendored
@ -1,575 +0,0 @@
|
||||
.\" Written and revised by Solar Designer <solar at openwall.com> in 2000-2011.
|
||||
.\" No copyright is claimed, and this man page is hereby placed in the public
|
||||
.\" domain. In case this attempt to disclaim copyright and place the man page
|
||||
.\" in the public domain is deemed null and void, then the man page is
|
||||
.\" Copyright (c) 2000-2011 Solar Designer and it is hereby released to the
|
||||
.\" general public under the following terms:
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted.
|
||||
.\"
|
||||
.\" There's ABSOLUTELY NO WARRANTY, express or implied.
|
||||
.\"
|
||||
.\" This manual page in its current form is intended for use on systems
|
||||
.\" based on the GNU C Library with crypt_blowfish patched into libcrypt.
|
||||
.\"
|
||||
.TH CRYPT 3 "July 7, 2014" "Openwall Project" "Library functions"
|
||||
.ad l
|
||||
.\" No macros in NAME to keep makewhatis happy.
|
||||
.SH NAME
|
||||
\fBcrypt\fR, \fBcrypt_r\fR, \fBcrypt_rn\fR, \fBcrypt_ra\fR,
|
||||
\fBcrypt_gensalt\fR, \fBcrypt_gensalt_rn\fR, \fBcrypt_gensalt_ra\fR
|
||||
\- password hashing
|
||||
.SH SYNOPSIS
|
||||
.B #define _XOPEN_SOURCE
|
||||
.br
|
||||
.B #include <unistd.h>
|
||||
.sp
|
||||
.in +8
|
||||
.ti -8
|
||||
.BI "char *crypt(const char *" key ", const char *" setting );
|
||||
.in -8
|
||||
.sp
|
||||
.B #define _GNU_SOURCE
|
||||
.br
|
||||
.B #include <crypt.h>
|
||||
.sp
|
||||
.in +8
|
||||
.ti -8
|
||||
.BI "char *crypt_r(const char *" key ", const char *" setting ", struct crypt_data *" data );
|
||||
.in -8
|
||||
.sp
|
||||
.B #define _OW_SOURCE
|
||||
.br
|
||||
.B #include <crypt.h>
|
||||
.sp
|
||||
.in +8
|
||||
.ti -8
|
||||
.BI "char *crypt_rn(const char *" key ", const char *" setting ", void *" data ", int " size );
|
||||
.ti -8
|
||||
.BI "char *crypt_ra(const char *" key ", const char *" setting ", void **" data ", int *" size );
|
||||
.ti -8
|
||||
.BI "char *crypt_gensalt(const char *" prefix ", unsigned long " count ", const char *" input ", int " size );
|
||||
.ti -8
|
||||
.BI "char *crypt_gensalt_rn(const char *" prefix ", unsigned long " count ", const char *" input ", int " size ", char *" output ", int " output_size );
|
||||
.ti -8
|
||||
.BI "char *crypt_gensalt_ra(const char *" prefix ", unsigned long " count ", const char *" input ", int " size );
|
||||
.ad b
|
||||
.de crypt
|
||||
.BR crypt ,
|
||||
.BR crypt_r ,
|
||||
.BR crypt_rn ", \\$1"
|
||||
.ie "\\$2"" .B crypt_ra
|
||||
.el .BR crypt_ra "\\$2"
|
||||
..
|
||||
.de crypt_gensalt
|
||||
.BR crypt_gensalt ,
|
||||
.BR crypt_gensalt_rn ", \\$1"
|
||||
.ie "\\$2"" .B crypt_gensalt_ra
|
||||
.el .BR crypt_gensalt_ra "\\$2"
|
||||
..
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.crypt and
|
||||
functions calculate a cryptographic hash function of
|
||||
.I key
|
||||
with one of a number of supported methods as requested with
|
||||
.IR setting ,
|
||||
which is also used to pass a salt and possibly other parameters to
|
||||
the chosen method.
|
||||
The hashing methods are explained below.
|
||||
.PP
|
||||
Unlike
|
||||
.BR crypt ,
|
||||
the functions
|
||||
.BR crypt_r ,
|
||||
.BR crypt_rn " and"
|
||||
.B crypt_ra
|
||||
are reentrant.
|
||||
They place their result and possibly their private data in a
|
||||
.I data
|
||||
area of
|
||||
.I size
|
||||
bytes as passed to them by an application and/or in memory they
|
||||
allocate dynamically. Some hashing algorithms may use the data area to
|
||||
cache precomputed intermediate values across calls. Thus, applications
|
||||
must properly initialize the data area before its first use.
|
||||
.B crypt_r
|
||||
requires that only
|
||||
.I data->initialized
|
||||
be reset to zero;
|
||||
.BR crypt_rn " and " crypt_ra
|
||||
require that either the entire data area is zeroed or, in the case of
|
||||
.BR crypt_ra ,
|
||||
.I *data
|
||||
is NULL. When called with a NULL
|
||||
.I *data
|
||||
or insufficient
|
||||
.I *size
|
||||
for the requested hashing algorithm,
|
||||
.B crypt_ra
|
||||
uses
|
||||
.BR realloc (3)
|
||||
to allocate the required amount of memory dynamically. Thus,
|
||||
.B crypt_ra
|
||||
has the additional requirement that
|
||||
.IR *data ,
|
||||
when non-NULL, must point to an area allocated either with a previous
|
||||
call to
|
||||
.B crypt_ra
|
||||
or with a
|
||||
.BR malloc (3)
|
||||
family call.
|
||||
The memory allocated by
|
||||
.B crypt_ra
|
||||
should be freed with
|
||||
.BR free "(3)."
|
||||
.PP
|
||||
The
|
||||
.crypt_gensalt and
|
||||
functions compile a string for use as
|
||||
.I setting
|
||||
\- with the given
|
||||
.I prefix
|
||||
(used to choose a hashing method), the iteration
|
||||
.I count
|
||||
(if supported by the chosen method) and up to
|
||||
.I size
|
||||
cryptographically random
|
||||
.I input
|
||||
bytes for use as the actual salt.
|
||||
If
|
||||
.I count
|
||||
is 0, a low default will be picked.
|
||||
The random bytes may be obtained from
|
||||
.BR /dev/urandom .
|
||||
Unlike
|
||||
.BR crypt_gensalt ,
|
||||
the functions
|
||||
.BR crypt_gensalt_rn " and " crypt_gensalt_ra
|
||||
are reentrant.
|
||||
.B crypt_gensalt_rn
|
||||
places its result in the
|
||||
.I output
|
||||
buffer of
|
||||
.I output_size
|
||||
bytes.
|
||||
.B crypt_gensalt_ra
|
||||
allocates memory for its result dynamically. The memory should be
|
||||
freed with
|
||||
.BR free "(3)."
|
||||
.SH RETURN VALUE
|
||||
Upon successful completion, the functions
|
||||
.crypt and
|
||||
return a pointer to a string containing the setting that was actually used
|
||||
and a printable encoding of the hash function value.
|
||||
The entire string is directly usable as
|
||||
.I setting
|
||||
with other calls to
|
||||
.crypt and
|
||||
and as
|
||||
.I prefix
|
||||
with calls to
|
||||
.crypt_gensalt and .
|
||||
.PP
|
||||
The behavior of
|
||||
.B crypt
|
||||
on errors isn't well standardized. Some implementations simply can't fail
|
||||
(unless the process dies, in which case they obviously can't return),
|
||||
others return NULL or a fixed string. Most implementations don't set
|
||||
.IR errno ,
|
||||
but some do. SUSv2 specifies only returning NULL and setting
|
||||
.I errno
|
||||
as a valid behavior, and defines only one possible error
|
||||
.RB "(" ENOSYS ,
|
||||
"The functionality is not supported on this implementation.")
|
||||
Unfortunately, most existing applications aren't prepared to handle
|
||||
NULL returns from
|
||||
.BR crypt .
|
||||
The description below corresponds to this implementation of
|
||||
.BR crypt " and " crypt_r
|
||||
only, and to
|
||||
.BR crypt_rn " and " crypt_ra .
|
||||
The behavior may change to match standards, other implementations or
|
||||
existing applications.
|
||||
.PP
|
||||
.BR crypt " and " crypt_r
|
||||
may only fail (and return) when passed an invalid or unsupported
|
||||
.IR setting ,
|
||||
in which case they return a pointer to a magic string that is
|
||||
shorter than 13 characters and is guaranteed to differ from
|
||||
.IR setting .
|
||||
This behavior is safe for older applications which assume that
|
||||
.B crypt
|
||||
can't fail, when both setting new passwords and authenticating against
|
||||
existing password hashes.
|
||||
.BR crypt_rn " and " crypt_ra
|
||||
return NULL to indicate failure. All four functions set
|
||||
.I errno
|
||||
when they fail.
|
||||
.PP
|
||||
The functions
|
||||
.crypt_gensalt and
|
||||
return a pointer to the compiled string for
|
||||
.IR setting ,
|
||||
or NULL on error in which case
|
||||
.I errno
|
||||
is set.
|
||||
.SH ERRORS
|
||||
.TP
|
||||
.B EINVAL
|
||||
.crypt "" :
|
||||
.I setting
|
||||
is invalid or not supported by this implementation;
|
||||
.sp
|
||||
.crypt_gensalt "" :
|
||||
.I prefix
|
||||
is invalid or not supported by this implementation;
|
||||
.I count
|
||||
is invalid for the requested
|
||||
.IR prefix ;
|
||||
the input
|
||||
.I size
|
||||
is insufficient for the smallest valid salt with the requested
|
||||
.IR prefix ;
|
||||
.I input
|
||||
is NULL.
|
||||
.TP
|
||||
.B ERANGE
|
||||
.BR crypt_rn :
|
||||
the provided data area
|
||||
.I size
|
||||
is insufficient for the requested hashing algorithm;
|
||||
.sp
|
||||
.BR crypt_gensalt_rn :
|
||||
.I output_size
|
||||
is too small to hold the compiled
|
||||
.I setting
|
||||
string.
|
||||
.TP
|
||||
.B ENOMEM
|
||||
.B crypt
|
||||
(original glibc only):
|
||||
failed to allocate memory for the output buffer (which subsequent calls
|
||||
would re-use);
|
||||
.sp
|
||||
.BR crypt_ra :
|
||||
.I *data
|
||||
is NULL or
|
||||
.I *size
|
||||
is insufficient for the requested hashing algorithm and
|
||||
.BR realloc (3)
|
||||
failed;
|
||||
.sp
|
||||
.BR crypt_gensalt_ra :
|
||||
failed to allocate memory for the compiled
|
||||
.I setting
|
||||
string.
|
||||
.TP
|
||||
.B ENOSYS
|
||||
.B crypt
|
||||
(SUSv2):
|
||||
the functionality is not supported on this implementation;
|
||||
.sp
|
||||
.BR crypt ,
|
||||
.B crypt_r
|
||||
(glibc 2.0 to 2.0.1 only):
|
||||
.de no-crypt-add-on
|
||||
the crypt add-on is not compiled in and
|
||||
.I setting
|
||||
requests something other than the MD5-based algorithm.
|
||||
..
|
||||
.no-crypt-add-on
|
||||
.TP
|
||||
.B EOPNOTSUPP
|
||||
.BR crypt ,
|
||||
.B crypt_r
|
||||
(glibc 2.0.2 to 2.1.3 only):
|
||||
.no-crypt-add-on
|
||||
.SH HASHING METHODS
|
||||
The implemented hashing methods are intended specifically for processing
|
||||
user passwords for storage and authentication;
|
||||
they are at best inefficient for most other purposes.
|
||||
.PP
|
||||
It is important to understand that password hashing is not a replacement
|
||||
for strong passwords.
|
||||
It is always possible for an attacker with access to password hashes
|
||||
to try guessing candidate passwords against the hashes.
|
||||
There are, however, certain properties a password hashing method may have
|
||||
which make these key search attacks somewhat harder.
|
||||
.PP
|
||||
All of the hashing methods use salts such that the same
|
||||
.I key
|
||||
may produce many possible hashes.
|
||||
Proper use of salts may defeat a number of attacks, including:
|
||||
.TP
|
||||
1.
|
||||
The ability to try candidate passwords against multiple hashes at the
|
||||
price of one.
|
||||
.TP
|
||||
2.
|
||||
The use of pre-hashed lists of candidate passwords.
|
||||
.TP
|
||||
3.
|
||||
The ability to determine whether two users (or two accounts of one user)
|
||||
have the same or different passwords without actually having to guess
|
||||
one of the passwords.
|
||||
.PP
|
||||
The key search attacks depend on computing hashes of large numbers of
|
||||
candidate passwords.
|
||||
Thus, the computational cost of a good password hashing method must be
|
||||
high \- but of course not too high to render it impractical.
|
||||
.PP
|
||||
All hashing methods implemented within the
|
||||
.crypt and
|
||||
interfaces use multiple iterations of an underlying cryptographic
|
||||
primitive specifically in order to increase the cost of trying a
|
||||
candidate password.
|
||||
Unfortunately, due to hardware improvements, the hashing methods which
|
||||
have a fixed cost become increasingly less secure over time.
|
||||
.PP
|
||||
In addition to salts, modern password hashing methods accept a variable
|
||||
iteration
|
||||
.IR count .
|
||||
This makes it possible to adapt their cost to the hardware improvements
|
||||
while still maintaining compatibility.
|
||||
.PP
|
||||
The following hashing methods are or may be implemented within the
|
||||
described interfaces:
|
||||
.PP
|
||||
.de hash
|
||||
.ad l
|
||||
.TP
|
||||
.I prefix
|
||||
.ie "\\$1"" \{\
|
||||
"" (empty string);
|
||||
.br
|
||||
a string matching ^[./0-9A-Za-z]{2} (see
|
||||
.BR regex (7))
|
||||
.\}
|
||||
.el "\\$1"
|
||||
.TP
|
||||
.B Encoding syntax
|
||||
\\$2
|
||||
.TP
|
||||
.B Maximum password length
|
||||
\\$3 (uses \\$4-bit characters)
|
||||
.TP
|
||||
.B Effective key size
|
||||
.ie "\\$5"" limited by the hash size only
|
||||
.el up to \\$5 bits
|
||||
.TP
|
||||
.B Hash size
|
||||
\\$6 bits
|
||||
.TP
|
||||
.B Salt size
|
||||
\\$7 bits
|
||||
.TP
|
||||
.B Iteration count
|
||||
\\$8
|
||||
.ad b
|
||||
..
|
||||
.ti -2
|
||||
.B Traditional DES-based
|
||||
.br
|
||||
This method is supported by almost all implementations of
|
||||
.BR crypt .
|
||||
Unfortunately, it no longer offers adequate security because of its many
|
||||
limitations.
|
||||
Thus, it should not be used for new passwords unless you absolutely have
|
||||
to be able to migrate the password hashes to other systems.
|
||||
.hash "" "[./0-9A-Za-z]{13}" 8 7 56 64 12 25
|
||||
.PP
|
||||
.ti -2
|
||||
.B Extended BSDI-style DES-based
|
||||
.br
|
||||
This method is used on BSDI and is also available on at least NetBSD,
|
||||
OpenBSD, and FreeBSD due to the use of David Burren's FreeSec library.
|
||||
.hash _ "_[./0-9A-Za-z]{19}" unlimited 7 56 64 24 "1 to 2**24-1 (must be odd)"
|
||||
.PP
|
||||
.ti -2
|
||||
.B FreeBSD-style MD5-based
|
||||
.br
|
||||
This is Poul-Henning Kamp's MD5-based password hashing method originally
|
||||
developed for FreeBSD.
|
||||
It is currently supported on many free Unix-like systems, on Solaris 10
|
||||
and newer, and it is part of the official glibc.
|
||||
Its main disadvantage is the fixed iteration count, which is already
|
||||
too low for the currently available hardware.
|
||||
.hash "$1$" "\e$1\e$[^$]{1,8}\e$[./0-9A-Za-z]{22}" unlimited 8 "" 128 "6 to 48" 1000
|
||||
.PP
|
||||
.ti -2
|
||||
.BR "OpenBSD-style Blowfish-based" " (" bcrypt )
|
||||
.br
|
||||
.B bcrypt
|
||||
was originally developed by Niels Provos and David Mazieres for OpenBSD
|
||||
and is also supported on recent versions of FreeBSD and NetBSD,
|
||||
on Solaris 10 and newer, and on several GNU/*/Linux distributions.
|
||||
It is, however, not part of the official glibc.
|
||||
.PP
|
||||
While both
|
||||
.B bcrypt
|
||||
and the BSDI-style DES-based hashing offer a variable iteration count,
|
||||
.B bcrypt
|
||||
may scale to even faster hardware, doesn't allow for certain optimizations
|
||||
specific to password cracking only, doesn't have the effective key size
|
||||
limitation, and uses 8-bit characters in passwords.
|
||||
.hash "$2b$" "\e$2[abxy]\e$[0-9]{2}\e$[./A-Za-z0-9]{53}" 72 8 "" 184 128 "2**4 to 2**99 (current implementations are limited to 2**31 iterations)"
|
||||
.PP
|
||||
With
|
||||
.BR bcrypt ,
|
||||
the
|
||||
.I count
|
||||
passed to
|
||||
.crypt_gensalt and
|
||||
is the base-2 logarithm of the actual iteration count.
|
||||
.PP
|
||||
.B bcrypt
|
||||
hashes used the "$2a$" prefix since 1997.
|
||||
However, in 2011 an implementation bug was discovered in crypt_blowfish
|
||||
(versions up to 1.0.4 inclusive) affecting handling of password characters with
|
||||
the 8th bit set.
|
||||
Besides fixing the bug,
|
||||
to provide for upgrade strategies for existing systems, two new prefixes were
|
||||
introduced: "$2x$", which fully re-introduces the bug, and "$2y$", which
|
||||
guarantees correct handling of both 7- and 8-bit characters.
|
||||
OpenBSD 5.5 introduced the "$2b$" prefix for behavior that exactly matches
|
||||
crypt_blowfish's "$2y$", and current crypt_blowfish supports it as well.
|
||||
Unfortunately, the behavior of "$2a$" on password characters with the 8th bit
|
||||
set has to be considered system-specific.
|
||||
When generating new password hashes, the "$2b$" or "$2y$" prefix should be used.
|
||||
(If such hashes ever need to be migrated to a system that does not yet support
|
||||
these new prefixes, the prefix in migrated copies of the already-generated
|
||||
hashes may be changed to "$2a$".)
|
||||
.PP
|
||||
.crypt_gensalt and
|
||||
support the "$2b$", "$2y$", and "$2a$" prefixes (the latter for legacy programs
|
||||
or configurations), but not "$2x$" (which must not be used for new hashes).
|
||||
.crypt and
|
||||
support all four of these prefixes.
|
||||
.SH PORTABILITY NOTES
|
||||
Programs using any of these functions on a glibc 2.x system must be
|
||||
linked against
|
||||
.BR libcrypt .
|
||||
However, many Unix-like operating systems and older versions of the
|
||||
GNU C Library include the
|
||||
.BR crypt " function in " libc .
|
||||
.PP
|
||||
The
|
||||
.BR crypt_r ,
|
||||
.BR crypt_rn ,
|
||||
.BR crypt_ra ,
|
||||
.crypt_gensalt and
|
||||
functions are very non-portable.
|
||||
.PP
|
||||
The set of supported hashing methods is implementation-dependent.
|
||||
.SH CONFORMING TO
|
||||
The
|
||||
.B crypt
|
||||
function conforms to SVID, X/OPEN, and is available on BSD 4.3.
|
||||
The strings returned by
|
||||
.B crypt
|
||||
are not required to be portable among conformant systems.
|
||||
.PP
|
||||
.B crypt_r
|
||||
is a GNU extension.
|
||||
There's also a
|
||||
.B crypt_r
|
||||
function on HP-UX and MKS Toolkit, but the prototypes and semantics differ.
|
||||
.PP
|
||||
.B crypt_gensalt
|
||||
is an Openwall extension.
|
||||
There's also a
|
||||
.B crypt_gensalt
|
||||
function on Solaris 10 and newer, but the prototypes and semantics differ.
|
||||
.PP
|
||||
.BR crypt_rn ,
|
||||
.BR crypt_ra ,
|
||||
.BR crypt_gensalt_rn ,
|
||||
and
|
||||
.B crypt_gensalt_ra
|
||||
are Openwall extensions.
|
||||
.SH HISTORY
|
||||
A rotor-based
|
||||
.B crypt
|
||||
function appeared in Version 6 AT&T UNIX.
|
||||
The "traditional"
|
||||
.B crypt
|
||||
first appeared in Version 7 AT&T UNIX.
|
||||
.PP
|
||||
The
|
||||
.B crypt_r
|
||||
function was introduced during glibc 2.0 development.
|
||||
.SH BUGS
|
||||
The return values of
|
||||
.BR crypt " and " crypt_gensalt
|
||||
point to static buffers that are overwritten by subsequent calls.
|
||||
These functions are not thread-safe.
|
||||
.RB ( crypt
|
||||
on recent versions of Solaris uses thread-specific data and actually is
|
||||
thread-safe.)
|
||||
.PP
|
||||
The strings returned by certain other implementations of
|
||||
.B crypt
|
||||
on error may be stored in read-only locations or only initialized once,
|
||||
which makes it unsafe to always attempt to zero out the buffer normally
|
||||
pointed to by the
|
||||
.B crypt
|
||||
return value as it would otherwise be preferable for security reasons.
|
||||
The problem could be avoided with the use of
|
||||
.BR crypt_r ,
|
||||
.BR crypt_rn ,
|
||||
or
|
||||
.B crypt_ra
|
||||
where the application has full control over output buffers of these functions
|
||||
(and often over some of their private data as well).
|
||||
Unfortunately, the functions aren't (yet?) available on platforms where
|
||||
.B crypt
|
||||
has this undesired property.
|
||||
.PP
|
||||
Applications using the thread-safe
|
||||
.B crypt_r
|
||||
need to allocate address space for the large (over 128 KB)
|
||||
.I struct crypt_data
|
||||
structure. Each thread needs a separate instance of the structure. The
|
||||
.B crypt_r
|
||||
interface makes it impossible to implement a hashing algorithm which
|
||||
would need to keep an even larger amount of private data, without breaking
|
||||
binary compatibility.
|
||||
.B crypt_ra
|
||||
allows for dynamically increasing the allocation size as required by the
|
||||
hashing algorithm that is actually used. Unfortunately,
|
||||
.B crypt_ra
|
||||
is even more non-portable than
|
||||
.BR crypt_r .
|
||||
.PP
|
||||
Multi-threaded applications or library functions which are meant to be
|
||||
thread-safe should use
|
||||
.BR crypt_gensalt_rn " or " crypt_gensalt_ra
|
||||
rather than
|
||||
.BR crypt_gensalt .
|
||||
.SH SEE ALSO
|
||||
.BR login (1),
|
||||
.BR passwd (1),
|
||||
.BR crypto (3),
|
||||
.BR encrypt (3),
|
||||
.BR free (3),
|
||||
.BR getpass (3),
|
||||
.BR getpwent (3),
|
||||
.BR malloc (3),
|
||||
.BR realloc (3),
|
||||
.BR shadow (3),
|
||||
.BR passwd (5),
|
||||
.BR shadow (5),
|
||||
.BR regex (7),
|
||||
.BR pam (8)
|
||||
.sp
|
||||
Niels Provos and David Mazieres. A Future-Adaptable Password Scheme.
|
||||
Proceedings of the 1999 USENIX Annual Technical Conference, June 1999.
|
||||
.br
|
||||
http://www.usenix.org/events/usenix99/provos.html
|
||||
.sp
|
||||
Robert Morris and Ken Thompson. Password Security: A Case History.
|
||||
Unix Seventh Edition Manual, Volume 2, April 1978.
|
||||
.br
|
||||
http://plan9.bell-labs.com/7thEdMan/vol2/password
|
24
deps/crypt_blowfish/crypt.h
vendored
24
deps/crypt_blowfish/crypt.h
vendored
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Written by Solar Designer <solar at openwall.com> in 2000-2002.
|
||||
* No copyright is claimed, and the software is hereby placed in the public
|
||||
* domain. In case this attempt to disclaim copyright and place the software
|
||||
* in the public domain is deemed null and void, then the software is
|
||||
* Copyright (c) 2000-2002 Solar Designer and it is hereby released to the
|
||||
* general public under the following terms:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted.
|
||||
*
|
||||
* There's ABSOLUTELY NO WARRANTY, express or implied.
|
||||
*
|
||||
* See crypt_blowfish.c for more information.
|
||||
*/
|
||||
|
||||
#include <gnu-crypt.h>
|
||||
|
||||
#if defined(_OW_SOURCE) || defined(__USE_OW)
|
||||
#define __SKIP_GNU
|
||||
#undef __SKIP_OW
|
||||
#include <ow-crypt.h>
|
||||
#undef __SKIP_GNU
|
||||
#endif
|
907
deps/crypt_blowfish/crypt_blowfish.c
vendored
907
deps/crypt_blowfish/crypt_blowfish.c
vendored
@ -1,907 +0,0 @@
|
||||
/*
|
||||
* The crypt_blowfish homepage is:
|
||||
*
|
||||
* http://www.openwall.com/crypt/
|
||||
*
|
||||
* This code comes from John the Ripper password cracker, with reentrant
|
||||
* and crypt(3) interfaces added, but optimizations specific to password
|
||||
* cracking removed.
|
||||
*
|
||||
* Written by Solar Designer <solar at openwall.com> in 1998-2014.
|
||||
* No copyright is claimed, and the software is hereby placed in the public
|
||||
* domain. In case this attempt to disclaim copyright and place the software
|
||||
* in the public domain is deemed null and void, then the software is
|
||||
* Copyright (c) 1998-2014 Solar Designer and it is hereby released to the
|
||||
* general public under the following terms:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted.
|
||||
*
|
||||
* There's ABSOLUTELY NO WARRANTY, express or implied.
|
||||
*
|
||||
* It is my intent that you should be able to use this on your system,
|
||||
* as part of a software package, or anywhere else to improve security,
|
||||
* ensure compatibility, or for any other purpose. I would appreciate
|
||||
* it if you give credit where it is due and keep your modifications in
|
||||
* the public domain as well, but I don't require that in order to let
|
||||
* you place this code and any modifications you make under a license
|
||||
* of your choice.
|
||||
*
|
||||
* This implementation is fully compatible with OpenBSD's bcrypt.c for prefix
|
||||
* "$2b$", originally by Niels Provos <provos at citi.umich.edu>, and it uses
|
||||
* some of his ideas. The password hashing algorithm was designed by David
|
||||
* Mazieres <dm at lcs.mit.edu>. For information on the level of
|
||||
* compatibility for bcrypt hash prefixes other than "$2b$", please refer to
|
||||
* the comments in BF_set_key() below and to the included crypt(3) man page.
|
||||
*
|
||||
* There's a paper on the algorithm that explains its design decisions:
|
||||
*
|
||||
* http://www.usenix.org/events/usenix99/provos.html
|
||||
*
|
||||
* Some of the tricks in BF_ROUND might be inspired by Eric Young's
|
||||
* Blowfish library (I can't be sure if I would think of something if I
|
||||
* hadn't seen his code).
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <errno.h>
|
||||
#ifndef __set_errno
|
||||
#define __set_errno(val) errno = (val)
|
||||
#endif
|
||||
|
||||
/* Just to make sure the prototypes match the actual definitions */
|
||||
#include "crypt_blowfish.h"
|
||||
|
||||
#ifdef __i386__
|
||||
#define BF_ASM 1
|
||||
#define BF_SCALE 1
|
||||
#elif defined(__x86_64__) || defined(__alpha__) || defined(__hppa__)
|
||||
#define BF_ASM 0
|
||||
#define BF_SCALE 1
|
||||
#else
|
||||
#define BF_ASM 0
|
||||
#define BF_SCALE 0
|
||||
#endif
|
||||
|
||||
typedef unsigned int BF_word;
|
||||
typedef signed int BF_word_signed;
|
||||
|
||||
/* Number of Blowfish rounds, this is also hardcoded into a few places */
|
||||
#define BF_N 16
|
||||
|
||||
typedef BF_word BF_key[BF_N + 2];
|
||||
|
||||
typedef struct {
|
||||
BF_word S[4][0x100];
|
||||
BF_key P;
|
||||
} BF_ctx;
|
||||
|
||||
/*
|
||||
* Magic IV for 64 Blowfish encryptions that we do at the end.
|
||||
* The string is "OrpheanBeholderScryDoubt" on big-endian.
|
||||
*/
|
||||
static BF_word BF_magic_w[6] = {
|
||||
0x4F727068, 0x65616E42, 0x65686F6C,
|
||||
0x64657253, 0x63727944, 0x6F756274
|
||||
};
|
||||
|
||||
/*
|
||||
* P-box and S-box tables initialized with digits of Pi.
|
||||
*/
|
||||
static BF_ctx BF_init_state = {
|
||||
{
|
||||
{
|
||||
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
|
||||
0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
|
||||
0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
|
||||
0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
|
||||
0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
|
||||
0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
|
||||
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
|
||||
0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
|
||||
0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
|
||||
0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
|
||||
0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
|
||||
0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
|
||||
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
|
||||
0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
|
||||
0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
|
||||
0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
|
||||
0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
|
||||
0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
|
||||
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
|
||||
0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
|
||||
0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
|
||||
0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
|
||||
0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
|
||||
0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
|
||||
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
|
||||
0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
|
||||
0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
|
||||
0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
|
||||
0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
|
||||
0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
|
||||
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
|
||||
0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
|
||||
0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
|
||||
0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
|
||||
0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
|
||||
0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
|
||||
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
|
||||
0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
|
||||
0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
|
||||
0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
|
||||
0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
|
||||
0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
|
||||
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
|
||||
0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
|
||||
0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
|
||||
0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
|
||||
0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
|
||||
0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
|
||||
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
|
||||
0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
|
||||
0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
|
||||
0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
|
||||
0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
|
||||
0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
|
||||
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
|
||||
0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
|
||||
0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
|
||||
0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
|
||||
0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
|
||||
0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
|
||||
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
|
||||
0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
|
||||
0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
|
||||
0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a
|
||||
}, {
|
||||
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
|
||||
0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
|
||||
0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
|
||||
0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
|
||||
0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
|
||||
0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
|
||||
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
|
||||
0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
|
||||
0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
|
||||
0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
|
||||
0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
|
||||
0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
|
||||
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
|
||||
0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
|
||||
0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
|
||||
0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
|
||||
0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
|
||||
0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
|
||||
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
|
||||
0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
|
||||
0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
|
||||
0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
|
||||
0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
|
||||
0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
|
||||
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
|
||||
0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
|
||||
0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
|
||||
0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
|
||||
0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
|
||||
0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
|
||||
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
|
||||
0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
|
||||
0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
|
||||
0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
|
||||
0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
|
||||
0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
|
||||
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
|
||||
0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
|
||||
0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
|
||||
0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
|
||||
0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
|
||||
0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
|
||||
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
|
||||
0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
|
||||
0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
|
||||
0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
|
||||
0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
|
||||
0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
|
||||
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
|
||||
0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
|
||||
0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
|
||||
0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
|
||||
0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
|
||||
0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
|
||||
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
|
||||
0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
|
||||
0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
|
||||
0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
|
||||
0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
|
||||
0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
|
||||
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
|
||||
0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
|
||||
0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
|
||||
0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7
|
||||
}, {
|
||||
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
|
||||
0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
|
||||
0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
|
||||
0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
|
||||
0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
|
||||
0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
|
||||
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
|
||||
0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
|
||||
0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
|
||||
0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
|
||||
0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
|
||||
0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
|
||||
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
|
||||
0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
|
||||
0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
|
||||
0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
|
||||
0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
|
||||
0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
|
||||
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
|
||||
0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
|
||||
0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
|
||||
0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
|
||||
0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
|
||||
0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
|
||||
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
|
||||
0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
|
||||
0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
|
||||
0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
|
||||
0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
|
||||
0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
|
||||
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
|
||||
0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
|
||||
0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
|
||||
0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
|
||||
0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
|
||||
0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
|
||||
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
|
||||
0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
|
||||
0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
|
||||
0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
|
||||
0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
|
||||
0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
|
||||
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
|
||||
0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
|
||||
0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
|
||||
0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
|
||||
0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
|
||||
0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
|
||||
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
|
||||
0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
|
||||
0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
|
||||
0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
|
||||
0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
|
||||
0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
|
||||
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
|
||||
0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
|
||||
0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
|
||||
0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
|
||||
0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
|
||||
0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
|
||||
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
|
||||
0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
|
||||
0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
|
||||
0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0
|
||||
}, {
|
||||
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
|
||||
0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
|
||||
0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
|
||||
0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
|
||||
0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
|
||||
0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
|
||||
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
|
||||
0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
|
||||
0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
|
||||
0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
|
||||
0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
|
||||
0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
|
||||
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
|
||||
0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
|
||||
0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
|
||||
0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
|
||||
0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
|
||||
0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
|
||||
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
|
||||
0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
|
||||
0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
|
||||
0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
|
||||
0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
|
||||
0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
|
||||
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
|
||||
0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
|
||||
0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
|
||||
0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
|
||||
0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
|
||||
0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
|
||||
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
|
||||
0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
|
||||
0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
|
||||
0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
|
||||
0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
|
||||
0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
|
||||
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
|
||||
0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
|
||||
0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
|
||||
0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
|
||||
0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
|
||||
0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
|
||||
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
|
||||
0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
|
||||
0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
|
||||
0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
|
||||
0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
|
||||
0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
|
||||
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
|
||||
0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
|
||||
0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
|
||||
0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
|
||||
0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
|
||||
0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
|
||||
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
|
||||
0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
|
||||
0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
|
||||
0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
|
||||
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
|
||||
0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
|
||||
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
|
||||
0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
|
||||
0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
|
||||
0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
|
||||
}
|
||||
}, {
|
||||
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
|
||||
0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
|
||||
0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
|
||||
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
|
||||
0x9216d5d9, 0x8979fb1b
|
||||
}
|
||||
};
|
||||
|
||||
static unsigned char BF_itoa64[64 + 1] =
|
||||
"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
static unsigned char BF_atoi64[0x60] = {
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 1,
|
||||
54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 64, 64, 64, 64, 64,
|
||||
64, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 64, 64, 64, 64, 64,
|
||||
64, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
|
||||
43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 64, 64, 64, 64, 64
|
||||
};
|
||||
|
||||
#define BF_safe_atoi64(dst, src) \
|
||||
{ \
|
||||
tmp = (unsigned char)(src); \
|
||||
if ((unsigned int)(tmp -= 0x20) >= 0x60) return -1; \
|
||||
tmp = BF_atoi64[tmp]; \
|
||||
if (tmp > 63) return -1; \
|
||||
(dst) = tmp; \
|
||||
}
|
||||
|
||||
static int BF_decode(BF_word *dst, const char *src, int size)
|
||||
{
|
||||
unsigned char *dptr = (unsigned char *)dst;
|
||||
unsigned char *end = dptr + size;
|
||||
const unsigned char *sptr = (const unsigned char *)src;
|
||||
unsigned int tmp, c1, c2, c3, c4;
|
||||
|
||||
do {
|
||||
BF_safe_atoi64(c1, *sptr++);
|
||||
BF_safe_atoi64(c2, *sptr++);
|
||||
*dptr++ = (c1 << 2) | ((c2 & 0x30) >> 4);
|
||||
if (dptr >= end) break;
|
||||
|
||||
BF_safe_atoi64(c3, *sptr++);
|
||||
*dptr++ = ((c2 & 0x0F) << 4) | ((c3 & 0x3C) >> 2);
|
||||
if (dptr >= end) break;
|
||||
|
||||
BF_safe_atoi64(c4, *sptr++);
|
||||
*dptr++ = ((c3 & 0x03) << 6) | c4;
|
||||
} while (dptr < end);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void BF_encode(char *dst, const BF_word *src, int size)
|
||||
{
|
||||
const unsigned char *sptr = (const unsigned char *)src;
|
||||
const unsigned char *end = sptr + size;
|
||||
unsigned char *dptr = (unsigned char *)dst;
|
||||
unsigned int c1, c2;
|
||||
|
||||
do {
|
||||
c1 = *sptr++;
|
||||
*dptr++ = BF_itoa64[c1 >> 2];
|
||||
c1 = (c1 & 0x03) << 4;
|
||||
if (sptr >= end) {
|
||||
*dptr++ = BF_itoa64[c1];
|
||||
break;
|
||||
}
|
||||
|
||||
c2 = *sptr++;
|
||||
c1 |= c2 >> 4;
|
||||
*dptr++ = BF_itoa64[c1];
|
||||
c1 = (c2 & 0x0f) << 2;
|
||||
if (sptr >= end) {
|
||||
*dptr++ = BF_itoa64[c1];
|
||||
break;
|
||||
}
|
||||
|
||||
c2 = *sptr++;
|
||||
c1 |= c2 >> 6;
|
||||
*dptr++ = BF_itoa64[c1];
|
||||
*dptr++ = BF_itoa64[c2 & 0x3f];
|
||||
} while (sptr < end);
|
||||
}
|
||||
|
||||
static void BF_swap(BF_word *x, int count)
|
||||
{
|
||||
static int endianness_check = 1;
|
||||
char *is_little_endian = (char *)&endianness_check;
|
||||
BF_word tmp;
|
||||
|
||||
if (*is_little_endian)
|
||||
do {
|
||||
tmp = *x;
|
||||
tmp = (tmp << 16) | (tmp >> 16);
|
||||
*x++ = ((tmp & 0x00FF00FF) << 8) | ((tmp >> 8) & 0x00FF00FF);
|
||||
} while (--count);
|
||||
}
|
||||
|
||||
#if BF_SCALE
|
||||
/* Architectures which can shift addresses left by 2 bits with no extra cost */
|
||||
#define BF_ROUND(L, R, N) \
|
||||
tmp1 = L & 0xFF; \
|
||||
tmp2 = L >> 8; \
|
||||
tmp2 &= 0xFF; \
|
||||
tmp3 = L >> 16; \
|
||||
tmp3 &= 0xFF; \
|
||||
tmp4 = L >> 24; \
|
||||
tmp1 = data.ctx.S[3][tmp1]; \
|
||||
tmp2 = data.ctx.S[2][tmp2]; \
|
||||
tmp3 = data.ctx.S[1][tmp3]; \
|
||||
tmp3 += data.ctx.S[0][tmp4]; \
|
||||
tmp3 ^= tmp2; \
|
||||
R ^= data.ctx.P[N + 1]; \
|
||||
tmp3 += tmp1; \
|
||||
R ^= tmp3;
|
||||
#else
|
||||
/* Architectures with no complicated addressing modes supported */
|
||||
#define BF_INDEX(S, i) \
|
||||
(*((BF_word *)(((unsigned char *)S) + (i))))
|
||||
#define BF_ROUND(L, R, N) \
|
||||
tmp1 = L & 0xFF; \
|
||||
tmp1 <<= 2; \
|
||||
tmp2 = L >> 6; \
|
||||
tmp2 &= 0x3FC; \
|
||||
tmp3 = L >> 14; \
|
||||
tmp3 &= 0x3FC; \
|
||||
tmp4 = L >> 22; \
|
||||
tmp4 &= 0x3FC; \
|
||||
tmp1 = BF_INDEX(data.ctx.S[3], tmp1); \
|
||||
tmp2 = BF_INDEX(data.ctx.S[2], tmp2); \
|
||||
tmp3 = BF_INDEX(data.ctx.S[1], tmp3); \
|
||||
tmp3 += BF_INDEX(data.ctx.S[0], tmp4); \
|
||||
tmp3 ^= tmp2; \
|
||||
R ^= data.ctx.P[N + 1]; \
|
||||
tmp3 += tmp1; \
|
||||
R ^= tmp3;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Encrypt one block, BF_N is hardcoded here.
|
||||
*/
|
||||
#define BF_ENCRYPT \
|
||||
L ^= data.ctx.P[0]; \
|
||||
BF_ROUND(L, R, 0); \
|
||||
BF_ROUND(R, L, 1); \
|
||||
BF_ROUND(L, R, 2); \
|
||||
BF_ROUND(R, L, 3); \
|
||||
BF_ROUND(L, R, 4); \
|
||||
BF_ROUND(R, L, 5); \
|
||||
BF_ROUND(L, R, 6); \
|
||||
BF_ROUND(R, L, 7); \
|
||||
BF_ROUND(L, R, 8); \
|
||||
BF_ROUND(R, L, 9); \
|
||||
BF_ROUND(L, R, 10); \
|
||||
BF_ROUND(R, L, 11); \
|
||||
BF_ROUND(L, R, 12); \
|
||||
BF_ROUND(R, L, 13); \
|
||||
BF_ROUND(L, R, 14); \
|
||||
BF_ROUND(R, L, 15); \
|
||||
tmp4 = R; \
|
||||
R = L; \
|
||||
L = tmp4 ^ data.ctx.P[BF_N + 1];
|
||||
|
||||
#if BF_ASM
|
||||
#define BF_body() \
|
||||
_BF_body_r(&data.ctx);
|
||||
#else
|
||||
#define BF_body() \
|
||||
L = R = 0; \
|
||||
ptr = data.ctx.P; \
|
||||
do { \
|
||||
ptr += 2; \
|
||||
BF_ENCRYPT; \
|
||||
*(ptr - 2) = L; \
|
||||
*(ptr - 1) = R; \
|
||||
} while (ptr < &data.ctx.P[BF_N + 2]); \
|
||||
\
|
||||
ptr = data.ctx.S[0]; \
|
||||
do { \
|
||||
ptr += 2; \
|
||||
BF_ENCRYPT; \
|
||||
*(ptr - 2) = L; \
|
||||
*(ptr - 1) = R; \
|
||||
} while (ptr < &data.ctx.S[3][0xFF]);
|
||||
#endif
|
||||
|
||||
static void BF_set_key(const char *key, BF_key expanded, BF_key initial,
|
||||
unsigned char flags)
|
||||
{
|
||||
const char *ptr = key;
|
||||
unsigned int bug, i, j;
|
||||
BF_word safety, sign, diff, tmp[2];
|
||||
|
||||
/*
|
||||
* There was a sign extension bug in older revisions of this function. While
|
||||
* we would have liked to simply fix the bug and move on, we have to provide
|
||||
* a backwards compatibility feature (essentially the bug) for some systems and
|
||||
* a safety measure for some others. The latter is needed because for certain
|
||||
* multiple inputs to the buggy algorithm there exist easily found inputs to
|
||||
* the correct algorithm that produce the same hash. Thus, we optionally
|
||||
* deviate from the correct algorithm just enough to avoid such collisions.
|
||||
* While the bug itself affected the majority of passwords containing
|
||||
* characters with the 8th bit set (although only a percentage of those in a
|
||||
* collision-producing way), the anti-collision safety measure affects
|
||||
* only a subset of passwords containing the '\xff' character (not even all of
|
||||
* those passwords, just some of them). This character is not found in valid
|
||||
* UTF-8 sequences and is rarely used in popular 8-bit character encodings.
|
||||
* Thus, the safety measure is unlikely to cause much annoyance, and is a
|
||||
* reasonable tradeoff to use when authenticating against existing hashes that
|
||||
* are not reliably known to have been computed with the correct algorithm.
|
||||
*
|
||||
* We use an approach that tries to minimize side-channel leaks of password
|
||||
* information - that is, we mostly use fixed-cost bitwise operations instead
|
||||
* of branches or table lookups. (One conditional branch based on password
|
||||
* length remains. It is not part of the bug aftermath, though, and is
|
||||
* difficult and possibly unreasonable to avoid given the use of C strings by
|
||||
* the caller, which results in similar timing leaks anyway.)
|
||||
*
|
||||
* For actual implementation, we set an array index in the variable "bug"
|
||||
* (0 means no bug, 1 means sign extension bug emulation) and a flag in the
|
||||
* variable "safety" (bit 16 is set when the safety measure is requested).
|
||||
* Valid combinations of settings are:
|
||||
*
|
||||
* Prefix "$2a$": bug = 0, safety = 0x10000
|
||||
* Prefix "$2b$": bug = 0, safety = 0
|
||||
* Prefix "$2x$": bug = 1, safety = 0
|
||||
* Prefix "$2y$": bug = 0, safety = 0
|
||||
*/
|
||||
bug = (unsigned int)flags & 1;
|
||||
safety = ((BF_word)flags & 2) << 15;
|
||||
|
||||
sign = diff = 0;
|
||||
|
||||
for (i = 0; i < BF_N + 2; i++) {
|
||||
tmp[0] = tmp[1] = 0;
|
||||
for (j = 0; j < 4; j++) {
|
||||
tmp[0] <<= 8;
|
||||
tmp[0] |= (unsigned char)*ptr; /* correct */
|
||||
tmp[1] <<= 8;
|
||||
tmp[1] |= (BF_word_signed)(signed char)*ptr; /* bug */
|
||||
/*
|
||||
* Sign extension in the first char has no effect - nothing to overwrite yet,
|
||||
* and those extra 24 bits will be fully shifted out of the 32-bit word. For
|
||||
* chars 2, 3, 4 in each four-char block, we set bit 7 of "sign" if sign
|
||||
* extension in tmp[1] occurs. Once this flag is set, it remains set.
|
||||
*/
|
||||
if (j)
|
||||
sign |= tmp[1] & 0x80;
|
||||
if (!*ptr)
|
||||
ptr = key;
|
||||
else
|
||||
ptr++;
|
||||
}
|
||||
diff |= tmp[0] ^ tmp[1]; /* Non-zero on any differences */
|
||||
|
||||
expanded[i] = tmp[bug];
|
||||
initial[i] = BF_init_state.P[i] ^ tmp[bug];
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point, "diff" is zero iff the correct and buggy algorithms produced
|
||||
* exactly the same result. If so and if "sign" is non-zero, which indicates
|
||||
* that there was a non-benign sign extension, this means that we have a
|
||||
* collision between the correctly computed hash for this password and a set of
|
||||
* passwords that could be supplied to the buggy algorithm. Our safety measure
|
||||
* is meant to protect from such many-buggy to one-correct collisions, by
|
||||
* deviating from the correct algorithm in such cases. Let's check for this.
|
||||
*/
|
||||
diff |= diff >> 16; /* still zero iff exact match */
|
||||
diff &= 0xffff; /* ditto */
|
||||
diff += 0xffff; /* bit 16 set iff "diff" was non-zero (on non-match) */
|
||||
sign <<= 9; /* move the non-benign sign extension flag to bit 16 */
|
||||
sign &= ~diff & safety; /* action needed? */
|
||||
|
||||
/*
|
||||
* If we have determined that we need to deviate from the correct algorithm,
|
||||
* flip bit 16 in initial expanded key. (The choice of 16 is arbitrary, but
|
||||
* let's stick to it now. It came out of the approach we used above, and it's
|
||||
* not any worse than any other choice we could make.)
|
||||
*
|
||||
* It is crucial that we don't do the same to the expanded key used in the main
|
||||
* Eksblowfish loop. By doing it to only one of these two, we deviate from a
|
||||
* state that could be directly specified by a password to the buggy algorithm
|
||||
* (and to the fully correct one as well, but that's a side-effect).
|
||||
*/
|
||||
initial[0] ^= sign;
|
||||
}
|
||||
|
||||
static const unsigned char flags_by_subtype[26] =
|
||||
{2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0};
|
||||
|
||||
static char *BF_crypt(const char *key, const char *setting,
|
||||
char *output, int size,
|
||||
BF_word min)
|
||||
{
|
||||
#if BF_ASM
|
||||
extern void _BF_body_r(BF_ctx *ctx);
|
||||
#endif
|
||||
struct {
|
||||
BF_ctx ctx;
|
||||
BF_key expanded_key;
|
||||
union {
|
||||
BF_word salt[4];
|
||||
BF_word output[6];
|
||||
} binary;
|
||||
} data;
|
||||
BF_word L, R;
|
||||
BF_word tmp1, tmp2, tmp3, tmp4;
|
||||
BF_word *ptr;
|
||||
BF_word count;
|
||||
int i;
|
||||
|
||||
if (size < 7 + 22 + 31 + 1) {
|
||||
__set_errno(ERANGE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (setting[0] != '$' ||
|
||||
setting[1] != '2' ||
|
||||
setting[2] < 'a' || setting[2] > 'z' ||
|
||||
!flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a'] ||
|
||||
setting[3] != '$' ||
|
||||
setting[4] < '0' || setting[4] > '3' ||
|
||||
setting[5] < '0' || setting[5] > '9' ||
|
||||
(setting[4] == '3' && setting[5] > '1') ||
|
||||
setting[6] != '$') {
|
||||
__set_errno(EINVAL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
count = (BF_word)1 << ((setting[4] - '0') * 10 + (setting[5] - '0'));
|
||||
if (count < min || BF_decode(data.binary.salt, &setting[7], 16)) {
|
||||
__set_errno(EINVAL);
|
||||
return NULL;
|
||||
}
|
||||
BF_swap(data.binary.salt, 4);
|
||||
|
||||
BF_set_key(key, data.expanded_key, data.ctx.P,
|
||||
flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a']);
|
||||
|
||||
memcpy(data.ctx.S, BF_init_state.S, sizeof(data.ctx.S));
|
||||
|
||||
L = R = 0;
|
||||
for (i = 0; i < BF_N + 2; i += 2) {
|
||||
L ^= data.binary.salt[i & 2];
|
||||
R ^= data.binary.salt[(i & 2) + 1];
|
||||
BF_ENCRYPT;
|
||||
data.ctx.P[i] = L;
|
||||
data.ctx.P[i + 1] = R;
|
||||
}
|
||||
|
||||
ptr = data.ctx.S[0];
|
||||
do {
|
||||
ptr += 4;
|
||||
L ^= data.binary.salt[(BF_N + 2) & 3];
|
||||
R ^= data.binary.salt[(BF_N + 3) & 3];
|
||||
BF_ENCRYPT;
|
||||
*(ptr - 4) = L;
|
||||
*(ptr - 3) = R;
|
||||
|
||||
L ^= data.binary.salt[(BF_N + 4) & 3];
|
||||
R ^= data.binary.salt[(BF_N + 5) & 3];
|
||||
BF_ENCRYPT;
|
||||
*(ptr - 2) = L;
|
||||
*(ptr - 1) = R;
|
||||
} while (ptr < &data.ctx.S[3][0xFF]);
|
||||
|
||||
do {
|
||||
int done;
|
||||
|
||||
for (i = 0; i < BF_N + 2; i += 2) {
|
||||
data.ctx.P[i] ^= data.expanded_key[i];
|
||||
data.ctx.P[i + 1] ^= data.expanded_key[i + 1];
|
||||
}
|
||||
|
||||
done = 0;
|
||||
do {
|
||||
BF_body();
|
||||
if (done)
|
||||
break;
|
||||
done = 1;
|
||||
|
||||
tmp1 = data.binary.salt[0];
|
||||
tmp2 = data.binary.salt[1];
|
||||
tmp3 = data.binary.salt[2];
|
||||
tmp4 = data.binary.salt[3];
|
||||
for (i = 0; i < BF_N; i += 4) {
|
||||
data.ctx.P[i] ^= tmp1;
|
||||
data.ctx.P[i + 1] ^= tmp2;
|
||||
data.ctx.P[i + 2] ^= tmp3;
|
||||
data.ctx.P[i + 3] ^= tmp4;
|
||||
}
|
||||
data.ctx.P[16] ^= tmp1;
|
||||
data.ctx.P[17] ^= tmp2;
|
||||
} while (1);
|
||||
} while (--count);
|
||||
|
||||
for (i = 0; i < 6; i += 2) {
|
||||
L = BF_magic_w[i];
|
||||
R = BF_magic_w[i + 1];
|
||||
|
||||
count = 64;
|
||||
do {
|
||||
BF_ENCRYPT;
|
||||
} while (--count);
|
||||
|
||||
data.binary.output[i] = L;
|
||||
data.binary.output[i + 1] = R;
|
||||
}
|
||||
|
||||
memcpy(output, setting, 7 + 22 - 1);
|
||||
output[7 + 22 - 1] = BF_itoa64[(int)
|
||||
BF_atoi64[(int)setting[7 + 22 - 1] - 0x20] & 0x30];
|
||||
|
||||
/* This has to be bug-compatible with the original implementation, so
|
||||
* only encode 23 of the 24 bytes. :-) */
|
||||
BF_swap(data.binary.output, 6);
|
||||
BF_encode(&output[7 + 22], data.binary.output, 23);
|
||||
output[7 + 22 + 31] = '\0';
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
int _crypt_output_magic(const char *setting, char *output, int size)
|
||||
{
|
||||
if (size < 3)
|
||||
return -1;
|
||||
|
||||
output[0] = '*';
|
||||
output[1] = '0';
|
||||
output[2] = '\0';
|
||||
|
||||
if (setting[0] == '*' && setting[1] == '0')
|
||||
output[1] = '1';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Please preserve the runtime self-test. It serves two purposes at once:
|
||||
*
|
||||
* 1. We really can't afford the risk of producing incompatible hashes e.g.
|
||||
* when there's something like gcc bug 26587 again, whereas an application or
|
||||
* library integrating this code might not also integrate our external tests or
|
||||
* it might not run them after every build. Even if it does, the miscompile
|
||||
* might only occur on the production build, but not on a testing build (such
|
||||
* as because of different optimization settings). It is painful to recover
|
||||
* from incorrectly-computed hashes - merely fixing whatever broke is not
|
||||
* enough. Thus, a proactive measure like this self-test is needed.
|
||||
*
|
||||
* 2. We don't want to leave sensitive data from our actual password hash
|
||||
* computation on the stack or in registers. Previous revisions of the code
|
||||
* would do explicit cleanups, but simply running the self-test after hash
|
||||
* computation is more reliable.
|
||||
*
|
||||
* The performance cost of this quick self-test is around 0.6% at the "$2a$08"
|
||||
* setting.
|
||||
*/
|
||||
char *_crypt_blowfish_rn(const char *key, const char *setting,
|
||||
char *output, int size)
|
||||
{
|
||||
const char *test_key = "8b \xd0\xc1\xd2\xcf\xcc\xd8";
|
||||
const char *test_setting = "$2a$00$abcdefghijklmnopqrstuu";
|
||||
static const char * const test_hashes[2] =
|
||||
{"i1D709vfamulimlGcq0qq3UvuUasvEa\0\x55", /* 'a', 'b', 'y' */
|
||||
"VUrPmXD6q/nVSSp7pNDhCR9071IfIRe\0\x55"}; /* 'x' */
|
||||
const char *test_hash = test_hashes[0];
|
||||
char *retval;
|
||||
const char *p;
|
||||
int save_errno, ok;
|
||||
struct {
|
||||
char s[7 + 22 + 1];
|
||||
char o[7 + 22 + 31 + 1 + 1 + 1];
|
||||
} buf;
|
||||
|
||||
/* Hash the supplied password */
|
||||
_crypt_output_magic(setting, output, size);
|
||||
retval = BF_crypt(key, setting, output, size, 16);
|
||||
save_errno = errno;
|
||||
|
||||
/*
|
||||
* Do a quick self-test. It is important that we make both calls to BF_crypt()
|
||||
* from the same scope such that they likely use the same stack locations,
|
||||
* which makes the second call overwrite the first call's sensitive data on the
|
||||
* stack and makes it more likely that any alignment related issues would be
|
||||
* detected by the self-test.
|
||||
*/
|
||||
memcpy(buf.s, test_setting, sizeof(buf.s));
|
||||
if (retval) {
|
||||
unsigned int flags = flags_by_subtype[
|
||||
(unsigned int)(unsigned char)setting[2] - 'a'];
|
||||
test_hash = test_hashes[flags & 1];
|
||||
buf.s[2] = setting[2];
|
||||
}
|
||||
memset(buf.o, 0x55, sizeof(buf.o));
|
||||
buf.o[sizeof(buf.o) - 1] = 0;
|
||||
p = BF_crypt(test_key, buf.s, buf.o, sizeof(buf.o) - (1 + 1), 1);
|
||||
|
||||
ok = (p == buf.o &&
|
||||
!memcmp(p, buf.s, 7 + 22) &&
|
||||
!memcmp(p + (7 + 22), test_hash, 31 + 1 + 1 + 1));
|
||||
|
||||
{
|
||||
const char *k = "\xff\xa3" "34" "\xff\xff\xff\xa3" "345";
|
||||
BF_key ae, ai, ye, yi;
|
||||
BF_set_key(k, ae, ai, 2); /* $2a$ */
|
||||
BF_set_key(k, ye, yi, 4); /* $2y$ */
|
||||
ai[0] ^= 0x10000; /* undo the safety (for comparison) */
|
||||
ok = ok && ai[0] == 0xdb9c59bc && ye[17] == 0x33343500 &&
|
||||
!memcmp(ae, ye, sizeof(ae)) &&
|
||||
!memcmp(ai, yi, sizeof(ai));
|
||||
}
|
||||
|
||||
__set_errno(save_errno);
|
||||
if (ok)
|
||||
return retval;
|
||||
|
||||
/* Should not happen */
|
||||
_crypt_output_magic(setting, output, size);
|
||||
__set_errno(EINVAL); /* pretend we don't support this hash type */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *_crypt_gensalt_blowfish_rn(const char *prefix, unsigned long count,
|
||||
const char *input, int size, char *output, int output_size)
|
||||
{
|
||||
if (size < 16 || output_size < 7 + 22 + 1 ||
|
||||
(count && (count < 4 || count > 31)) ||
|
||||
prefix[0] != '$' || prefix[1] != '2' ||
|
||||
(prefix[2] != 'a' && prefix[2] != 'b' && prefix[2] != 'y')) {
|
||||
if (output_size > 0) output[0] = '\0';
|
||||
__set_errno((output_size < 7 + 22 + 1) ? ERANGE : EINVAL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!count) count = 5;
|
||||
|
||||
output[0] = '$';
|
||||
output[1] = '2';
|
||||
output[2] = prefix[2];
|
||||
output[3] = '$';
|
||||
output[4] = '0' + count / 10;
|
||||
output[5] = '0' + count % 10;
|
||||
output[6] = '$';
|
||||
|
||||
BF_encode(&output[7], (const BF_word *)input, 16);
|
||||
output[7 + 22] = '\0';
|
||||
|
||||
return output;
|
||||
}
|
27
deps/crypt_blowfish/crypt_blowfish.h
vendored
27
deps/crypt_blowfish/crypt_blowfish.h
vendored
@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Written by Solar Designer <solar at openwall.com> in 2000-2011.
|
||||
* No copyright is claimed, and the software is hereby placed in the public
|
||||
* domain. In case this attempt to disclaim copyright and place the software
|
||||
* in the public domain is deemed null and void, then the software is
|
||||
* Copyright (c) 2000-2011 Solar Designer and it is hereby released to the
|
||||
* general public under the following terms:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted.
|
||||
*
|
||||
* There's ABSOLUTELY NO WARRANTY, express or implied.
|
||||
*
|
||||
* See crypt_blowfish.c for more information.
|
||||
*/
|
||||
|
||||
#ifndef _CRYPT_BLOWFISH_H
|
||||
#define _CRYPT_BLOWFISH_H
|
||||
|
||||
extern int _crypt_output_magic(const char *setting, char *output, int size);
|
||||
extern char *_crypt_blowfish_rn(const char *key, const char *setting,
|
||||
char *output, int size);
|
||||
extern char *_crypt_gensalt_blowfish_rn(const char *prefix,
|
||||
unsigned long count,
|
||||
const char *input, int size, char *output, int output_size);
|
||||
|
||||
#endif
|
124
deps/crypt_blowfish/crypt_gensalt.c
vendored
124
deps/crypt_blowfish/crypt_gensalt.c
vendored
@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Written by Solar Designer <solar at openwall.com> in 2000-2011.
|
||||
* No copyright is claimed, and the software is hereby placed in the public
|
||||
* domain. In case this attempt to disclaim copyright and place the software
|
||||
* in the public domain is deemed null and void, then the software is
|
||||
* Copyright (c) 2000-2011 Solar Designer and it is hereby released to the
|
||||
* general public under the following terms:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted.
|
||||
*
|
||||
* There's ABSOLUTELY NO WARRANTY, express or implied.
|
||||
*
|
||||
* See crypt_blowfish.c for more information.
|
||||
*
|
||||
* This file contains salt generation functions for the traditional and
|
||||
* other common crypt(3) algorithms, except for bcrypt which is defined
|
||||
* entirely in crypt_blowfish.c.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <errno.h>
|
||||
#ifndef __set_errno
|
||||
#define __set_errno(val) errno = (val)
|
||||
#endif
|
||||
|
||||
/* Just to make sure the prototypes match the actual definitions */
|
||||
#include "crypt_gensalt.h"
|
||||
|
||||
unsigned char _crypt_itoa64[64 + 1] =
|
||||
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
char *_crypt_gensalt_traditional_rn(const char *prefix, unsigned long count,
|
||||
const char *input, int size, char *output, int output_size)
|
||||
{
|
||||
(void) prefix;
|
||||
|
||||
if (size < 2 || output_size < 2 + 1 || (count && count != 25)) {
|
||||
if (output_size > 0) output[0] = '\0';
|
||||
__set_errno((output_size < 2 + 1) ? ERANGE : EINVAL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
output[0] = _crypt_itoa64[(unsigned int)input[0] & 0x3f];
|
||||
output[1] = _crypt_itoa64[(unsigned int)input[1] & 0x3f];
|
||||
output[2] = '\0';
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
char *_crypt_gensalt_extended_rn(const char *prefix, unsigned long count,
|
||||
const char *input, int size, char *output, int output_size)
|
||||
{
|
||||
unsigned long value;
|
||||
|
||||
(void) prefix;
|
||||
|
||||
/* Even iteration counts make it easier to detect weak DES keys from a look
|
||||
* at the hash, so they should be avoided */
|
||||
if (size < 3 || output_size < 1 + 4 + 4 + 1 ||
|
||||
(count && (count > 0xffffff || !(count & 1)))) {
|
||||
if (output_size > 0) output[0] = '\0';
|
||||
__set_errno((output_size < 1 + 4 + 4 + 1) ? ERANGE : EINVAL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!count) count = 725;
|
||||
|
||||
output[0] = '_';
|
||||
output[1] = _crypt_itoa64[count & 0x3f];
|
||||
output[2] = _crypt_itoa64[(count >> 6) & 0x3f];
|
||||
output[3] = _crypt_itoa64[(count >> 12) & 0x3f];
|
||||
output[4] = _crypt_itoa64[(count >> 18) & 0x3f];
|
||||
value = (unsigned long)(unsigned char)input[0] |
|
||||
((unsigned long)(unsigned char)input[1] << 8) |
|
||||
((unsigned long)(unsigned char)input[2] << 16);
|
||||
output[5] = _crypt_itoa64[value & 0x3f];
|
||||
output[6] = _crypt_itoa64[(value >> 6) & 0x3f];
|
||||
output[7] = _crypt_itoa64[(value >> 12) & 0x3f];
|
||||
output[8] = _crypt_itoa64[(value >> 18) & 0x3f];
|
||||
output[9] = '\0';
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
char *_crypt_gensalt_md5_rn(const char *prefix, unsigned long count,
|
||||
const char *input, int size, char *output, int output_size)
|
||||
{
|
||||
unsigned long value;
|
||||
|
||||
(void) prefix;
|
||||
|
||||
if (size < 3 || output_size < 3 + 4 + 1 || (count && count != 1000)) {
|
||||
if (output_size > 0) output[0] = '\0';
|
||||
__set_errno((output_size < 3 + 4 + 1) ? ERANGE : EINVAL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
output[0] = '$';
|
||||
output[1] = '1';
|
||||
output[2] = '$';
|
||||
value = (unsigned long)(unsigned char)input[0] |
|
||||
((unsigned long)(unsigned char)input[1] << 8) |
|
||||
((unsigned long)(unsigned char)input[2] << 16);
|
||||
output[3] = _crypt_itoa64[value & 0x3f];
|
||||
output[4] = _crypt_itoa64[(value >> 6) & 0x3f];
|
||||
output[5] = _crypt_itoa64[(value >> 12) & 0x3f];
|
||||
output[6] = _crypt_itoa64[(value >> 18) & 0x3f];
|
||||
output[7] = '\0';
|
||||
|
||||
if (size >= 6 && output_size >= 3 + 4 + 4 + 1) {
|
||||
value = (unsigned long)(unsigned char)input[3] |
|
||||
((unsigned long)(unsigned char)input[4] << 8) |
|
||||
((unsigned long)(unsigned char)input[5] << 16);
|
||||
output[7] = _crypt_itoa64[value & 0x3f];
|
||||
output[8] = _crypt_itoa64[(value >> 6) & 0x3f];
|
||||
output[9] = _crypt_itoa64[(value >> 12) & 0x3f];
|
||||
output[10] = _crypt_itoa64[(value >> 18) & 0x3f];
|
||||
output[11] = '\0';
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
30
deps/crypt_blowfish/crypt_gensalt.h
vendored
30
deps/crypt_blowfish/crypt_gensalt.h
vendored
@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Written by Solar Designer <solar at openwall.com> in 2000-2011.
|
||||
* No copyright is claimed, and the software is hereby placed in the public
|
||||
* domain. In case this attempt to disclaim copyright and place the software
|
||||
* in the public domain is deemed null and void, then the software is
|
||||
* Copyright (c) 2000-2011 Solar Designer and it is hereby released to the
|
||||
* general public under the following terms:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted.
|
||||
*
|
||||
* There's ABSOLUTELY NO WARRANTY, express or implied.
|
||||
*
|
||||
* See crypt_blowfish.c for more information.
|
||||
*/
|
||||
|
||||
#ifndef _CRYPT_GENSALT_H
|
||||
#define _CRYPT_GENSALT_H
|
||||
|
||||
extern unsigned char _crypt_itoa64[];
|
||||
extern char *_crypt_gensalt_traditional_rn(const char *prefix,
|
||||
unsigned long count,
|
||||
const char *input, int size, char *output, int output_size);
|
||||
extern char *_crypt_gensalt_extended_rn(const char *prefix,
|
||||
unsigned long count,
|
||||
const char *input, int size, char *output, int output_size);
|
||||
extern char *_crypt_gensalt_md5_rn(const char *prefix, unsigned long count,
|
||||
const char *input, int size, char *output, int output_size);
|
||||
|
||||
#endif
|
53
deps/crypt_blowfish/glibc-2.1.3-crypt.diff
vendored
53
deps/crypt_blowfish/glibc-2.1.3-crypt.diff
vendored
@ -1,53 +0,0 @@
|
||||
--- glibc-2.1.3.orig/crypt/sysdeps/unix/Makefile 1997-03-05 00:33:59 +0000
|
||||
+++ glibc-2.1.3/crypt/sysdeps/unix/Makefile 2000-06-11 03:13:41 +0000
|
||||
@@ -1,4 +1,4 @@
|
||||
ifeq ($(subdir),md5-crypt)
|
||||
-libcrypt-routines += crypt crypt_util
|
||||
-dont_distribute += crypt.c crypt_util.c
|
||||
+libcrypt-routines += crypt crypt_util crypt_blowfish x86 crypt_gensalt wrapper
|
||||
+dont_distribute += crypt.c crypt_util.c crypt_blowfish.c x86.S crypt_gensalt.c wrapper.c
|
||||
endif
|
||||
--- glibc-2.1.3.orig/crypt/sysdeps/unix/crypt-entry.c 1998-12-10 12:49:04 +0000
|
||||
+++ glibc-2.1.3/crypt/sysdeps/unix/crypt-entry.c 2000-06-11 03:14:57 +0000
|
||||
@@ -70,7 +70,7 @@ extern struct crypt_data _ufc_foobar;
|
||||
*/
|
||||
|
||||
char *
|
||||
-__crypt_r (key, salt, data)
|
||||
+__des_crypt_r (key, salt, data)
|
||||
const char *key;
|
||||
const char *salt;
|
||||
struct crypt_data * __restrict data;
|
||||
@@ -115,6 +115,7 @@ __crypt_r (key, salt, data)
|
||||
_ufc_output_conversion_r (res[0], res[1], salt, data);
|
||||
return data->crypt_3_buf;
|
||||
}
|
||||
+#if 0
|
||||
weak_alias (__crypt_r, crypt_r)
|
||||
|
||||
char *
|
||||
@@ -147,3 +148,4 @@ __fcrypt (key, salt)
|
||||
return crypt (key, salt);
|
||||
}
|
||||
#endif
|
||||
+#endif
|
||||
--- glibc-2.1.3.orig/md5-crypt/Makefile 1998-07-02 22:46:47 +0000
|
||||
+++ glibc-2.1.3/md5-crypt/Makefile 2000-06-11 03:12:34 +0000
|
||||
@@ -21,7 +21,7 @@
|
||||
#
|
||||
subdir := md5-crypt
|
||||
|
||||
-headers := crypt.h
|
||||
+headers := crypt.h gnu-crypt.h ow-crypt.h
|
||||
|
||||
distribute := md5.h
|
||||
|
||||
--- glibc-2.1.3.orig/md5-crypt/Versions 1998-07-02 22:32:07 +0000
|
||||
+++ glibc-2.1.3/md5-crypt/Versions 2000-06-11 09:11:03 +0000
|
||||
@@ -1,5 +1,6 @@
|
||||
libcrypt {
|
||||
GLIBC_2.0 {
|
||||
crypt; crypt_r; encrypt; encrypt_r; fcrypt; setkey; setkey_r;
|
||||
+ crypt_rn; crypt_ra; crypt_gensalt; crypt_gensalt_rn; crypt_gensalt_ra;
|
||||
}
|
||||
}
|
55
deps/crypt_blowfish/glibc-2.14-crypt.diff
vendored
55
deps/crypt_blowfish/glibc-2.14-crypt.diff
vendored
@ -1,55 +0,0 @@
|
||||
diff -urp glibc-2.14.orig/crypt/Makefile glibc-2.14/crypt/Makefile
|
||||
--- glibc-2.14.orig/crypt/Makefile 2011-05-31 04:12:33 +0000
|
||||
+++ glibc-2.14/crypt/Makefile 2011-07-16 21:40:56 +0000
|
||||
@@ -22,6 +22,7 @@
|
||||
subdir := crypt
|
||||
|
||||
headers := crypt.h
|
||||
+headers += gnu-crypt.h ow-crypt.h
|
||||
|
||||
extra-libs := libcrypt
|
||||
extra-libs-others := $(extra-libs)
|
||||
@@ -29,6 +30,8 @@ extra-libs-others := $(extra-libs)
|
||||
libcrypt-routines := crypt-entry md5-crypt sha256-crypt sha512-crypt crypt \
|
||||
crypt_util
|
||||
|
||||
+libcrypt-routines += crypt_blowfish x86 crypt_gensalt wrapper
|
||||
+
|
||||
tests := cert md5c-test sha256c-test sha512c-test
|
||||
|
||||
distribute := ufc-crypt.h crypt-private.h ufc.c speeds.c README.ufc-crypt \
|
||||
diff -urp glibc-2.14.orig/crypt/Versions glibc-2.14/crypt/Versions
|
||||
--- glibc-2.14.orig/crypt/Versions 2011-05-31 04:12:33 +0000
|
||||
+++ glibc-2.14/crypt/Versions 2011-07-16 21:40:56 +0000
|
||||
@@ -1,5 +1,6 @@
|
||||
libcrypt {
|
||||
GLIBC_2.0 {
|
||||
crypt; crypt_r; encrypt; encrypt_r; fcrypt; setkey; setkey_r;
|
||||
+ crypt_rn; crypt_ra; crypt_gensalt; crypt_gensalt_rn; crypt_gensalt_ra;
|
||||
}
|
||||
}
|
||||
diff -urp glibc-2.14.orig/crypt/crypt-entry.c glibc-2.14/crypt/crypt-entry.c
|
||||
--- glibc-2.14.orig/crypt/crypt-entry.c 2011-05-31 04:12:33 +0000
|
||||
+++ glibc-2.14/crypt/crypt-entry.c 2011-07-16 21:40:56 +0000
|
||||
@@ -82,7 +82,7 @@ extern struct crypt_data _ufc_foobar;
|
||||
*/
|
||||
|
||||
char *
|
||||
-__crypt_r (key, salt, data)
|
||||
+__des_crypt_r (key, salt, data)
|
||||
const char *key;
|
||||
const char *salt;
|
||||
struct crypt_data * __restrict data;
|
||||
@@ -137,6 +137,7 @@ __crypt_r (key, salt, data)
|
||||
_ufc_output_conversion_r (res[0], res[1], salt, data);
|
||||
return data->crypt_3_buf;
|
||||
}
|
||||
+#if 0
|
||||
weak_alias (__crypt_r, crypt_r)
|
||||
|
||||
char *
|
||||
@@ -177,3 +178,4 @@ __fcrypt (key, salt)
|
||||
return crypt (key, salt);
|
||||
}
|
||||
#endif
|
||||
+#endif
|
52
deps/crypt_blowfish/glibc-2.3.6-crypt.diff
vendored
52
deps/crypt_blowfish/glibc-2.3.6-crypt.diff
vendored
@ -1,52 +0,0 @@
|
||||
--- glibc-2.3.6.orig/crypt/Makefile 2001-07-06 04:54:45 +0000
|
||||
+++ glibc-2.3.6/crypt/Makefile 2004-02-27 00:23:48 +0000
|
||||
@@ -21,14 +21,14 @@
|
||||
#
|
||||
subdir := crypt
|
||||
|
||||
-headers := crypt.h
|
||||
+headers := crypt.h gnu-crypt.h ow-crypt.h
|
||||
|
||||
distribute := md5.h
|
||||
|
||||
extra-libs := libcrypt
|
||||
extra-libs-others := $(extra-libs)
|
||||
|
||||
-libcrypt-routines := crypt-entry md5-crypt md5 crypt crypt_util
|
||||
+libcrypt-routines := crypt-entry md5-crypt md5 crypt crypt_util crypt_blowfish x86 crypt_gensalt wrapper
|
||||
|
||||
tests = cert md5test md5c-test
|
||||
|
||||
--- glibc-2.3.6.orig/crypt/Versions 2000-03-04 00:47:30 +0000
|
||||
+++ glibc-2.3.6/crypt/Versions 2004-02-27 00:25:15 +0000
|
||||
@@ -1,5 +1,6 @@
|
||||
libcrypt {
|
||||
GLIBC_2.0 {
|
||||
crypt; crypt_r; encrypt; encrypt_r; fcrypt; setkey; setkey_r;
|
||||
+ crypt_rn; crypt_ra; crypt_gensalt; crypt_gensalt_rn; crypt_gensalt_ra;
|
||||
}
|
||||
}
|
||||
--- glibc-2.3.6.orig/crypt/crypt-entry.c 2001-07-06 05:18:49 +0000
|
||||
+++ glibc-2.3.6/crypt/crypt-entry.c 2004-02-27 00:12:32 +0000
|
||||
@@ -70,7 +70,7 @@ extern struct crypt_data _ufc_foobar;
|
||||
*/
|
||||
|
||||
char *
|
||||
-__crypt_r (key, salt, data)
|
||||
+__des_crypt_r (key, salt, data)
|
||||
const char *key;
|
||||
const char *salt;
|
||||
struct crypt_data * __restrict data;
|
||||
@@ -115,6 +115,7 @@ __crypt_r (key, salt, data)
|
||||
_ufc_output_conversion_r (res[0], res[1], salt, data);
|
||||
return data->crypt_3_buf;
|
||||
}
|
||||
+#if 0
|
||||
weak_alias (__crypt_r, crypt_r)
|
||||
|
||||
char *
|
||||
@@ -147,3 +148,4 @@ __fcrypt (key, salt)
|
||||
return crypt (key, salt);
|
||||
}
|
||||
#endif
|
||||
+#endif
|
43
deps/crypt_blowfish/ow-crypt.h
vendored
43
deps/crypt_blowfish/ow-crypt.h
vendored
@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Written by Solar Designer <solar at openwall.com> in 2000-2011.
|
||||
* No copyright is claimed, and the software is hereby placed in the public
|
||||
* domain. In case this attempt to disclaim copyright and place the software
|
||||
* in the public domain is deemed null and void, then the software is
|
||||
* Copyright (c) 2000-2011 Solar Designer and it is hereby released to the
|
||||
* general public under the following terms:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted.
|
||||
*
|
||||
* There's ABSOLUTELY NO WARRANTY, express or implied.
|
||||
*
|
||||
* See crypt_blowfish.c for more information.
|
||||
*/
|
||||
|
||||
#ifndef _OW_CRYPT_H
|
||||
#define _OW_CRYPT_H
|
||||
|
||||
#ifndef __GNUC__
|
||||
#undef __const
|
||||
#define __const const
|
||||
#endif
|
||||
|
||||
#ifndef __SKIP_GNU
|
||||
extern char *crypt(__const char *key, __const char *setting);
|
||||
extern char *crypt_r(__const char *key, __const char *setting, void *data);
|
||||
#endif
|
||||
|
||||
#ifndef __SKIP_OW
|
||||
extern char *crypt_rn(__const char *key, __const char *setting,
|
||||
void *data, int size);
|
||||
extern char *crypt_ra(__const char *key, __const char *setting,
|
||||
void **data, int *size);
|
||||
extern char *crypt_gensalt(__const char *prefix, unsigned long count,
|
||||
__const char *input, int size);
|
||||
extern char *crypt_gensalt_rn(__const char *prefix, unsigned long count,
|
||||
__const char *input, int size, char *output, int output_size);
|
||||
extern char *crypt_gensalt_ra(__const char *prefix, unsigned long count,
|
||||
__const char *input, int size);
|
||||
#endif
|
||||
|
||||
#endif
|
551
deps/crypt_blowfish/wrapper.c
vendored
551
deps/crypt_blowfish/wrapper.c
vendored
@ -1,551 +0,0 @@
|
||||
/*
|
||||
* Written by Solar Designer <solar at openwall.com> in 2000-2014.
|
||||
* No copyright is claimed, and the software is hereby placed in the public
|
||||
* domain. In case this attempt to disclaim copyright and place the software
|
||||
* in the public domain is deemed null and void, then the software is
|
||||
* Copyright (c) 2000-2014 Solar Designer and it is hereby released to the
|
||||
* general public under the following terms:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted.
|
||||
*
|
||||
* There's ABSOLUTELY NO WARRANTY, express or implied.
|
||||
*
|
||||
* See crypt_blowfish.c for more information.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <errno.h>
|
||||
#ifndef __set_errno
|
||||
#define __set_errno(val) errno = (val)
|
||||
#endif
|
||||
|
||||
#ifdef TEST
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/times.h>
|
||||
#ifdef TEST_THREADS
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define CRYPT_OUTPUT_SIZE (7 + 22 + 31 + 1)
|
||||
#define CRYPT_GENSALT_OUTPUT_SIZE (7 + 22 + 1)
|
||||
|
||||
#if defined(__GLIBC__) && defined(_LIBC)
|
||||
#define __SKIP_GNU
|
||||
#endif
|
||||
#include "ow-crypt.h"
|
||||
|
||||
#include "crypt_blowfish.h"
|
||||
#include "crypt_gensalt.h"
|
||||
|
||||
#if defined(__GLIBC__) && defined(_LIBC)
|
||||
/* crypt.h from glibc-crypt-2.1 will define struct crypt_data for us */
|
||||
#include "crypt.h"
|
||||
extern char *__md5_crypt_r(const char *key, const char *salt,
|
||||
char *buffer, int buflen);
|
||||
/* crypt-entry.c needs to be patched to define __des_crypt_r rather than
|
||||
* __crypt_r, and not define crypt_r and crypt at all */
|
||||
extern char *__des_crypt_r(const char *key, const char *salt,
|
||||
struct crypt_data *data);
|
||||
extern struct crypt_data _ufc_foobar;
|
||||
#endif
|
||||
|
||||
static int _crypt_data_alloc(void **data, int *size, int need)
|
||||
{
|
||||
void *updated;
|
||||
|
||||
if (*data && *size >= need) return 0;
|
||||
|
||||
updated = realloc(*data, need);
|
||||
|
||||
if (!updated) {
|
||||
#ifndef __GLIBC__
|
||||
/* realloc(3) on glibc sets errno, so we don't need to bother */
|
||||
__set_errno(ENOMEM);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(__GLIBC__) && defined(_LIBC)
|
||||
if (need >= sizeof(struct crypt_data))
|
||||
((struct crypt_data *)updated)->initialized = 0;
|
||||
#endif
|
||||
|
||||
*data = updated;
|
||||
*size = need;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *_crypt_retval_magic(char *retval, const char *setting,
|
||||
char *output, int size)
|
||||
{
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (_crypt_output_magic(setting, output, size))
|
||||
return NULL; /* shouldn't happen */
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
#if defined(__GLIBC__) && defined(_LIBC)
|
||||
/*
|
||||
* Applications may re-use the same instance of struct crypt_data without
|
||||
* resetting the initialized field in order to let crypt_r() skip some of
|
||||
* its initialization code. Thus, it is important that our multiple hashing
|
||||
* algorithms either don't conflict with each other in their use of the
|
||||
* data area or reset the initialized field themselves whenever required.
|
||||
* Currently, the hashing algorithms simply have no conflicts: the first
|
||||
* field of struct crypt_data is the 128-byte large DES key schedule which
|
||||
* __des_crypt_r() calculates each time it is called while the two other
|
||||
* hashing algorithms use less than 128 bytes of the data area.
|
||||
*/
|
||||
|
||||
char *__crypt_rn(__const char *key, __const char *setting,
|
||||
void *data, int size)
|
||||
{
|
||||
if (setting[0] == '$' && setting[1] == '2')
|
||||
return _crypt_blowfish_rn(key, setting, (char *)data, size);
|
||||
if (setting[0] == '$' && setting[1] == '1')
|
||||
return __md5_crypt_r(key, setting, (char *)data, size);
|
||||
if (setting[0] == '$' || setting[0] == '_') {
|
||||
__set_errno(EINVAL);
|
||||
return NULL;
|
||||
}
|
||||
if (size >= sizeof(struct crypt_data))
|
||||
return __des_crypt_r(key, setting, (struct crypt_data *)data);
|
||||
__set_errno(ERANGE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *__crypt_ra(__const char *key, __const char *setting,
|
||||
void **data, int *size)
|
||||
{
|
||||
if (setting[0] == '$' && setting[1] == '2') {
|
||||
if (_crypt_data_alloc(data, size, CRYPT_OUTPUT_SIZE))
|
||||
return NULL;
|
||||
return _crypt_blowfish_rn(key, setting, (char *)*data, *size);
|
||||
}
|
||||
if (setting[0] == '$' && setting[1] == '1') {
|
||||
if (_crypt_data_alloc(data, size, CRYPT_OUTPUT_SIZE))
|
||||
return NULL;
|
||||
return __md5_crypt_r(key, setting, (char *)*data, *size);
|
||||
}
|
||||
if (setting[0] == '$' || setting[0] == '_') {
|
||||
__set_errno(EINVAL);
|
||||
return NULL;
|
||||
}
|
||||
if (_crypt_data_alloc(data, size, sizeof(struct crypt_data)))
|
||||
return NULL;
|
||||
return __des_crypt_r(key, setting, (struct crypt_data *)*data);
|
||||
}
|
||||
|
||||
char *__crypt_r(__const char *key, __const char *setting,
|
||||
struct crypt_data *data)
|
||||
{
|
||||
return _crypt_retval_magic(
|
||||
__crypt_rn(key, setting, data, sizeof(*data)),
|
||||
setting, (char *)data, sizeof(*data));
|
||||
}
|
||||
|
||||
char *__crypt(__const char *key, __const char *setting)
|
||||
{
|
||||
return _crypt_retval_magic(
|
||||
__crypt_rn(key, setting, &_ufc_foobar, sizeof(_ufc_foobar)),
|
||||
setting, (char *)&_ufc_foobar, sizeof(_ufc_foobar));
|
||||
}
|
||||
#else
|
||||
char *crypt_rn(const char *key, const char *setting, void *data, int size)
|
||||
{
|
||||
return _crypt_blowfish_rn(key, setting, (char *)data, size);
|
||||
}
|
||||
|
||||
char *crypt_ra(const char *key, const char *setting,
|
||||
void **data, int *size)
|
||||
{
|
||||
if (_crypt_data_alloc(data, size, CRYPT_OUTPUT_SIZE))
|
||||
return NULL;
|
||||
return _crypt_blowfish_rn(key, setting, (char *)*data, *size);
|
||||
}
|
||||
|
||||
char *crypt_r(const char *key, const char *setting, void *data)
|
||||
{
|
||||
return _crypt_retval_magic(
|
||||
crypt_rn(key, setting, data, CRYPT_OUTPUT_SIZE),
|
||||
setting, (char *)data, CRYPT_OUTPUT_SIZE);
|
||||
}
|
||||
|
||||
char *crypt(const char *key, const char *setting)
|
||||
{
|
||||
static char output[CRYPT_OUTPUT_SIZE];
|
||||
|
||||
return _crypt_retval_magic(
|
||||
crypt_rn(key, setting, output, sizeof(output)),
|
||||
setting, output, sizeof(output));
|
||||
}
|
||||
|
||||
#define __crypt_gensalt_rn crypt_gensalt_rn
|
||||
#define __crypt_gensalt_ra crypt_gensalt_ra
|
||||
#define __crypt_gensalt crypt_gensalt
|
||||
#endif
|
||||
|
||||
char *__crypt_gensalt_rn(const char *prefix, unsigned long count,
|
||||
const char *input, int size, char *output, int output_size)
|
||||
{
|
||||
char *(*use)(const char *_prefix, unsigned long _count,
|
||||
const char *_input, int _size,
|
||||
char *_output, int _output_size);
|
||||
|
||||
/* This may be supported on some platforms in the future */
|
||||
if (!input) {
|
||||
__set_errno(EINVAL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!strncmp(prefix, "$2a$", 4) || !strncmp(prefix, "$2b$", 4) ||
|
||||
!strncmp(prefix, "$2y$", 4))
|
||||
use = _crypt_gensalt_blowfish_rn;
|
||||
else
|
||||
if (!strncmp(prefix, "$1$", 3))
|
||||
use = _crypt_gensalt_md5_rn;
|
||||
else
|
||||
if (prefix[0] == '_')
|
||||
use = _crypt_gensalt_extended_rn;
|
||||
else
|
||||
if (!prefix[0] ||
|
||||
(prefix[0] && prefix[1] &&
|
||||
memchr(_crypt_itoa64, prefix[0], 64) &&
|
||||
memchr(_crypt_itoa64, prefix[1], 64)))
|
||||
use = _crypt_gensalt_traditional_rn;
|
||||
else {
|
||||
__set_errno(EINVAL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return use(prefix, count, input, size, output, output_size);
|
||||
}
|
||||
|
||||
char *__crypt_gensalt_ra(const char *prefix, unsigned long count,
|
||||
const char *input, int size)
|
||||
{
|
||||
char output[CRYPT_GENSALT_OUTPUT_SIZE];
|
||||
char *retval;
|
||||
|
||||
retval = __crypt_gensalt_rn(prefix, count,
|
||||
input, size, output, sizeof(output));
|
||||
|
||||
if (retval) {
|
||||
retval = strdup(retval);
|
||||
#ifndef __GLIBC__
|
||||
/* strdup(3) on glibc sets errno, so we don't need to bother */
|
||||
if (!retval)
|
||||
__set_errno(ENOMEM);
|
||||
#endif
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
char *__crypt_gensalt(const char *prefix, unsigned long count,
|
||||
const char *input, int size)
|
||||
{
|
||||
static char output[CRYPT_GENSALT_OUTPUT_SIZE];
|
||||
|
||||
return __crypt_gensalt_rn(prefix, count,
|
||||
input, size, output, sizeof(output));
|
||||
}
|
||||
|
||||
#if defined(__GLIBC__) && defined(_LIBC)
|
||||
weak_alias(__crypt_rn, crypt_rn)
|
||||
weak_alias(__crypt_ra, crypt_ra)
|
||||
weak_alias(__crypt_r, crypt_r)
|
||||
weak_alias(__crypt, crypt)
|
||||
weak_alias(__crypt_gensalt_rn, crypt_gensalt_rn)
|
||||
weak_alias(__crypt_gensalt_ra, crypt_gensalt_ra)
|
||||
weak_alias(__crypt_gensalt, crypt_gensalt)
|
||||
weak_alias(crypt, fcrypt)
|
||||
#endif
|
||||
|
||||
#ifdef TEST
|
||||
static const char *tests[][3] = {
|
||||
{"$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW",
|
||||
"U*U"},
|
||||
{"$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK",
|
||||
"U*U*"},
|
||||
{"$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a",
|
||||
"U*U*U"},
|
||||
{"$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui",
|
||||
"0123456789abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
"chars after 72 are ignored"},
|
||||
{"$2x$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
|
||||
"\xa3"},
|
||||
{"$2x$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
|
||||
"\xff\xff\xa3"},
|
||||
{"$2y$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
|
||||
"\xff\xff\xa3"},
|
||||
{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.nqd1wy.pTMdcvrRWxyiGL2eMz.2a85.",
|
||||
"\xff\xff\xa3"},
|
||||
{"$2b$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
|
||||
"\xff\xff\xa3"},
|
||||
{"$2y$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq",
|
||||
"\xa3"},
|
||||
{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq",
|
||||
"\xa3"},
|
||||
{"$2b$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq",
|
||||
"\xa3"},
|
||||
{"$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
|
||||
"1\xa3" "345"},
|
||||
{"$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
|
||||
"\xff\xa3" "345"},
|
||||
{"$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
|
||||
"\xff\xa3" "34" "\xff\xff\xff\xa3" "345"},
|
||||
{"$2y$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
|
||||
"\xff\xa3" "34" "\xff\xff\xff\xa3" "345"},
|
||||
{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.ZC1JEJ8Z4gPfpe1JOr/oyPXTWl9EFd.",
|
||||
"\xff\xa3" "34" "\xff\xff\xff\xa3" "345"},
|
||||
{"$2y$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e",
|
||||
"\xff\xa3" "345"},
|
||||
{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e",
|
||||
"\xff\xa3" "345"},
|
||||
{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS",
|
||||
"\xa3" "ab"},
|
||||
{"$2x$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS",
|
||||
"\xa3" "ab"},
|
||||
{"$2y$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS",
|
||||
"\xa3" "ab"},
|
||||
{"$2x$05$6bNw2HLQYeqHYyBfLMsv/OiwqTymGIGzFsA4hOTWebfehXHNprcAS",
|
||||
"\xd1\x91"},
|
||||
{"$2x$05$6bNw2HLQYeqHYyBfLMsv/O9LIGgn8OMzuDoHfof8AQimSGfcSWxnS",
|
||||
"\xd0\xc1\xd2\xcf\xcc\xd8"},
|
||||
{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6",
|
||||
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
|
||||
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
|
||||
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
|
||||
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
|
||||
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
|
||||
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
|
||||
"chars after 72 are ignored as usual"},
|
||||
{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.R9xrDjiycxMbQE2bp.vgqlYpW5wx2yy",
|
||||
"\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
|
||||
"\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
|
||||
"\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
|
||||
"\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
|
||||
"\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
|
||||
"\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"},
|
||||
{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.9tQZzcJfm3uj2NvJ/n5xkhpqLrMpWCe",
|
||||
"\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
|
||||
"\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
|
||||
"\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
|
||||
"\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
|
||||
"\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
|
||||
"\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"},
|
||||
{"$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy",
|
||||
""},
|
||||
{"*0", "", "$2a$03$CCCCCCCCCCCCCCCCCCCCC."},
|
||||
{"*0", "", "$2a$32$CCCCCCCCCCCCCCCCCCCCC."},
|
||||
{"*0", "", "$2c$05$CCCCCCCCCCCCCCCCCCCCC."},
|
||||
{"*0", "", "$2z$05$CCCCCCCCCCCCCCCCCCCCC."},
|
||||
{"*0", "", "$2`$05$CCCCCCCCCCCCCCCCCCCCC."},
|
||||
{"*0", "", "$2{$05$CCCCCCCCCCCCCCCCCCCCC."},
|
||||
{"*1", "", "*0"},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
#define which tests[0]
|
||||
|
||||
static volatile sig_atomic_t running;
|
||||
|
||||
static void handle_timer(int signum)
|
||||
{
|
||||
(void) signum;
|
||||
running = 0;
|
||||
}
|
||||
|
||||
static void *run(void *arg)
|
||||
{
|
||||
unsigned long count = 0;
|
||||
int i = 0;
|
||||
void *data = NULL;
|
||||
int size = 0x12345678;
|
||||
|
||||
do {
|
||||
const char *hash = tests[i][0];
|
||||
const char *key = tests[i][1];
|
||||
const char *setting = tests[i][2];
|
||||
|
||||
if (!tests[++i][0])
|
||||
i = 0;
|
||||
|
||||
if (setting && strlen(hash) < 30) /* not for benchmark */
|
||||
continue;
|
||||
|
||||
if (strcmp(crypt_ra(key, hash, &data, &size), hash)) {
|
||||
printf("%d: FAILED (crypt_ra/%d/%lu)\n",
|
||||
(int)((char *)arg - (char *)0), i, count);
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
count++;
|
||||
} while (running);
|
||||
|
||||
free(data);
|
||||
return count + (char *)0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct itimerval it;
|
||||
struct tms buf;
|
||||
clock_t clk_tck, start_real, start_virtual, end_real, end_virtual;
|
||||
unsigned long count;
|
||||
void *data;
|
||||
int size;
|
||||
char *setting1, *setting2;
|
||||
int i;
|
||||
#ifdef TEST_THREADS
|
||||
pthread_t t[TEST_THREADS];
|
||||
void *t_retval;
|
||||
#endif
|
||||
|
||||
data = NULL;
|
||||
size = 0x12345678;
|
||||
|
||||
for (i = 0; tests[i][0]; i++) {
|
||||
const char *hash = tests[i][0];
|
||||
const char *key = tests[i][1];
|
||||
const char *setting = tests[i][2];
|
||||
const char *p;
|
||||
int ok = !setting || strlen(hash) >= 30;
|
||||
int o_size;
|
||||
char s_buf[30], o_buf[61];
|
||||
if (!setting) {
|
||||
memcpy(s_buf, hash, sizeof(s_buf) - 1);
|
||||
s_buf[sizeof(s_buf) - 1] = 0;
|
||||
setting = s_buf;
|
||||
}
|
||||
|
||||
__set_errno(0);
|
||||
p = crypt(key, setting);
|
||||
if ((!ok && !errno) || strcmp(p, hash)) {
|
||||
printf("FAILED (crypt/%d)\n", i);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ok && strcmp(crypt(key, hash), hash)) {
|
||||
printf("FAILED (crypt/%d)\n", i);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (o_size = -1; o_size <= (int)sizeof(o_buf); o_size++) {
|
||||
int ok_n = ok && o_size == (int)sizeof(o_buf);
|
||||
const char *x = "abc";
|
||||
strcpy(o_buf, x);
|
||||
if (o_size >= 3) {
|
||||
x = "*0";
|
||||
if (setting[0] == '*' && setting[1] == '0')
|
||||
x = "*1";
|
||||
}
|
||||
__set_errno(0);
|
||||
p = crypt_rn(key, setting, o_buf, o_size);
|
||||
if ((ok_n && (!p || strcmp(p, hash))) ||
|
||||
(!ok_n && (!errno || p || strcmp(o_buf, x)))) {
|
||||
printf("FAILED (crypt_rn/%d)\n", i);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
__set_errno(0);
|
||||
p = crypt_ra(key, setting, &data, &size);
|
||||
if ((ok && (!p || strcmp(p, hash))) ||
|
||||
(!ok && (!errno || p || strcmp((char *)data, hash)))) {
|
||||
printf("FAILED (crypt_ra/%d)\n", i);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
setting1 = crypt_gensalt(which[0], 12, data, size);
|
||||
if (!setting1 || strncmp(setting1, "$2a$12$", 7)) {
|
||||
puts("FAILED (crypt_gensalt)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
setting2 = crypt_gensalt_ra(setting1, 12, data, size);
|
||||
if (strcmp(setting1, setting2)) {
|
||||
puts("FAILED (crypt_gensalt_ra/1)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
(*(char *)data)++;
|
||||
setting1 = crypt_gensalt_ra(setting2, 12, data, size);
|
||||
if (!strcmp(setting1, setting2)) {
|
||||
puts("FAILED (crypt_gensalt_ra/2)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
free(setting1);
|
||||
free(setting2);
|
||||
free(data);
|
||||
|
||||
#if defined(_SC_CLK_TCK) || !defined(CLK_TCK)
|
||||
clk_tck = sysconf(_SC_CLK_TCK);
|
||||
#else
|
||||
clk_tck = CLK_TCK;
|
||||
#endif
|
||||
|
||||
running = 1;
|
||||
signal(SIGALRM, handle_timer);
|
||||
|
||||
memset(&it, 0, sizeof(it));
|
||||
it.it_value.tv_sec = 5;
|
||||
setitimer(ITIMER_REAL, &it, NULL);
|
||||
|
||||
start_real = times(&buf);
|
||||
start_virtual = buf.tms_utime + buf.tms_stime;
|
||||
|
||||
count = (char *)run((char *)0) - (char *)0;
|
||||
|
||||
end_real = times(&buf);
|
||||
end_virtual = buf.tms_utime + buf.tms_stime;
|
||||
if (end_virtual == start_virtual) end_virtual++;
|
||||
|
||||
printf("%.1f c/s real, %.1f c/s virtual\n",
|
||||
(float)count * clk_tck / (end_real - start_real),
|
||||
(float)count * clk_tck / (end_virtual - start_virtual));
|
||||
|
||||
#ifdef TEST_THREADS
|
||||
running = 1;
|
||||
it.it_value.tv_sec = 60;
|
||||
setitimer(ITIMER_REAL, &it, NULL);
|
||||
start_real = times(&buf);
|
||||
|
||||
for (i = 0; i < TEST_THREADS; i++)
|
||||
if (pthread_create(&t[i], NULL, run, i + (char *)0)) {
|
||||
perror("pthread_create");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < TEST_THREADS; i++) {
|
||||
if (pthread_join(t[i], &t_retval)) {
|
||||
perror("pthread_join");
|
||||
continue;
|
||||
}
|
||||
if (!t_retval) continue;
|
||||
count = (char *)t_retval - (char *)0;
|
||||
end_real = times(&buf);
|
||||
printf("%d: %.1f c/s real\n", i,
|
||||
(float)count * clk_tck / (end_real - start_real));
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
203
deps/crypt_blowfish/x86.S
vendored
203
deps/crypt_blowfish/x86.S
vendored
@ -1,203 +0,0 @@
|
||||
/*
|
||||
* Written by Solar Designer <solar at openwall.com> in 1998-2010.
|
||||
* No copyright is claimed, and the software is hereby placed in the public
|
||||
* domain. In case this attempt to disclaim copyright and place the software
|
||||
* in the public domain is deemed null and void, then the software is
|
||||
* Copyright (c) 1998-2010 Solar Designer and it is hereby released to the
|
||||
* general public under the following terms:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted.
|
||||
*
|
||||
* There's ABSOLUTELY NO WARRANTY, express or implied.
|
||||
*
|
||||
* See crypt_blowfish.c for more information.
|
||||
*/
|
||||
|
||||
#ifdef __i386__
|
||||
|
||||
#if defined(__OpenBSD__) && !defined(__ELF__)
|
||||
#define UNDERSCORES
|
||||
#define ALIGN_LOG
|
||||
#endif
|
||||
|
||||
#if defined(__CYGWIN32__) || defined(__MINGW32__)
|
||||
#define UNDERSCORES
|
||||
#endif
|
||||
|
||||
#ifdef __DJGPP__
|
||||
#define UNDERSCORES
|
||||
#define ALIGN_LOG
|
||||
#endif
|
||||
|
||||
#ifdef UNDERSCORES
|
||||
#define _BF_body_r __BF_body_r
|
||||
#endif
|
||||
|
||||
#ifdef ALIGN_LOG
|
||||
#define DO_ALIGN(log) .align (log)
|
||||
#elif defined(DUMBAS)
|
||||
#define DO_ALIGN(log) .align 1 << log
|
||||
#else
|
||||
#define DO_ALIGN(log) .align (1 << (log))
|
||||
#endif
|
||||
|
||||
#define BF_FRAME 0x200
|
||||
#define ctx %esp
|
||||
|
||||
#define BF_ptr (ctx)
|
||||
|
||||
#define S(N, r) N+BF_FRAME(ctx,r,4)
|
||||
#ifdef DUMBAS
|
||||
#define P(N) 0x1000+N+N+N+N+BF_FRAME(ctx)
|
||||
#else
|
||||
#define P(N) 0x1000+4*N+BF_FRAME(ctx)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This version of the assembly code is optimized primarily for the original
|
||||
* Intel Pentium but is also careful to avoid partial register stalls on the
|
||||
* Pentium Pro family of processors (tested up to Pentium III Coppermine).
|
||||
*
|
||||
* It is possible to do 15% faster on the Pentium Pro family and probably on
|
||||
* many non-Intel x86 processors, but, unfortunately, that would make things
|
||||
* twice slower for the original Pentium.
|
||||
*
|
||||
* An additional 2% speedup may be achieved with non-reentrant code.
|
||||
*/
|
||||
|
||||
#define L %esi
|
||||
#define R %edi
|
||||
#define tmp1 %eax
|
||||
#define tmp1_lo %al
|
||||
#define tmp2 %ecx
|
||||
#define tmp2_hi %ch
|
||||
#define tmp3 %edx
|
||||
#define tmp3_lo %dl
|
||||
#define tmp4 %ebx
|
||||
#define tmp4_hi %bh
|
||||
#define tmp5 %ebp
|
||||
|
||||
.text
|
||||
|
||||
#define BF_ROUND(L, R, N) \
|
||||
xorl L,tmp2; \
|
||||
xorl tmp1,tmp1; \
|
||||
movl tmp2,L; \
|
||||
shrl $16,tmp2; \
|
||||
movl L,tmp4; \
|
||||
movb tmp2_hi,tmp1_lo; \
|
||||
andl $0xFF,tmp2; \
|
||||
movb tmp4_hi,tmp3_lo; \
|
||||
andl $0xFF,tmp4; \
|
||||
movl S(0,tmp1),tmp1; \
|
||||
movl S(0x400,tmp2),tmp5; \
|
||||
addl tmp5,tmp1; \
|
||||
movl S(0x800,tmp3),tmp5; \
|
||||
xorl tmp5,tmp1; \
|
||||
movl S(0xC00,tmp4),tmp5; \
|
||||
addl tmp1,tmp5; \
|
||||
movl 4+P(N),tmp2; \
|
||||
xorl tmp5,R
|
||||
|
||||
#define BF_ENCRYPT_START \
|
||||
BF_ROUND(L, R, 0); \
|
||||
BF_ROUND(R, L, 1); \
|
||||
BF_ROUND(L, R, 2); \
|
||||
BF_ROUND(R, L, 3); \
|
||||
BF_ROUND(L, R, 4); \
|
||||
BF_ROUND(R, L, 5); \
|
||||
BF_ROUND(L, R, 6); \
|
||||
BF_ROUND(R, L, 7); \
|
||||
BF_ROUND(L, R, 8); \
|
||||
BF_ROUND(R, L, 9); \
|
||||
BF_ROUND(L, R, 10); \
|
||||
BF_ROUND(R, L, 11); \
|
||||
BF_ROUND(L, R, 12); \
|
||||
BF_ROUND(R, L, 13); \
|
||||
BF_ROUND(L, R, 14); \
|
||||
BF_ROUND(R, L, 15); \
|
||||
movl BF_ptr,tmp5; \
|
||||
xorl L,tmp2; \
|
||||
movl P(17),L
|
||||
|
||||
#define BF_ENCRYPT_END \
|
||||
xorl R,L; \
|
||||
movl tmp2,R
|
||||
|
||||
DO_ALIGN(5)
|
||||
.globl _BF_body_r
|
||||
_BF_body_r:
|
||||
movl 4(%esp),%eax
|
||||
pushl %ebp
|
||||
pushl %ebx
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
subl $BF_FRAME-8,%eax
|
||||
xorl L,L
|
||||
cmpl %esp,%eax
|
||||
ja BF_die
|
||||
xchgl %eax,%esp
|
||||
xorl R,R
|
||||
pushl %eax
|
||||
leal 0x1000+BF_FRAME-4(ctx),%eax
|
||||
movl 0x1000+BF_FRAME-4(ctx),tmp2
|
||||
pushl %eax
|
||||
xorl tmp3,tmp3
|
||||
BF_loop_P:
|
||||
BF_ENCRYPT_START
|
||||
addl $8,tmp5
|
||||
BF_ENCRYPT_END
|
||||
leal 0x1000+18*4+BF_FRAME(ctx),tmp1
|
||||
movl tmp5,BF_ptr
|
||||
cmpl tmp5,tmp1
|
||||
movl L,-8(tmp5)
|
||||
movl R,-4(tmp5)
|
||||
movl P(0),tmp2
|
||||
ja BF_loop_P
|
||||
leal BF_FRAME(ctx),tmp5
|
||||
xorl tmp3,tmp3
|
||||
movl tmp5,BF_ptr
|
||||
BF_loop_S:
|
||||
BF_ENCRYPT_START
|
||||
BF_ENCRYPT_END
|
||||
movl P(0),tmp2
|
||||
movl L,(tmp5)
|
||||
movl R,4(tmp5)
|
||||
BF_ENCRYPT_START
|
||||
BF_ENCRYPT_END
|
||||
movl P(0),tmp2
|
||||
movl L,8(tmp5)
|
||||
movl R,12(tmp5)
|
||||
BF_ENCRYPT_START
|
||||
BF_ENCRYPT_END
|
||||
movl P(0),tmp2
|
||||
movl L,16(tmp5)
|
||||
movl R,20(tmp5)
|
||||
BF_ENCRYPT_START
|
||||
addl $32,tmp5
|
||||
BF_ENCRYPT_END
|
||||
leal 0x1000+BF_FRAME(ctx),tmp1
|
||||
movl tmp5,BF_ptr
|
||||
cmpl tmp5,tmp1
|
||||
movl P(0),tmp2
|
||||
movl L,-8(tmp5)
|
||||
movl R,-4(tmp5)
|
||||
ja BF_loop_S
|
||||
movl 4(%esp),%esp
|
||||
popl %edi
|
||||
popl %esi
|
||||
popl %ebx
|
||||
popl %ebp
|
||||
ret
|
||||
|
||||
BF_die:
|
||||
/* Oops, need to re-compile with a larger BF_FRAME. */
|
||||
hlt
|
||||
jmp BF_die
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__ELF__) && defined(__linux__)
|
||||
.section .note.GNU-stack,"",@progbits
|
||||
#endif
|
1
deps/libbacktrace
vendored
Submodule
1
deps/libbacktrace
vendored
Submodule
Submodule deps/libbacktrace added at 11427f31a6
5
deps/libbacktrace/.gitignore
vendored
5
deps/libbacktrace/.gitignore
vendored
@ -1,5 +0,0 @@
|
||||
*~
|
||||
*.o
|
||||
*.lo
|
||||
*.a
|
||||
*.la
|
9286
deps/libbacktrace/Isaac.Newton-Opticks.txt
vendored
9286
deps/libbacktrace/Isaac.Newton-Opticks.txt
vendored
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user