Compare commits
86 Commits
v0.0.33
...
3ef795452d
| Author | SHA1 | Date | |
|---|---|---|---|
| 3ef795452d | |||
| 479d87c8b8 | |||
| a56077dcc7 | |||
| d3f4587c3b | |||
| 623705b7a1 | |||
| 8f87f4751d | |||
| 2ac6dfde9d | |||
| 81ade7a400 | |||
| 63f7ff9f27 | |||
| 8a0fa17a79 | |||
| 0ead5ed967 | |||
| 53261a6fbc | |||
| c60ff86a4d | |||
| 83a0b017c5 | |||
| 3746622a11 | |||
| ccd50cf59f | |||
| 93680eb43d | |||
| 3ae4b7086a | |||
| 446b1f8600 | |||
| 00fd208a2c | |||
| e574d03716 | |||
| c1f3116c9d | |||
| 3aec7e6c14 | |||
| 9f0020dec8 | |||
| 6e78ad9729 | |||
| 44d84a9b2a | |||
| ac7809415c | |||
| 675cecaa20 | |||
| 5d179cc088 | |||
| d905618590 | |||
| 3fd9bc0b18 | |||
| 39abee7f73 | |||
| b770619111 | |||
| 1c44857da4 | |||
| bca4440867 | |||
| 4855543961 | |||
| cb3d6a98b9 | |||
| ada67a13d3 | |||
| f4c928f26e | |||
| 91fd515d39 | |||
| be6e841d3d | |||
| af6afa6903 | |||
| 6ab5d2a28d | |||
| 4be033f288 | |||
| c550f92003 | |||
| ed836b3ee0 | |||
| ac7a43abf4 | |||
| 49f19fce91 | |||
| b2197eb8e9 | |||
| 5fbc2cae1c | |||
| 730abb49ce | |||
| edccab054a | |||
| e8210c6fdd | |||
| 55d69d7c13 | |||
| 50fb18d4ff | |||
| 77b1ea1fc8 | |||
| 61501a9b64 | |||
| c1507adac5 | |||
| 45fb9eda1c | |||
| 982a61f4bf | |||
| 18e5b41663 | |||
| 910c39cbd0 | |||
| 9952dfd49d | |||
| 00f75d5382 | |||
| d78828554b | |||
| b84b561109 | |||
| a618815500 | |||
| e1f3dc6ae4 | |||
| f378db6c6f | |||
| 7cec0f7d61 | |||
| f902d0374c | |||
| b5f0a0c4f7 | |||
| 00623cea09 | |||
| ed4f1d6f2c | |||
| 73f4a3407f | |||
| 6f11318e84 | |||
| e88ee91f0e | |||
| 3f8daf257c | |||
| dc387acadc | |||
| 68aa41ab96 | |||
| 85b23437b3 | |||
| c59fba817d | |||
| c3415ab75c | |||
| f1d0151d71 | |||
| 3c5c1756d1 | |||
| 6a6b65d1b3 |
358
Doxyfile
358
Doxyfile
@@ -1,4 +1,4 @@
|
|||||||
# Doxyfile 1.9.8
|
# Doxyfile 1.9.4
|
||||||
|
|
||||||
# This file describes the settings to be used by the documentation system
|
# This file describes the settings to be used by the documentation system
|
||||||
# doxygen (www.doxygen.org) for a project.
|
# doxygen (www.doxygen.org) for a project.
|
||||||
@@ -19,8 +19,7 @@
|
|||||||
# configuration file:
|
# configuration file:
|
||||||
# doxygen -x [configFile]
|
# doxygen -x [configFile]
|
||||||
# Use doxygen to compare the used configuration file with the template
|
# Use doxygen to compare the used configuration file with the template
|
||||||
# configuration file without replacing the environment variables or CMake type
|
# configuration file without replacing the environment variables:
|
||||||
# replacement variables:
|
|
||||||
# doxygen -x_noenv [configFile]
|
# doxygen -x_noenv [configFile]
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
@@ -86,7 +85,7 @@ CREATE_SUBDIRS = NO
|
|||||||
# level increment doubles the number of directories, resulting in 4096
|
# level increment doubles the number of directories, resulting in 4096
|
||||||
# directories at level 8 which is the default and also the maximum value. The
|
# directories at level 8 which is the default and also the maximum value. The
|
||||||
# sub-directories are organized in 2 levels, the first level always has a fixed
|
# sub-directories are organized in 2 levels, the first level always has a fixed
|
||||||
# number of 16 directories.
|
# numer of 16 directories.
|
||||||
# Minimum value: 0, maximum value: 8, default value: 8.
|
# Minimum value: 0, maximum value: 8, default value: 8.
|
||||||
# This tag requires that the tag CREATE_SUBDIRS is set to YES.
|
# This tag requires that the tag CREATE_SUBDIRS is set to YES.
|
||||||
|
|
||||||
@@ -363,17 +362,6 @@ MARKDOWN_SUPPORT = YES
|
|||||||
|
|
||||||
TOC_INCLUDE_HEADINGS = 5
|
TOC_INCLUDE_HEADINGS = 5
|
||||||
|
|
||||||
# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to
|
|
||||||
# generate identifiers for the Markdown headings. Note: Every identifier is
|
|
||||||
# unique.
|
|
||||||
# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a
|
|
||||||
# sequence number starting at 0 and GITHUB use the lower case version of title
|
|
||||||
# with any whitespace replaced by '-' and punctuation characters removed.
|
|
||||||
# The default value is: DOXYGEN.
|
|
||||||
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
|
|
||||||
|
|
||||||
MARKDOWN_ID_STYLE = DOXYGEN
|
|
||||||
|
|
||||||
# When enabled doxygen tries to link words that correspond to documented
|
# When enabled doxygen tries to link words that correspond to documented
|
||||||
# classes, or namespaces to their corresponding documentation. Such a link can
|
# classes, or namespaces to their corresponding documentation. Such a link can
|
||||||
# be prevented in individual cases by putting a % sign in front of the word or
|
# be prevented in individual cases by putting a % sign in front of the word or
|
||||||
@@ -498,14 +486,6 @@ LOOKUP_CACHE_SIZE = 0
|
|||||||
|
|
||||||
NUM_PROC_THREADS = 1
|
NUM_PROC_THREADS = 1
|
||||||
|
|
||||||
# If the TIMESTAMP tag is set different from NO then each generated page will
|
|
||||||
# contain the date or date and time when the page was generated. Setting this to
|
|
||||||
# NO can help when comparing the output of multiple runs.
|
|
||||||
# Possible values are: YES, NO, DATETIME and DATE.
|
|
||||||
# The default value is: NO.
|
|
||||||
|
|
||||||
TIMESTAMP = NO
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
# Build related configuration options
|
# Build related configuration options
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
@@ -587,8 +567,7 @@ HIDE_UNDOC_MEMBERS = NO
|
|||||||
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
|
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
|
||||||
# undocumented classes that are normally visible in the class hierarchy. If set
|
# undocumented classes that are normally visible in the class hierarchy. If set
|
||||||
# to NO, these classes will be included in the various overviews. This option
|
# to NO, these classes will be included in the various overviews. This option
|
||||||
# will also hide undocumented C++ concepts if enabled. This option has no effect
|
# has no effect if EXTRACT_ALL is enabled.
|
||||||
# if EXTRACT_ALL is enabled.
|
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
|
|
||||||
HIDE_UNDOC_CLASSES = NO
|
HIDE_UNDOC_CLASSES = NO
|
||||||
@@ -626,8 +605,7 @@ INTERNAL_DOCS = NO
|
|||||||
# Windows (including Cygwin) and MacOS, users should typically set this option
|
# Windows (including Cygwin) and MacOS, users should typically set this option
|
||||||
# to NO, whereas on Linux or other Unix flavors it should typically be set to
|
# to NO, whereas on Linux or other Unix flavors it should typically be set to
|
||||||
# YES.
|
# YES.
|
||||||
# Possible values are: SYSTEM, NO and YES.
|
# The default value is: system dependent.
|
||||||
# The default value is: SYSTEM.
|
|
||||||
|
|
||||||
CASE_SENSE_NAMES = YES
|
CASE_SENSE_NAMES = YES
|
||||||
|
|
||||||
@@ -879,26 +857,11 @@ WARN_IF_INCOMPLETE_DOC = YES
|
|||||||
|
|
||||||
WARN_NO_PARAMDOC = NO
|
WARN_NO_PARAMDOC = NO
|
||||||
|
|
||||||
# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
|
|
||||||
# undocumented enumeration values. If set to NO, doxygen will accept
|
|
||||||
# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
|
|
||||||
# will automatically be disabled.
|
|
||||||
# The default value is: NO.
|
|
||||||
|
|
||||||
WARN_IF_UNDOC_ENUM_VAL = NO
|
|
||||||
|
|
||||||
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
|
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
|
||||||
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
|
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
|
||||||
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
|
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
|
||||||
# at the end of the doxygen process doxygen will return with a non-zero status.
|
# at the end of the doxygen process doxygen will return with a non-zero status.
|
||||||
# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves
|
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
|
||||||
# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not
|
|
||||||
# write the warning messages in between other messages but write them at the end
|
|
||||||
# of a run, in case a WARN_LOGFILE is defined the warning messages will be
|
|
||||||
# besides being in the defined file also be shown at the end of a run, unless
|
|
||||||
# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case
|
|
||||||
# the behavior will remain as with the setting FAIL_ON_WARNINGS.
|
|
||||||
# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.
|
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
|
|
||||||
WARN_AS_ERROR = NO
|
WARN_AS_ERROR = NO
|
||||||
@@ -947,7 +910,6 @@ INPUT = README.md \
|
|||||||
core/app.js \
|
core/app.js \
|
||||||
core/client.js \
|
core/client.js \
|
||||||
core/core.js \
|
core/core.js \
|
||||||
core/http.js \
|
|
||||||
core/tfrpc.js \
|
core/tfrpc.js \
|
||||||
docs/ \
|
docs/ \
|
||||||
src/
|
src/
|
||||||
@@ -957,21 +919,10 @@ INPUT = README.md \
|
|||||||
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
|
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
|
||||||
# documentation (see:
|
# documentation (see:
|
||||||
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
|
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
|
||||||
# See also: INPUT_FILE_ENCODING
|
|
||||||
# The default value is: UTF-8.
|
# The default value is: UTF-8.
|
||||||
|
|
||||||
INPUT_ENCODING = UTF-8
|
INPUT_ENCODING = UTF-8
|
||||||
|
|
||||||
# This tag can be used to specify the character encoding of the source files
|
|
||||||
# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
|
|
||||||
# character encoding on a per file pattern basis. Doxygen will compare the file
|
|
||||||
# name with each pattern and apply the encoding instead of the default
|
|
||||||
# INPUT_ENCODING) if there is a match. The character encodings are a list of the
|
|
||||||
# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
|
|
||||||
# "INPUT_ENCODING" for further information on supported encodings.
|
|
||||||
|
|
||||||
INPUT_FILE_ENCODING =
|
|
||||||
|
|
||||||
# If the value of the INPUT tag contains directories, you can use the
|
# If the value of the INPUT tag contains directories, you can use the
|
||||||
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
|
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
|
||||||
# *.h) to filter out the source-files in the directories.
|
# *.h) to filter out the source-files in the directories.
|
||||||
@@ -983,12 +934,12 @@ INPUT_FILE_ENCODING =
|
|||||||
# Note the list of default checked file patterns might differ from the list of
|
# Note the list of default checked file patterns might differ from the list of
|
||||||
# default file extension mappings.
|
# default file extension mappings.
|
||||||
#
|
#
|
||||||
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,
|
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
|
||||||
# *.cpp, *.cppm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl,
|
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
|
||||||
# *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, *.php,
|
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
|
||||||
# *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be
|
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
|
||||||
# provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
|
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
|
||||||
# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
|
# *.vhdl, *.ucf, *.qsf and *.ice.
|
||||||
|
|
||||||
FILE_PATTERNS = *.h \
|
FILE_PATTERNS = *.h \
|
||||||
*.js \
|
*.js \
|
||||||
@@ -1030,6 +981,9 @@ EXCLUDE_PATTERNS =
|
|||||||
# output. The symbol name can be a fully qualified name, a word, or if the
|
# output. The symbol name can be a fully qualified name, a word, or if the
|
||||||
# wildcard * is used, a substring. Examples: ANamespace, AClass,
|
# wildcard * is used, a substring. Examples: ANamespace, AClass,
|
||||||
# ANamespace::AClass, ANamespace::*Test
|
# ANamespace::AClass, ANamespace::*Test
|
||||||
|
#
|
||||||
|
# Note that the wildcards are matched against the file with absolute path, so to
|
||||||
|
# exclude all test directories use the pattern */test/*
|
||||||
|
|
||||||
EXCLUDE_SYMBOLS =
|
EXCLUDE_SYMBOLS =
|
||||||
|
|
||||||
@@ -1074,11 +1028,6 @@ IMAGE_PATH = docs/images/
|
|||||||
# code is scanned, but not when the output code is generated. If lines are added
|
# code is scanned, but not when the output code is generated. If lines are added
|
||||||
# or removed, the anchors will not be placed correctly.
|
# or removed, the anchors will not be placed correctly.
|
||||||
#
|
#
|
||||||
# Note that doxygen will use the data processed and written to standard output
|
|
||||||
# for further processing, therefore nothing else, like debug statements or used
|
|
||||||
# commands (so in case of a Windows batch file always use @echo OFF), should be
|
|
||||||
# written to standard output.
|
|
||||||
#
|
|
||||||
# Note that for custom extensions or not directly supported extensions you also
|
# Note that for custom extensions or not directly supported extensions you also
|
||||||
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
|
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
|
||||||
# properly processed by doxygen.
|
# properly processed by doxygen.
|
||||||
@@ -1120,15 +1069,6 @@ FILTER_SOURCE_PATTERNS =
|
|||||||
|
|
||||||
USE_MDFILE_AS_MAINPAGE = README.md
|
USE_MDFILE_AS_MAINPAGE = README.md
|
||||||
|
|
||||||
# The Fortran standard specifies that for fixed formatted Fortran code all
|
|
||||||
# characters from position 72 are to be considered as comment. A common
|
|
||||||
# extension is to allow longer lines before the automatic comment starts. The
|
|
||||||
# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
|
|
||||||
# be processed before the automatic comment starts.
|
|
||||||
# Minimum value: 7, maximum value: 10000, default value: 72.
|
|
||||||
|
|
||||||
FORTRAN_COMMENT_AFTER = 72
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
# Configuration options related to source browsing
|
# Configuration options related to source browsing
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
@@ -1266,11 +1206,10 @@ CLANG_DATABASE_PATH =
|
|||||||
|
|
||||||
ALPHABETICAL_INDEX = YES
|
ALPHABETICAL_INDEX = YES
|
||||||
|
|
||||||
# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
|
# In case all classes in a project start with a common prefix, all classes will
|
||||||
# that should be ignored while generating the index headers. The IGNORE_PREFIX
|
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
|
||||||
# tag works for classes, function and member names. The entity will be placed in
|
# can be used to specify a prefix (or a list of prefixes) that should be ignored
|
||||||
# the alphabetical list under the first letter of the entity name that remains
|
# while generating the index headers.
|
||||||
# after removing the prefix.
|
|
||||||
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
|
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
|
||||||
|
|
||||||
IGNORE_PREFIX =
|
IGNORE_PREFIX =
|
||||||
@@ -1349,12 +1288,7 @@ HTML_STYLESHEET =
|
|||||||
# Doxygen will copy the style sheet files to the output directory.
|
# Doxygen will copy the style sheet files to the output directory.
|
||||||
# Note: The order of the extra style sheet files is of importance (e.g. the last
|
# Note: The order of the extra style sheet files is of importance (e.g. the last
|
||||||
# style sheet in the list overrules the setting of the previous ones in the
|
# style sheet in the list overrules the setting of the previous ones in the
|
||||||
# list).
|
# list). For an example see the documentation.
|
||||||
# Note: Since the styling of scrollbars can currently not be overruled in
|
|
||||||
# Webkit/Chromium, the styling will be left out of the default doxygen.css if
|
|
||||||
# one or more extra stylesheets have been specified. So if scrollbar
|
|
||||||
# customization is desired it has to be added explicitly. For an example see the
|
|
||||||
# documentation.
|
|
||||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||||
|
|
||||||
HTML_EXTRA_STYLESHEET =
|
HTML_EXTRA_STYLESHEET =
|
||||||
@@ -1369,19 +1303,6 @@ HTML_EXTRA_STYLESHEET =
|
|||||||
|
|
||||||
HTML_EXTRA_FILES =
|
HTML_EXTRA_FILES =
|
||||||
|
|
||||||
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
|
|
||||||
# should be rendered with a dark or light theme.
|
|
||||||
# Possible values are: LIGHT always generate light mode output, DARK always
|
|
||||||
# generate dark mode output, AUTO_LIGHT automatically set the mode according to
|
|
||||||
# the user preference, use light mode if no preference is set (the default),
|
|
||||||
# AUTO_DARK automatically set the mode according to the user preference, use
|
|
||||||
# dark mode if no preference is set and TOGGLE allow to user to switch between
|
|
||||||
# light and dark mode via a button.
|
|
||||||
# The default value is: AUTO_LIGHT.
|
|
||||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
|
||||||
|
|
||||||
HTML_COLORSTYLE = AUTO_LIGHT
|
|
||||||
|
|
||||||
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
|
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
|
||||||
# will adjust the colors in the style sheet and background images according to
|
# will adjust the colors in the style sheet and background images according to
|
||||||
# this color. Hue is specified as an angle on a color-wheel, see
|
# this color. Hue is specified as an angle on a color-wheel, see
|
||||||
@@ -1412,6 +1333,15 @@ HTML_COLORSTYLE_SAT = 100
|
|||||||
|
|
||||||
HTML_COLORSTYLE_GAMMA = 80
|
HTML_COLORSTYLE_GAMMA = 80
|
||||||
|
|
||||||
|
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
|
||||||
|
# page will contain the date and time when the page was generated. Setting this
|
||||||
|
# to YES can help to show when doxygen was last run and thus if the
|
||||||
|
# documentation is up to date.
|
||||||
|
# The default value is: NO.
|
||||||
|
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||||
|
|
||||||
|
#HTML_TIMESTAMP = NO
|
||||||
|
|
||||||
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
|
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
|
||||||
# documentation will contain a main index with vertical navigation menus that
|
# documentation will contain a main index with vertical navigation menus that
|
||||||
# are dynamically created via JavaScript. If disabled, the navigation index will
|
# are dynamically created via JavaScript. If disabled, the navigation index will
|
||||||
@@ -1431,13 +1361,6 @@ HTML_DYNAMIC_MENUS = YES
|
|||||||
|
|
||||||
HTML_DYNAMIC_SECTIONS = NO
|
HTML_DYNAMIC_SECTIONS = NO
|
||||||
|
|
||||||
# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be
|
|
||||||
# dynamically folded and expanded in the generated HTML source code.
|
|
||||||
# The default value is: YES.
|
|
||||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
|
||||||
|
|
||||||
HTML_CODE_FOLDING = YES
|
|
||||||
|
|
||||||
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
|
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
|
||||||
# shown in the various tree structured indices initially; the user can expand
|
# shown in the various tree structured indices initially; the user can expand
|
||||||
# and collapse entries dynamically later on. Doxygen will expand the tree to
|
# and collapse entries dynamically later on. Doxygen will expand the tree to
|
||||||
@@ -1568,16 +1491,6 @@ BINARY_TOC = NO
|
|||||||
|
|
||||||
TOC_EXPAND = NO
|
TOC_EXPAND = NO
|
||||||
|
|
||||||
# The SITEMAP_URL tag is used to specify the full URL of the place where the
|
|
||||||
# generated documentation will be placed on the server by the user during the
|
|
||||||
# deployment of the documentation. The generated sitemap is called sitemap.xml
|
|
||||||
# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL
|
|
||||||
# is specified no sitemap is generated. For information about the sitemap
|
|
||||||
# protocol see https://www.sitemaps.org
|
|
||||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
|
||||||
|
|
||||||
SITEMAP_URL =
|
|
||||||
|
|
||||||
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
|
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
|
||||||
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
|
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
|
||||||
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
|
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
|
||||||
@@ -1753,6 +1666,17 @@ HTML_FORMULA_FORMAT = png
|
|||||||
|
|
||||||
FORMULA_FONTSIZE = 10
|
FORMULA_FONTSIZE = 10
|
||||||
|
|
||||||
|
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
|
||||||
|
# generated for formulas are transparent PNGs. Transparent PNGs are not
|
||||||
|
# supported properly for IE 6.0, but are supported on all modern browsers.
|
||||||
|
#
|
||||||
|
# Note that when changing this option you need to delete any form_*.png files in
|
||||||
|
# the HTML output directory before the changes have effect.
|
||||||
|
# The default value is: YES.
|
||||||
|
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||||
|
|
||||||
|
#FORMULA_TRANSPARENT = YES
|
||||||
|
|
||||||
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
|
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
|
||||||
# to create new LaTeX commands to be used in formulas as building blocks. See
|
# to create new LaTeX commands to be used in formulas as building blocks. See
|
||||||
# the section "Including formulas" for details.
|
# the section "Including formulas" for details.
|
||||||
@@ -1814,8 +1738,8 @@ MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2
|
|||||||
|
|
||||||
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
|
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
|
||||||
# extension names that should be enabled during MathJax rendering. For example
|
# extension names that should be enabled during MathJax rendering. For example
|
||||||
# for MathJax version 2 (see
|
# for MathJax version 2 (see https://docs.mathjax.org/en/v2.7-latest/tex.html
|
||||||
# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
|
# #tex-and-latex-extensions):
|
||||||
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
|
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
|
||||||
# For example for MathJax version 3 (see
|
# For example for MathJax version 3 (see
|
||||||
# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
|
# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
|
||||||
@@ -2066,16 +1990,9 @@ PDF_HYPERLINKS = YES
|
|||||||
|
|
||||||
USE_PDFLATEX = YES
|
USE_PDFLATEX = YES
|
||||||
|
|
||||||
# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error.
|
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
|
||||||
# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch
|
# command to the generated LaTeX files. This will instruct LaTeX to keep running
|
||||||
# mode nothing is printed on the terminal, errors are scrolled as if <return> is
|
# if errors occur, instead of asking the user for help.
|
||||||
# hit at every error; missing files that TeX tries to input or request from
|
|
||||||
# keyboard input (\read on a not open input stream) cause the job to abort,
|
|
||||||
# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,
|
|
||||||
# but there is no possibility of user interaction just like in batch mode,
|
|
||||||
# SCROLL In scroll mode, TeX will stop only for missing files to input or if
|
|
||||||
# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at
|
|
||||||
# each error, asking for user intervention.
|
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||||
|
|
||||||
@@ -2096,6 +2013,14 @@ LATEX_HIDE_INDICES = NO
|
|||||||
|
|
||||||
LATEX_BIB_STYLE = plain
|
LATEX_BIB_STYLE = plain
|
||||||
|
|
||||||
|
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
|
||||||
|
# page will contain the date and time when the page was generated. Setting this
|
||||||
|
# to NO can help when comparing the output of multiple runs.
|
||||||
|
# The default value is: NO.
|
||||||
|
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||||
|
|
||||||
|
#LATEX_TIMESTAMP = NO
|
||||||
|
|
||||||
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
|
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
|
||||||
# path from which the emoji images will be read. If a relative path is entered,
|
# path from which the emoji images will be read. If a relative path is entered,
|
||||||
# it will be relative to the LATEX_OUTPUT directory. If left blank the
|
# it will be relative to the LATEX_OUTPUT directory. If left blank the
|
||||||
@@ -2261,39 +2186,13 @@ DOCBOOK_OUTPUT = docbook
|
|||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
|
|
||||||
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
|
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
|
||||||
# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures
|
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
|
||||||
# the structure of the code including all documentation. Note that this feature
|
# the structure of the code including all documentation. Note that this feature
|
||||||
# is still experimental and incomplete at the moment.
|
# is still experimental and incomplete at the moment.
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
|
|
||||||
GENERATE_AUTOGEN_DEF = NO
|
GENERATE_AUTOGEN_DEF = NO
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
# Configuration options related to Sqlite3 output
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3
|
|
||||||
# database with symbols found by doxygen stored in tables.
|
|
||||||
# The default value is: NO.
|
|
||||||
|
|
||||||
#GENERATE_SQLITE3 = NO
|
|
||||||
|
|
||||||
# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be
|
|
||||||
# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put
|
|
||||||
# in front of it.
|
|
||||||
# The default directory is: sqlite3.
|
|
||||||
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
|
||||||
|
|
||||||
#SQLITE3_OUTPUT = sqlite3
|
|
||||||
|
|
||||||
# The SQLITE3_OVERWRITE_DB tag is set to YES, the existing doxygen_sqlite3.db
|
|
||||||
# database file will be recreated with each doxygen run. If set to NO, doxygen
|
|
||||||
# will warn if an a database file is already found and not modify it.
|
|
||||||
# The default value is: YES.
|
|
||||||
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
|
||||||
|
|
||||||
#SQLITE3_RECREATE_DB = YES
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
# Configuration options related to the Perl module output
|
# Configuration options related to the Perl module output
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
@@ -2436,15 +2335,15 @@ TAGFILES =
|
|||||||
|
|
||||||
GENERATE_TAGFILE =
|
GENERATE_TAGFILE =
|
||||||
|
|
||||||
# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces
|
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
|
||||||
# will be listed in the class and namespace index. If set to NO, only the
|
# the class index. If set to NO, only the inherited external classes will be
|
||||||
# inherited external classes will be listed.
|
# listed.
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
|
|
||||||
ALLEXTERNALS = NO
|
ALLEXTERNALS = NO
|
||||||
|
|
||||||
# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
|
# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
|
||||||
# in the topic index. If set to NO, only the current project's groups will be
|
# in the modules index. If set to NO, only the current project's groups will be
|
||||||
# listed.
|
# listed.
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
|
|
||||||
@@ -2458,9 +2357,16 @@ EXTERNAL_GROUPS = YES
|
|||||||
EXTERNAL_PAGES = YES
|
EXTERNAL_PAGES = YES
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
# Configuration options related to diagram generator tools
|
# Configuration options related to the dot tool
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||||
|
# then run dia to produce the diagram and insert it in the documentation. The
|
||||||
|
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||||
|
# If left empty dia is assumed to be found in the default search path.
|
||||||
|
|
||||||
|
DIA_PATH =
|
||||||
|
|
||||||
# If set to YES the inheritance and collaboration graphs will hide inheritance
|
# If set to YES the inheritance and collaboration graphs will hide inheritance
|
||||||
# and usage relations if the target is undocumented or is not a class.
|
# and usage relations if the target is undocumented or is not a class.
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
@@ -2469,10 +2375,10 @@ HIDE_UNDOC_RELATIONS = YES
|
|||||||
|
|
||||||
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
|
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
|
||||||
# available from the path. This tool is part of Graphviz (see:
|
# available from the path. This tool is part of Graphviz (see:
|
||||||
# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
|
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
|
||||||
# Bell Labs. The other options in this section have no effect if this option is
|
# Bell Labs. The other options in this section have no effect if this option is
|
||||||
# set to NO
|
# set to NO
|
||||||
# The default value is: YES.
|
# The default value is: NO.
|
||||||
|
|
||||||
HAVE_DOT = YES
|
HAVE_DOT = YES
|
||||||
|
|
||||||
@@ -2486,51 +2392,37 @@ HAVE_DOT = YES
|
|||||||
|
|
||||||
DOT_NUM_THREADS = 0
|
DOT_NUM_THREADS = 0
|
||||||
|
|
||||||
# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
|
# When you want a differently looking font in the dot files that doxygen
|
||||||
# subgraphs. When you want a differently looking font in the dot files that
|
# generates you can specify the font name using DOT_FONTNAME. You need to make
|
||||||
# doxygen generates you can specify fontname, fontcolor and fontsize attributes.
|
# sure dot is able to find the font, which can be done by putting it in a
|
||||||
# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
|
# standard location or by setting the DOTFONTPATH environment variable or by
|
||||||
# Edge and Graph Attributes specification</a> You need to make sure dot is able
|
# setting DOT_FONTPATH to the directory containing the font.
|
||||||
# to find the font, which can be done by putting it in a standard location or by
|
# The default value is: Helvetica.
|
||||||
# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
|
|
||||||
# directory containing the font. Default graphviz fontsize is 14.
|
|
||||||
# The default value is: fontname=Helvetica,fontsize=10.
|
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10"
|
#DOT_FONTNAME = Helvetica
|
||||||
|
|
||||||
# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
|
# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
|
||||||
# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
|
# dot graphs.
|
||||||
# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about
|
# Minimum value: 4, maximum value: 24, default value: 10.
|
||||||
# arrows shapes.</a>
|
|
||||||
# The default value is: labelfontname=Helvetica,labelfontsize=10.
|
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10"
|
#DOT_FONTSIZE = 10
|
||||||
|
|
||||||
# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
|
# By default doxygen will tell dot to use the default font as specified with
|
||||||
# around nodes set 'shape=plain' or 'shape=plaintext' <a
|
# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
|
||||||
# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>
|
# the path where dot can find it using this tag.
|
||||||
# The default value is: shape=box,height=0.2,width=0.4.
|
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
|
#DOT_FONTPATH =
|
||||||
|
|
||||||
# You can set the path where dot can find font specified with fontname in
|
# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
|
||||||
# DOT_COMMON_ATTR and others dot attributes.
|
# graph for each documented class showing the direct and indirect inheritance
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
|
||||||
|
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
|
||||||
DOT_FONTPATH =
|
# to TEXT the direct and indirect inheritance relations will be shown as texts /
|
||||||
|
# links.
|
||||||
# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will
|
# Possible values are: NO, YES, TEXT and GRAPH.
|
||||||
# generate a graph for each documented class showing the direct and indirect
|
|
||||||
# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and
|
|
||||||
# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case
|
|
||||||
# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the
|
|
||||||
# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.
|
|
||||||
# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance
|
|
||||||
# relations will be shown as texts / links.
|
|
||||||
# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.
|
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
|
|
||||||
CLASS_GRAPH = YES
|
CLASS_GRAPH = YES
|
||||||
@@ -2538,21 +2430,15 @@ CLASS_GRAPH = YES
|
|||||||
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
|
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
|
||||||
# graph for each documented class showing the direct and indirect implementation
|
# graph for each documented class showing the direct and indirect implementation
|
||||||
# dependencies (inheritance, containment, and class references variables) of the
|
# dependencies (inheritance, containment, and class references variables) of the
|
||||||
# class with other documented classes. Explicit enabling a collaboration graph,
|
# class with other documented classes.
|
||||||
# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the
|
|
||||||
# command \collaborationgraph. Disabling a collaboration graph can be
|
|
||||||
# accomplished by means of the command \hidecollaborationgraph.
|
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
COLLABORATION_GRAPH = YES
|
COLLABORATION_GRAPH = YES
|
||||||
|
|
||||||
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
|
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
|
||||||
# groups, showing the direct groups dependencies. Explicit enabling a group
|
# groups, showing the direct groups dependencies. See also the chapter Grouping
|
||||||
# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means
|
# in the manual.
|
||||||
# of the command \groupgraph. Disabling a directory graph can be accomplished by
|
|
||||||
# means of the command \hidegroupgraph. See also the chapter Grouping in the
|
|
||||||
# manual.
|
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
@@ -2612,9 +2498,7 @@ TEMPLATE_RELATIONS = NO
|
|||||||
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
|
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
|
||||||
# YES then doxygen will generate a graph for each documented file showing the
|
# YES then doxygen will generate a graph for each documented file showing the
|
||||||
# direct and indirect include dependencies of the file with other documented
|
# direct and indirect include dependencies of the file with other documented
|
||||||
# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,
|
# files.
|
||||||
# can be accomplished by means of the command \includegraph. Disabling an
|
|
||||||
# include graph can be accomplished by means of the command \hideincludegraph.
|
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
@@ -2623,10 +2507,7 @@ INCLUDE_GRAPH = YES
|
|||||||
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
|
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
|
||||||
# set to YES then doxygen will generate a graph for each documented file showing
|
# set to YES then doxygen will generate a graph for each documented file showing
|
||||||
# the direct and indirect include dependencies of the file with other documented
|
# the direct and indirect include dependencies of the file with other documented
|
||||||
# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set
|
# files.
|
||||||
# to NO, can be accomplished by means of the command \includedbygraph. Disabling
|
|
||||||
# an included by graph can be accomplished by means of the command
|
|
||||||
# \hideincludedbygraph.
|
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
@@ -2666,10 +2547,7 @@ GRAPHICAL_HIERARCHY = YES
|
|||||||
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
|
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
|
||||||
# dependencies a directory has on other directories in a graphical way. The
|
# dependencies a directory has on other directories in a graphical way. The
|
||||||
# dependency relations are determined by the #include relations between the
|
# dependency relations are determined by the #include relations between the
|
||||||
# files in the directories. Explicit enabling a directory graph, when
|
# files in the directories.
|
||||||
# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command
|
|
||||||
# \directorygraph. Disabling a directory graph can be accomplished by means of
|
|
||||||
# the command \hidedirectorygraph.
|
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
@@ -2685,13 +2563,12 @@ DIR_GRAPH_MAX_DEPTH = 1
|
|||||||
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
|
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
|
||||||
# generated by dot. For an explanation of the image formats see the section
|
# generated by dot. For an explanation of the image formats see the section
|
||||||
# output formats in the documentation of the dot tool (Graphviz (see:
|
# output formats in the documentation of the dot tool (Graphviz (see:
|
||||||
# https://www.graphviz.org/)).
|
# http://www.graphviz.org/)).
|
||||||
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
|
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
|
||||||
# to make the SVG files visible in IE 9+ (other browsers do not have this
|
# to make the SVG files visible in IE 9+ (other browsers do not have this
|
||||||
# requirement).
|
# requirement).
|
||||||
# Possible values are: png, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd,
|
# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
|
||||||
# gif, gif:cairo, gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd,
|
# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
|
||||||
# png:cairo, png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
|
|
||||||
# png:gdiplus:gdiplus.
|
# png:gdiplus:gdiplus.
|
||||||
# The default value is: png.
|
# The default value is: png.
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
@@ -2723,12 +2600,11 @@ DOT_PATH =
|
|||||||
|
|
||||||
DOTFILE_DIRS =
|
DOTFILE_DIRS =
|
||||||
|
|
||||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
# The MSCFILE_DIRS tag can be used to specify one or more directories that
|
||||||
# then run dia to produce the diagram and insert it in the documentation. The
|
# contain msc files that are included in the documentation (see the \mscfile
|
||||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
# command).
|
||||||
# If left empty dia is assumed to be found in the default search path.
|
|
||||||
|
|
||||||
DIA_PATH =
|
MSCFILE_DIRS =
|
||||||
|
|
||||||
# The DIAFILE_DIRS tag can be used to specify one or more directories that
|
# The DIAFILE_DIRS tag can be used to specify one or more directories that
|
||||||
# contain dia files that are included in the documentation (see the \diafile
|
# contain dia files that are included in the documentation (see the \diafile
|
||||||
@@ -2778,6 +2654,18 @@ DOT_GRAPH_MAX_NODES = 50
|
|||||||
|
|
||||||
MAX_DOT_GRAPH_DEPTH = 0
|
MAX_DOT_GRAPH_DEPTH = 0
|
||||||
|
|
||||||
|
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
|
||||||
|
# background. This is disabled by default, because dot on Windows does not seem
|
||||||
|
# to support this out of the box.
|
||||||
|
#
|
||||||
|
# Warning: Depending on the platform used, enabling this option may lead to
|
||||||
|
# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
|
||||||
|
# read).
|
||||||
|
# The default value is: NO.
|
||||||
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
|
#DOT_TRANSPARENT = NO
|
||||||
|
|
||||||
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
|
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
|
||||||
# files in one run (i.e. multiple -o and -T options on the command line). This
|
# files in one run (i.e. multiple -o and -T options on the command line). This
|
||||||
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
|
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
|
||||||
@@ -2805,19 +2693,3 @@ GENERATE_LEGEND = YES
|
|||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
|
|
||||||
DOT_CLEANUP = YES
|
DOT_CLEANUP = YES
|
||||||
|
|
||||||
# You can define message sequence charts within doxygen comments using the \msc
|
|
||||||
# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will
|
|
||||||
# use a built-in version of mscgen tool to produce the charts. Alternatively,
|
|
||||||
# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,
|
|
||||||
# specifying prog as the value, doxygen will call the tool as prog -T
|
|
||||||
# <outfile_format> -o <outputfile> <inputfile>. The external tool should support
|
|
||||||
# output file formats "png", "eps", "svg", and "ismap".
|
|
||||||
|
|
||||||
MSCGEN_TOOL =
|
|
||||||
|
|
||||||
# The MSCFILE_DIRS tag can be used to specify one or more directories that
|
|
||||||
# contain msc files that are included in the documentation (see the \mscfile
|
|
||||||
# command).
|
|
||||||
|
|
||||||
MSCFILE_DIRS =
|
|
||||||
|
|||||||
17
GNUmakefile
17
GNUmakefile
@@ -16,9 +16,9 @@ MAKEFLAGS += --no-builtin-rules
|
|||||||
## LD := Linker.
|
## LD := Linker.
|
||||||
## ANDROID_SDK := Path to the Android SDK.
|
## ANDROID_SDK := Path to the Android SDK.
|
||||||
|
|
||||||
VERSION_CODE := 40
|
VERSION_CODE := 44
|
||||||
VERSION_CODE_IOS := 15
|
VERSION_CODE_IOS := 18
|
||||||
VERSION_NUMBER := 0.0.33
|
VERSION_NUMBER := 0.2025.10-wip
|
||||||
VERSION_NAME := This program kills fascists.
|
VERSION_NAME := This program kills fascists.
|
||||||
|
|
||||||
IPHONEOS_VERSION_MIN=14.0
|
IPHONEOS_VERSION_MIN=14.0
|
||||||
@@ -253,7 +253,10 @@ $(ANDROID_TARGETS): CFLAGS += \
|
|||||||
-fno-asynchronous-unwind-tables \
|
-fno-asynchronous-unwind-tables \
|
||||||
-funwind-tables \
|
-funwind-tables \
|
||||||
-Wno-unknown-warning-option
|
-Wno-unknown-warning-option
|
||||||
$(ANDROID_TARGETS): LDFLAGS += --sysroot $(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/sysroot -fPIC
|
$(ANDROID_TARGETS): LDFLAGS += \
|
||||||
|
--sysroot $(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/sysroot \
|
||||||
|
-Wl,-z,max-page-size=16384 \
|
||||||
|
-fPIC
|
||||||
$(DEBUG_TARGETS): CFLAGS += -DDEBUG -Og
|
$(DEBUG_TARGETS): CFLAGS += -DDEBUG -Og
|
||||||
$(DEBUG_TARGETS): LDFLAGS += -Og
|
$(DEBUG_TARGETS): LDFLAGS += -Og
|
||||||
$(RELEASE_TARGETS): CFLAGS += \
|
$(RELEASE_TARGETS): CFLAGS += \
|
||||||
@@ -1140,6 +1143,11 @@ releaseapkgo: out/TildeFriends-arm-release.apk ## Build, install, and run a rele
|
|||||||
@adb shell am start com.unprompted.tildefriends/.TildeFriendsActivity
|
@adb shell am start com.unprompted.tildefriends/.TildeFriendsActivity
|
||||||
.PHONY: releaseapkgo
|
.PHONY: releaseapkgo
|
||||||
|
|
||||||
|
x86releaseapkgo: out/TildeFriends-x86-release.apk ## Build, install, and run an x86 release Android APK.
|
||||||
|
@adb install -r $<
|
||||||
|
@adb shell am start com.unprompted.tildefriends/.TildeFriendsActivity
|
||||||
|
.PHONY: x86releaseapkgo
|
||||||
|
|
||||||
apklog: ## Display Android log output.
|
apklog: ## Display Android log output.
|
||||||
@adb logcat *:S tildefriends
|
@adb logcat *:S tildefriends
|
||||||
.PHONY: apklog
|
.PHONY: apklog
|
||||||
@@ -1185,6 +1193,7 @@ out/tildefriends-%.ipa: out/tildefriends-ios%.app/tildefriends
|
|||||||
@echo "[ipa] $@"
|
@echo "[ipa] $@"
|
||||||
@rm -rf $@.tmp $@
|
@rm -rf $@.tmp $@
|
||||||
@mkdir -p $@.tmp/Payload/tildefriends.app/
|
@mkdir -p $@.tmp/Payload/tildefriends.app/
|
||||||
|
@cp src/ios/tildefriends512.png $@.tmp/iTunesArtwork
|
||||||
@cp -R $(dir $<)/* $@.tmp/Payload/tildefriends.app/
|
@cp -R $(dir $<)/* $@.tmp/Payload/tildefriends.app/
|
||||||
@cd $@.tmp/ && zip -u ../../$@ -q -9 -r ./
|
@cd $@.tmp/ && zip -u ../../$@ -q -9 -r ./
|
||||||
@rm -rf $@.tmp/
|
@rm -rf $@.tmp/
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "📜",
|
"emoji": "📜",
|
||||||
"previous": "&BEf0nraBdHk/+PWqx6tOSu5rheWVaxaL7orAOz3285M=.sha256"
|
"previous": "&sJqeyYjHys6Z8IqqtZ2ij2ZC1E2xieu/FU/u2hE+O1U=.sha256"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ app.setDocument(`<head>
|
|||||||
</head>
|
</head>
|
||||||
<body style="color:#fff">
|
<body style="color:#fff">
|
||||||
${markdown(docs.docs.global)}
|
${markdown(docs.docs.global)}
|
||||||
|
<!--
|
||||||
|
${Object.keys(docs.docs).filter(x => [...treeify('', globalThis)].indexOf(x) == -1).map(x => `<p>STALE: ${x}</p>`).join('')}
|
||||||
|
-->
|
||||||
${[...treeify('', globalThis)].map(x => document(x)).join('\n')}
|
${[...treeify('', globalThis)].map(x => document(x)).join('\n')}
|
||||||
<a id="Database"></a>
|
<a id="Database"></a>
|
||||||
${markdown(docs.docs.database)}
|
${markdown(docs.docs.database)}
|
||||||
|
|||||||
@@ -195,51 +195,6 @@ Call a function after some delay.
|
|||||||
* *Number* **timeout** Number of milliseconds to wait before calling the callback function.
|
* *Number* **timeout** Number of milliseconds to wait before calling the callback function.
|
||||||
`;
|
`;
|
||||||
|
|
||||||
docs['parseHttpRequest()'] = `
|
|
||||||
Parses an HTTP request.
|
|
||||||
### Parameters
|
|
||||||
* *Uint8Array* **request** The request data. Maybe be partial or contain extra data. The return value will
|
|
||||||
indicate when and where it is complete.
|
|
||||||
* *Number* **last_length** The length of the data passed on a previous attempt for the same request, or 0 initially.
|
|
||||||
### Returns
|
|
||||||
* *Integer* **-2** if the request is incomplete.
|
|
||||||
* *Integer* **-1** if the request could not be parsed.
|
|
||||||
* *Object* An object with **bytes_parsed**, **minor_version**, **path**, and **headers** fields on successful parse.
|
|
||||||
`;
|
|
||||||
|
|
||||||
docs['parseHttpResponse()'] = `
|
|
||||||
Parses an HTTP response.
|
|
||||||
### Parameters
|
|
||||||
* *Uint8Array* **response** The response data. Maybe be partial or contain extra data. The return value will
|
|
||||||
indicate when and where it is complete.
|
|
||||||
* *Number* **last_length** The length of the data passed on a previous attempt for the same response, or 0 initially.
|
|
||||||
### Returns
|
|
||||||
* *Integer* **-2** if the response is incomplete.
|
|
||||||
* *Integer* **-1** if the response could not be parsed.
|
|
||||||
* *Object* An object with **bytes_parsed**, **minor_version**, **status**, **message**, and **headers** fields on successful parse.
|
|
||||||
`;
|
|
||||||
|
|
||||||
docs['sha1Digest()'] = `
|
|
||||||
Calculates a SHA1 digest.
|
|
||||||
|
|
||||||
Completes synchronously.
|
|
||||||
### Parameters
|
|
||||||
* *String* **value** The value for which to calculate the digest.
|
|
||||||
### Returns
|
|
||||||
*String* The SHA1 digest of UTF-8 encoded \`value\`.
|
|
||||||
`;
|
|
||||||
|
|
||||||
docs['maskBytes()'] = `
|
|
||||||
Masks bytes for WebSocket communication.
|
|
||||||
|
|
||||||
Completes synchronously.
|
|
||||||
### Parameters
|
|
||||||
* *Uint8Array* **bytes** The byte array of data to mask.
|
|
||||||
* *Uint32* **mask** The mask to apply.
|
|
||||||
### Returns
|
|
||||||
*Uint32Array* The masked bytes.
|
|
||||||
`;
|
|
||||||
|
|
||||||
docs['exit()'] = `
|
docs['exit()'] = `
|
||||||
Exits the app. But why would you want to do that?
|
Exits the app. But why would you want to do that?
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "🦀",
|
"emoji": "🦀",
|
||||||
"previous": "&DGtlnm5wWRZCgJMF8JsP6VtzNRrd4KLoERJRpFULqOY=.sha256"
|
"previous": "&ULwb8udTxSkqf+mcZ0NxJs6p/hGB03eQEEKzJvWO3fk=.sha256"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,9 @@ tfrpc.register(function setHash(hash) {
|
|||||||
core.register('onMessage', async function (id) {
|
core.register('onMessage', async function (id) {
|
||||||
await tfrpc.rpc.notifyNewMessage(id);
|
await tfrpc.rpc.notifyNewMessage(id);
|
||||||
});
|
});
|
||||||
|
core.register('onBlob', async function (id) {
|
||||||
|
await tfrpc.rpc.notifyNewBlob(id);
|
||||||
|
});
|
||||||
tfrpc.register(async function store_blob(blob) {
|
tfrpc.register(async function store_blob(blob) {
|
||||||
if (Array.isArray(blob)) {
|
if (Array.isArray(blob)) {
|
||||||
blob = Uint8Array.from(blob);
|
blob = Uint8Array.from(blob);
|
||||||
|
|||||||
@@ -21,10 +21,13 @@ class TfElement extends LitElement {
|
|||||||
channels_latest: {type: Object},
|
channels_latest: {type: Object},
|
||||||
guest: {type: Boolean},
|
guest: {type: Boolean},
|
||||||
url: {type: String},
|
url: {type: String},
|
||||||
|
private_closed: {type: Object},
|
||||||
private_messages: {type: Array},
|
private_messages: {type: Array},
|
||||||
|
grouped_private_messages: {type: Object},
|
||||||
recent_reactions: {type: Array},
|
recent_reactions: {type: Array},
|
||||||
is_administrator: {type: Boolean},
|
is_administrator: {type: Boolean},
|
||||||
stay_connected: {type: Boolean},
|
stay_connected: {type: Boolean},
|
||||||
|
progress: {type: Number},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,6 +50,7 @@ class TfElement extends LitElement {
|
|||||||
this.loading_latest = 0;
|
this.loading_latest = 0;
|
||||||
this.loading_latest_scheduled = 0;
|
this.loading_latest_scheduled = 0;
|
||||||
this.recent_reactions = [];
|
this.recent_reactions = [];
|
||||||
|
this.private_closed = {};
|
||||||
tfrpc.rpc.getBroadcasts().then((b) => {
|
tfrpc.rpc.getBroadcasts().then((b) => {
|
||||||
self.broadcasts = b || [];
|
self.broadcasts = b || [];
|
||||||
});
|
});
|
||||||
@@ -56,10 +60,22 @@ class TfElement extends LitElement {
|
|||||||
tfrpc.rpc.getHash().then((hash) => self.set_hash(hash));
|
tfrpc.rpc.getHash().then((hash) => self.set_hash(hash));
|
||||||
tfrpc.register(function hashChanged(hash) {
|
tfrpc.register(function hashChanged(hash) {
|
||||||
self.set_hash(hash);
|
self.set_hash(hash);
|
||||||
|
self.reset_progress();
|
||||||
});
|
});
|
||||||
tfrpc.register(async function notifyNewMessage(id) {
|
tfrpc.register(async function notifyNewMessage(id) {
|
||||||
await self.fetch_new_message(id);
|
await self.fetch_new_message(id);
|
||||||
});
|
});
|
||||||
|
tfrpc.register(async function notifyNewBlob(id) {
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent('blob-stored', {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
detail: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
tfrpc.register(function set(name, value) {
|
tfrpc.register(function set(name, value) {
|
||||||
if (name === 'broadcasts') {
|
if (name === 'broadcasts') {
|
||||||
self.broadcasts = value;
|
self.broadcasts = value;
|
||||||
@@ -83,9 +99,22 @@ class TfElement extends LitElement {
|
|||||||
this.whoami = whoami ?? (ids.length ? ids[0] : undefined);
|
this.whoami = whoami ?? (ids.length ? ids[0] : undefined);
|
||||||
this.guest = !this.whoami?.length;
|
this.guest = !this.whoami?.length;
|
||||||
this.ids = ids;
|
this.ids = ids;
|
||||||
|
let private_closed =
|
||||||
|
(await tfrpc.rpc.databaseGet('private_closed')) ?? '{}';
|
||||||
|
this.private_closed = JSON.parse(private_closed);
|
||||||
await this.load_channels();
|
await this.load_channels();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async close_private_chat(event) {
|
||||||
|
let update = {};
|
||||||
|
update[event.detail.key] = true;
|
||||||
|
this.private_closed = Object.assign(update, this.private_closed);
|
||||||
|
await tfrpc.rpc.databaseSet(
|
||||||
|
'private_closed',
|
||||||
|
JSON.stringify(this.private_closed)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async load_channels() {
|
async load_channels() {
|
||||||
let channels = await tfrpc.rpc.query(
|
let channels = await tfrpc.rpc.query(
|
||||||
`
|
`
|
||||||
@@ -133,12 +162,32 @@ class TfElement extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
visible_private() {
|
||||||
|
if (!this.grouped_private_messages || !this.private_closed) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
let self = this;
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(this.grouped_private_messages).filter(([key, value]) => {
|
||||||
|
let channel = '🔐' + [...new Set(JSON.parse(key))].sort().join(',');
|
||||||
|
let grouped_latest = Math.max(...value.map((x) => x.rowid));
|
||||||
|
return (
|
||||||
|
!self.private_closed[key] ||
|
||||||
|
self.channels_unread[channel] === undefined ||
|
||||||
|
grouped_latest > self.channels_unread[channel]
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
next_channel(delta) {
|
next_channel(delta) {
|
||||||
let channel_names = [
|
let channel_names = [
|
||||||
'',
|
'',
|
||||||
'@',
|
'@',
|
||||||
'👍',
|
'👍',
|
||||||
'🔐',
|
...Object.keys(this.visible_private())
|
||||||
|
.sort()
|
||||||
|
.map((x) => '🔐' + JSON.parse(x).join(',')),
|
||||||
...this.channels.map((x) => '#' + x),
|
...this.channels.map((x) => '#' + x),
|
||||||
];
|
];
|
||||||
let index = channel_names.indexOf(this.hash.substring(1));
|
let index = channel_names.indexOf(this.hash.substring(1));
|
||||||
@@ -364,6 +413,36 @@ class TfElement extends LitElement {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async group_private_messages(messages) {
|
||||||
|
let groups = {};
|
||||||
|
let result = await this.decrypt(
|
||||||
|
await tfrpc.rpc.query(
|
||||||
|
`
|
||||||
|
SELECT messages.rowid, messages.id, author, timestamp, json(content) AS content
|
||||||
|
FROM messages
|
||||||
|
JOIN json_each(?) AS ids
|
||||||
|
WHERE messages.id = ids.value
|
||||||
|
ORDER BY timestamp DESC
|
||||||
|
`,
|
||||||
|
[JSON.stringify(messages)]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
for (let message of result) {
|
||||||
|
let key = JSON.stringify(
|
||||||
|
[
|
||||||
|
...new Set(
|
||||||
|
message?.decrypted?.recps?.filter((x) => x != this.whoami)
|
||||||
|
),
|
||||||
|
].sort() ?? []
|
||||||
|
);
|
||||||
|
if (!groups[key]) {
|
||||||
|
groups[key] = [];
|
||||||
|
}
|
||||||
|
groups[key].push(message);
|
||||||
|
}
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
async load_channels_latest(following) {
|
async load_channels_latest(following) {
|
||||||
let start_time = new Date();
|
let start_time = new Date();
|
||||||
let latest_private = this.get_latest_private(following);
|
let latest_private = this.get_latest_private(following);
|
||||||
@@ -436,12 +515,15 @@ class TfElement extends LitElement {
|
|||||||
console.log('channels took', (new Date() - start_time) / 1000.0);
|
console.log('channels took', (new Date() - start_time) / 1000.0);
|
||||||
let self = this;
|
let self = this;
|
||||||
start_time = new Date();
|
start_time = new Date();
|
||||||
latest_private.then(function (latest) {
|
latest_private.then(async function (latest) {
|
||||||
self.channels_latest = Object.assign({}, self.channels_latest, {
|
self.channels_latest = Object.assign({}, self.channels_latest, {
|
||||||
'🔐': latest[0],
|
'🔐': latest[0],
|
||||||
});
|
});
|
||||||
console.log('private took', (new Date() - start_time) / 1000.0);
|
console.log('private took', (new Date() - start_time) / 1000.0);
|
||||||
self.private_messages = latest[1];
|
self.private_messages = latest[1];
|
||||||
|
self.grouped_private_messages = await self.group_private_messages(
|
||||||
|
latest[1]
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,7 +532,28 @@ class TfElement extends LitElement {
|
|||||||
this.schedule_load_latest();
|
this.schedule_load_latest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reset_progress() {
|
||||||
|
if (this.progress === undefined) {
|
||||||
|
this._progress_start = new Date();
|
||||||
|
requestAnimationFrame(this.update_progress.bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update_progress() {
|
||||||
|
if (
|
||||||
|
!this.loading_latest &&
|
||||||
|
!this.loading_latest_scheduled &&
|
||||||
|
!this.shadowRoot.getElementById('tf-tab-news')?.is_loading()
|
||||||
|
) {
|
||||||
|
this.progress = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.progress = (new Date() - this._progress_start).valueOf();
|
||||||
|
requestAnimationFrame(this.update_progress.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
schedule_load_latest() {
|
schedule_load_latest() {
|
||||||
|
this.reset_progress();
|
||||||
if (!this.loading_latest) {
|
if (!this.loading_latest) {
|
||||||
this.shadowRoot.getElementById('tf-tab-news')?.load_latest();
|
this.shadowRoot.getElementById('tf-tab-news')?.load_latest();
|
||||||
this.load();
|
this.load();
|
||||||
@@ -495,6 +598,7 @@ class TfElement extends LitElement {
|
|||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
this.loading_latest = true;
|
this.loading_latest = true;
|
||||||
|
this.reset_progress();
|
||||||
try {
|
try {
|
||||||
let start_time = new Date();
|
let start_time = new Date();
|
||||||
let whoami = this.whoami;
|
let whoami = this.whoami;
|
||||||
@@ -603,8 +707,11 @@ class TfElement extends LitElement {
|
|||||||
@channelsetunread=${this.channel_set_unread}
|
@channelsetunread=${this.channel_set_unread}
|
||||||
@refresh=${this.refresh}
|
@refresh=${this.refresh}
|
||||||
@toggle_stay_connected=${this.toggle_stay_connected}
|
@toggle_stay_connected=${this.toggle_stay_connected}
|
||||||
|
@loadmessages=${this.reset_progress}
|
||||||
|
@closeprivatechat=${this.close_private_chat}
|
||||||
.connections=${this.connections}
|
.connections=${this.connections}
|
||||||
.private_messages=${this.private_messages}
|
.private_messages=${this.private_messages}
|
||||||
|
.grouped_private_messages=${this.visible_private()}
|
||||||
.recent_reactions=${this.recent_reactions}
|
.recent_reactions=${this.recent_reactions}
|
||||||
?is_administrator=${this.is_administrator}
|
?is_administrator=${this.is_administrator}
|
||||||
?stay_connected=${this.stay_connected}
|
?stay_connected=${this.stay_connected}
|
||||||
@@ -636,7 +743,7 @@ class TfElement extends LitElement {
|
|||||||
whoami=${this.whoami}
|
whoami=${this.whoami}
|
||||||
.users=${this.users}
|
.users=${this.users}
|
||||||
query=${this.hash?.startsWith('#sql=')
|
query=${this.hash?.startsWith('#sql=')
|
||||||
? decodeURIComponent(this.hash.substring(5))
|
? this.hash.substring(5)
|
||||||
: null}
|
: null}
|
||||||
></tf-tab-query>
|
></tf-tab-query>
|
||||||
`;
|
`;
|
||||||
@@ -646,6 +753,7 @@ class TfElement extends LitElement {
|
|||||||
async set_tab(tab) {
|
async set_tab(tab) {
|
||||||
this.tab = tab;
|
this.tab = tab;
|
||||||
if (tab === 'news') {
|
if (tab === 'news') {
|
||||||
|
this.schedule_load_latest();
|
||||||
await tfrpc.rpc.setHash('#');
|
await tfrpc.rpc.setHash('#');
|
||||||
} else if (tab === 'connections') {
|
} else if (tab === 'connections') {
|
||||||
await tfrpc.rpc.setHash('#connections');
|
await tfrpc.rpc.setHash('#connections');
|
||||||
@@ -692,14 +800,13 @@ class TfElement extends LitElement {
|
|||||||
class="w3-bar w3-theme-l1"
|
class="w3-bar w3-theme-l1"
|
||||||
style="position: static; top: 0; z-index: 10"
|
style="position: static; top: 0; z-index: 10"
|
||||||
>
|
>
|
||||||
${this.is_administrator && self.tab != 'news'
|
${this.is_administrator
|
||||||
? html`
|
? html`
|
||||||
<button
|
<button
|
||||||
class=${'w3-bar-item w3-button w3-circle w3-ripple' +
|
class=${'w3-bar-item w3-button w3-circle w3-ripple' +
|
||||||
(this.connections?.some((x) => x.flags.one_shot)
|
(this.connections?.some((x) => x.flags.one_shot)
|
||||||
? ' w3-spin'
|
? ' w3-spin'
|
||||||
: '')}
|
: '')}
|
||||||
style="width: 1.5em; height: 1.5em; padding: 8px"
|
|
||||||
@click=${this.refresh}
|
@click=${this.refresh}
|
||||||
>
|
>
|
||||||
↻
|
↻
|
||||||
@@ -751,11 +858,23 @@ class TfElement extends LitElement {
|
|||||||
Loading...
|
Loading...
|
||||||
</div>`
|
</div>`
|
||||||
: this.render_tab();
|
: this.render_tab();
|
||||||
|
let progress =
|
||||||
|
this.progress !== undefined
|
||||||
|
? html`
|
||||||
|
<div style="position: absolute; width: 100%" id="progress">
|
||||||
|
<div
|
||||||
|
class="w3-theme-l3"
|
||||||
|
style=${`height: 4px; position: absolute; right: ${Math.cos(this.progress / 250) > 0 ? 'auto' : '0'}; width: ${50 * Math.sin(this.progress / 250) + 50}%`}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: undefined;
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
style="width: 100vw; min-height: 100vh; height: 100vh; display: flex; flex-direction: column"
|
style="width: 100vw; min-height: 100vh; height: 100vh; display: flex; flex-direction: column"
|
||||||
class="w3-theme-dark"
|
class="w3-theme-dark"
|
||||||
>
|
>
|
||||||
|
${progress}
|
||||||
<div style="flex: 0 0">${tabs}</div>
|
<div style="flex: 0 0">${tabs}</div>
|
||||||
<div style="flex: 1 1; overflow: auto; contain: layout">
|
<div style="flex: 1 1; overflow: auto; contain: layout">
|
||||||
${contents}
|
${contents}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class TfComposeElement extends LitElement {
|
|||||||
author: {type: String},
|
author: {type: String},
|
||||||
channel: {type: String},
|
channel: {type: String},
|
||||||
new_thread: {type: Boolean},
|
new_thread: {type: Boolean},
|
||||||
|
recipients: {type: Array},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +92,9 @@ class TfComposeElement extends LitElement {
|
|||||||
bubbles: true,
|
bubbles: true,
|
||||||
composed: true,
|
composed: true,
|
||||||
detail: {
|
detail: {
|
||||||
id: this.branch,
|
id:
|
||||||
|
this.branch ??
|
||||||
|
(this.recipients ? this.recipients.join(',') : undefined),
|
||||||
draft: draft,
|
draft: draft,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -291,7 +294,7 @@ class TfComposeElement extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {
|
get_values() {
|
||||||
let values = Object.entries(this.users).map((x) => ({
|
let values = Object.entries(this.users).map((x) => ({
|
||||||
key: x[1].name ?? x[0],
|
key: x[1].name ?? x[0],
|
||||||
value: x[0],
|
value: x[0],
|
||||||
@@ -307,11 +310,15 @@ class TfComposeElement extends LitElement {
|
|||||||
values
|
values
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
let tribute = new Tribute({
|
let tribute = new Tribute({
|
||||||
iframe: this.shadowRoot,
|
iframe: this.shadowRoot,
|
||||||
collection: [
|
collection: [
|
||||||
{
|
{
|
||||||
values: values,
|
values: this.get_values(),
|
||||||
selectTemplate: function (item) {
|
selectTemplate: function (item) {
|
||||||
return item
|
return item
|
||||||
? `[@${item.original.key}](${item.original.value})`
|
? `[@${item.original.key}](${item.original.value})`
|
||||||
@@ -330,6 +337,7 @@ class TfComposeElement extends LitElement {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
tribute.attach(this.renderRoot.getElementById('edit'));
|
tribute.attach(this.renderRoot.getElementById('edit'));
|
||||||
|
this._tribute = tribute;
|
||||||
}
|
}
|
||||||
|
|
||||||
updated() {
|
updated() {
|
||||||
@@ -340,6 +348,7 @@ class TfComposeElement extends LitElement {
|
|||||||
preview.innerHTML = this.process_text(edit.innerText);
|
preview.innerHTML = this.process_text(edit.innerText);
|
||||||
this.last_updated_text = edit.innerText;
|
this.last_updated_text = edit.innerText;
|
||||||
}
|
}
|
||||||
|
this._tribute.collection[0].values = this.get_values();
|
||||||
let encrypt = this.renderRoot.getElementById('encrypt_to');
|
let encrypt = this.renderRoot.getElementById('encrypt_to');
|
||||||
if (encrypt) {
|
if (encrypt) {
|
||||||
let tribute = new Tribute({
|
let tribute = new Tribute({
|
||||||
@@ -496,7 +505,17 @@ class TfComposeElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get_draft() {
|
get_draft() {
|
||||||
return this.drafts[this.branch || ''] || {};
|
let key =
|
||||||
|
this.branch ||
|
||||||
|
(this.recipients ? this.recipients.join(',') : undefined) ||
|
||||||
|
'';
|
||||||
|
let draft = this.drafts[key] || {};
|
||||||
|
if (this.recipients && !draft.encrypt_to?.length) {
|
||||||
|
draft.encrypt_to = [
|
||||||
|
...new Set(this.recipients).union(new Set(draft.encrypt_to ?? [])),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return draft;
|
||||||
}
|
}
|
||||||
|
|
||||||
update_encrypt(event) {
|
update_encrypt(event) {
|
||||||
@@ -606,7 +625,7 @@ class TfComposeElement extends LitElement {
|
|||||||
<div class="w3-half">
|
<div class="w3-half">
|
||||||
<span
|
<span
|
||||||
class="w3-input w3-theme-d1 w3-border"
|
class="w3-input w3-theme-d1 w3-border"
|
||||||
style="resize: vertical; width: 100%; overflow: hidden; white-space: pre-wrap"
|
style="resize: vertical; width: 100%; white-space: pre-wrap"
|
||||||
placeholder="Write a post here."
|
placeholder="Write a post here."
|
||||||
id="edit"
|
id="edit"
|
||||||
@input=${this.input}
|
@input=${this.input}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class TfMessageElement extends LitElement {
|
|||||||
channel: {type: String},
|
channel: {type: String},
|
||||||
channel_unread: {type: Number},
|
channel_unread: {type: Number},
|
||||||
recent_reactions: {type: Array},
|
recent_reactions: {type: Array},
|
||||||
|
depth: {type: Number},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,16 +41,20 @@ class TfMessageElement extends LitElement {
|
|||||||
this.expanded = {};
|
this.expanded = {};
|
||||||
this.channel_unread = -1;
|
this.channel_unread = -1;
|
||||||
this.recent_reactions = [];
|
this.recent_reactions = [];
|
||||||
|
this.depth = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this._click_callback = this.document_click.bind(this);
|
this._click_callback = this.document_click.bind(this);
|
||||||
|
this._blob_stored = this.blob_stored.bind(this);
|
||||||
document.body.addEventListener('mouseup', this._click_callback);
|
document.body.addEventListener('mouseup', this._click_callback);
|
||||||
|
window.addEventListener('blob-stored', this._blob_stored);
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
|
window.removeEventListener('blob-stored', this._blob_stored);
|
||||||
document.body.removeEventListener('mouseup', this._click_callback);
|
document.body.removeEventListener('mouseup', this._click_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +66,16 @@ class TfMessageElement extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blob_stored(event) {
|
||||||
|
let search = `/${event.detail.id}/view`;
|
||||||
|
for (let img of this.shadowRoot.querySelectorAll('img')) {
|
||||||
|
if (img.src.indexOf(search) != -1) {
|
||||||
|
let src = img.src.split('?')[0];
|
||||||
|
img.src = `${src}?${new Date().valueOf()}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
show_reply() {
|
show_reply() {
|
||||||
let event = new CustomEvent('tf-draft', {
|
let event = new CustomEvent('tf-draft', {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
@@ -349,23 +364,26 @@ class TfMessageElement extends LitElement {
|
|||||||
</button>
|
</button>
|
||||||
`;
|
`;
|
||||||
} else {
|
} else {
|
||||||
return html` <div class="w3-container w3-margin-bottom">
|
return html` <ul class="w3-container w3-margin-bottom w3-ul w3-card-4">
|
||||||
${repeat(
|
${repeat(
|
||||||
this.message.child_messages || [],
|
this.message.child_messages || [],
|
||||||
(x) => x.id,
|
(x) => x.id,
|
||||||
(x) =>
|
(x) =>
|
||||||
html`<tf-message
|
html`<li style="padding: 0">
|
||||||
.message=${x}
|
<tf-message
|
||||||
whoami=${this.whoami}
|
.message=${x}
|
||||||
.users=${this.users}
|
whoami=${this.whoami}
|
||||||
.drafts=${this.drafts}
|
.users=${this.users}
|
||||||
.expanded=${this.expanded}
|
.drafts=${this.drafts}
|
||||||
channel=${this.channel}
|
.expanded=${this.expanded}
|
||||||
channel_unread=${this.channel_unread}
|
channel=${this.channel}
|
||||||
.recent_reactions=${this.recent_reactions}
|
channel_unread=${this.channel_unread}
|
||||||
></tf-message>`
|
.recent_reactions=${this.recent_reactions}
|
||||||
|
depth=${this.depth + 1}
|
||||||
|
></tf-message>
|
||||||
|
</li>`
|
||||||
)}
|
)}
|
||||||
</div>
|
</ul>
|
||||||
<button
|
<button
|
||||||
class="w3-button w3-theme-d1 w3-block w3-bar"
|
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||||
style="box-sizing: border-box"
|
style="box-sizing: border-box"
|
||||||
@@ -536,8 +554,11 @@ class TfMessageElement extends LitElement {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div
|
<div
|
||||||
class="w3-card-4 ${this.class_background()} w3-border-theme w3-margin-top"
|
class="w3-card-4 ${this.class_background()} w3-border-theme ${this
|
||||||
style="overflow: auto; overflow-wrap: anywhere; display: block; max-width: 100%"
|
.depth == 0
|
||||||
|
? 'w3-margin-top'
|
||||||
|
: ''}"
|
||||||
|
style="overflow-wrap: anywhere; display: block; max-width: 100%"
|
||||||
>
|
>
|
||||||
${inner}
|
${inner}
|
||||||
</div>
|
</div>
|
||||||
@@ -563,6 +584,7 @@ class TfMessageElement extends LitElement {
|
|||||||
channel=${self.channel}
|
channel=${self.channel}
|
||||||
channel_unread=${self.channel_unread}
|
channel_unread=${self.channel_unread}
|
||||||
.recent_reactions=${self.recent_reactions}
|
.recent_reactions=${self.recent_reactions}
|
||||||
|
depth=${self.depth + 1}
|
||||||
></tf-message>
|
></tf-message>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
@@ -631,6 +653,35 @@ class TfMessageElement extends LitElement {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
channel_group_by_author() {
|
||||||
|
let sorted = this.message.messages
|
||||||
|
.map((x) => [
|
||||||
|
x.author,
|
||||||
|
x.content.subscribed ? 'subscribed to' : 'unsubscribed from',
|
||||||
|
x.content.channel,
|
||||||
|
x,
|
||||||
|
])
|
||||||
|
.sort();
|
||||||
|
let result = [];
|
||||||
|
let last;
|
||||||
|
let group;
|
||||||
|
for (let row of sorted) {
|
||||||
|
if (last && last[0] == row[0] && last[1] == row[1]) {
|
||||||
|
group.push(row[2]);
|
||||||
|
} else {
|
||||||
|
if (group) {
|
||||||
|
result.push({author: last[0], action: last[1], channels: group});
|
||||||
|
}
|
||||||
|
last = row;
|
||||||
|
group = [row[2]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (group) {
|
||||||
|
result.push({author: last[0], action: last[1], channels: group});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
allow_unread() {
|
allow_unread() {
|
||||||
return (
|
return (
|
||||||
this.channel == '@' ||
|
this.channel == '@' ||
|
||||||
@@ -665,6 +716,7 @@ class TfMessageElement extends LitElement {
|
|||||||
.expanded=${this.expanded}
|
.expanded=${this.expanded}
|
||||||
channel=${this.channel}
|
channel=${this.channel}
|
||||||
channel_unread=${this.channel_unread}
|
channel_unread=${this.channel_unread}
|
||||||
|
depth=${this.depth + 1}
|
||||||
></tf-message>`
|
></tf-message>`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -706,6 +758,56 @@ class TfMessageElement extends LitElement {
|
|||||||
</button>
|
</button>
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
} else if (this.message?.type === 'channel_group') {
|
||||||
|
if (this.expanded[this.expanded_key()]) {
|
||||||
|
return this.render_frame(html`
|
||||||
|
<div class="w3-padding">
|
||||||
|
${this.message.messages.map(
|
||||||
|
(x) =>
|
||||||
|
html`<tf-message
|
||||||
|
.message=${x}
|
||||||
|
whoami=${this.whoami}
|
||||||
|
.users=${this.users}
|
||||||
|
.drafts=${this.drafts}
|
||||||
|
.expanded=${this.expanded}
|
||||||
|
channel=${this.channel}
|
||||||
|
channel_unread=${this.channel_unread}
|
||||||
|
depth=${this.depth + 1}
|
||||||
|
></tf-message>`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||||
|
style="box-sizing: border-box"
|
||||||
|
@click=${() => self.set_expanded(false)}
|
||||||
|
>
|
||||||
|
Collapse
|
||||||
|
</button>
|
||||||
|
`);
|
||||||
|
} else {
|
||||||
|
return this.render_frame(html`
|
||||||
|
<div class="w3-padding">
|
||||||
|
${this.channel_group_by_author().map(
|
||||||
|
(x) => html`
|
||||||
|
<div>
|
||||||
|
<tf-user id=${x.author} .users=${this.users}></tf-user>
|
||||||
|
${x.action}
|
||||||
|
${x.channels.map(
|
||||||
|
(y) => html` <tf-tag tag=${'#' + y}></tf-tag> `
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||||
|
style="box-sizing: border-box"
|
||||||
|
@click=${() => self.set_expanded(true)}
|
||||||
|
>
|
||||||
|
Expand
|
||||||
|
</button>
|
||||||
|
`);
|
||||||
|
}
|
||||||
} else if (this.message.placeholder) {
|
} else if (this.message.placeholder) {
|
||||||
return this.render_frame(
|
return this.render_frame(
|
||||||
html`<div>
|
html`<div>
|
||||||
@@ -751,6 +853,7 @@ class TfMessageElement extends LitElement {
|
|||||||
.expanded=${this.expanded}
|
.expanded=${this.expanded}
|
||||||
channel=${this.channel}
|
channel=${this.channel}
|
||||||
channel_unread=${this.channel_unread}
|
channel_unread=${this.channel_unread}
|
||||||
|
depth=${this.depth + 1}
|
||||||
></tf-message>
|
></tf-message>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
@@ -789,60 +892,45 @@ class TfMessageElement extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
} else if (content.type == 'contact') {
|
} else if (content.type == 'contact') {
|
||||||
return this.render_frame(html`
|
switch (this.format) {
|
||||||
<div class="w3-bar">
|
case 'message':
|
||||||
<div class="w3-bar-item">
|
default:
|
||||||
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
return this.render_frame(html`
|
||||||
is
|
<div class="w3-bar">
|
||||||
${content.blocking === true
|
<div class="w3-bar-item">
|
||||||
? 'blocking'
|
<tf-user
|
||||||
: content.blocking === false
|
id=${this.message.author}
|
||||||
? 'no longer blocking'
|
.users=${this.users}
|
||||||
: content.following === true
|
></tf-user>
|
||||||
? 'following'
|
is
|
||||||
: content.following === false
|
${content.blocking === true
|
||||||
? 'no longer following'
|
? 'blocking'
|
||||||
: '?'}
|
: content.blocking === false
|
||||||
<tf-user
|
? 'no longer blocking'
|
||||||
id=${this.message.content.contact}
|
: content.following === true
|
||||||
.users=${this.users}
|
? 'following'
|
||||||
></tf-user>
|
: content.following === false
|
||||||
</div>
|
? 'no longer following'
|
||||||
<div class="w3-bar-item w3-right">
|
: '?'}
|
||||||
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
|
<tf-user
|
||||||
%
|
id=${this.message.content.contact}
|
||||||
</button>
|
.users=${this.users}
|
||||||
<div
|
></tf-user>
|
||||||
class="w3-dropdown-content w3-bar-block w3-card-4 w3-theme-l1"
|
</div>
|
||||||
style="right: 48px"
|
${this.render_menu()} ${this.render_votes()}
|
||||||
>
|
${this.render_actions()}
|
||||||
<a
|
|
||||||
target="_top"
|
|
||||||
class="w3-button w3-bar-item"
|
|
||||||
href=${'#' + encodeURIComponent(this.message?.id)}
|
|
||||||
>View Message</a
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
class="w3-button w3-bar-item w3-border-bottom"
|
|
||||||
@click=${this.copy_id}
|
|
||||||
>
|
|
||||||
Copy ID
|
|
||||||
</button>
|
|
||||||
${this.drafts[this.message?.id] === undefined
|
|
||||||
? html`
|
|
||||||
<button
|
|
||||||
class="w3-button w3-bar-item"
|
|
||||||
@click=${this.show_reply}
|
|
||||||
>
|
|
||||||
↩️ Reply
|
|
||||||
</button>
|
|
||||||
`
|
|
||||||
: undefined}
|
|
||||||
</div>
|
</div>
|
||||||
|
`);
|
||||||
|
break;
|
||||||
|
case 'raw':
|
||||||
|
return this.render_frame(html`
|
||||||
|
${this.render_header()}
|
||||||
|
<div class="w3-container">${this.render_raw()}</div>
|
||||||
|
${this.render_votes()} ${this.render_actions()}
|
||||||
</div>
|
</div>
|
||||||
${this.render_votes()} ${this.render_actions()}
|
`);
|
||||||
</div>
|
break;
|
||||||
`);
|
}
|
||||||
} else if (content.type == 'post') {
|
} else if (content.type == 'post') {
|
||||||
let self = this;
|
let self = this;
|
||||||
let body;
|
let body;
|
||||||
|
|||||||
@@ -160,11 +160,29 @@ class TfNewsElement extends LitElement {
|
|||||||
return recursive_sort(roots, true);
|
return recursive_sort(roots, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
group_following(messages) {
|
group_messages(messages) {
|
||||||
let result = [];
|
let result = [];
|
||||||
let group = [];
|
let group = [];
|
||||||
|
let type = undefined;
|
||||||
for (let message of messages) {
|
for (let message of messages) {
|
||||||
if (message?.content?.type === 'contact') {
|
if (
|
||||||
|
message?.content?.type === 'contact' ||
|
||||||
|
message?.content?.type === 'channel'
|
||||||
|
) {
|
||||||
|
if (type && message.content.type !== type) {
|
||||||
|
if (group.length == 1) {
|
||||||
|
result.push(group[0]);
|
||||||
|
group = [];
|
||||||
|
} else if (group.length > 1) {
|
||||||
|
result.push({
|
||||||
|
rowid: Math.max(...group.map((x) => x.rowid)),
|
||||||
|
type: `${type}_group`,
|
||||||
|
messages: group,
|
||||||
|
});
|
||||||
|
group = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type = message.content.type;
|
||||||
group.push(message);
|
group.push(message);
|
||||||
} else {
|
} else {
|
||||||
if (group.length == 1) {
|
if (group.length == 1) {
|
||||||
@@ -173,12 +191,13 @@ class TfNewsElement extends LitElement {
|
|||||||
} else if (group.length > 1) {
|
} else if (group.length > 1) {
|
||||||
result.push({
|
result.push({
|
||||||
rowid: Math.max(...group.map((x) => x.rowid)),
|
rowid: Math.max(...group.map((x) => x.rowid)),
|
||||||
type: 'contact_group',
|
type: `${type}_group`,
|
||||||
messages: group,
|
messages: group,
|
||||||
});
|
});
|
||||||
group = [];
|
group = [];
|
||||||
}
|
}
|
||||||
result.push(message);
|
result.push(message);
|
||||||
|
type = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (group.length == 1) {
|
if (group.length == 1) {
|
||||||
@@ -187,7 +206,7 @@ class TfNewsElement extends LitElement {
|
|||||||
} else if (group.length > 1) {
|
} else if (group.length > 1) {
|
||||||
result.push({
|
result.push({
|
||||||
rowid: Math.max(...group.map((x) => x.rowid)),
|
rowid: Math.max(...group.map((x) => x.rowid)),
|
||||||
type: 'contact_group',
|
type: `${type}_group`,
|
||||||
messages: group,
|
messages: group,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -200,7 +219,7 @@ class TfNewsElement extends LitElement {
|
|||||||
|
|
||||||
load_and_render(messages) {
|
load_and_render(messages) {
|
||||||
let messages_by_id = this.process_messages(messages);
|
let messages_by_id = this.process_messages(messages);
|
||||||
let final_messages = this.group_following(
|
let final_messages = this.group_messages(
|
||||||
this.finalize_messages(messages_by_id)
|
this.finalize_messages(messages_by_id)
|
||||||
);
|
);
|
||||||
let unread_rowid = -1;
|
let unread_rowid = -1;
|
||||||
|
|||||||
@@ -349,6 +349,9 @@ class TfProfileElement extends LitElement {
|
|||||||
${until(this.load_follows(), html`<p>Loading accounts followed...</p>`)}
|
${until(this.load_follows(), html`<p>Loading accounts followed...</p>`)}
|
||||||
<footer class="w3-container">
|
<footer class="w3-container">
|
||||||
<p>
|
<p>
|
||||||
|
<a class="w3-button w3-theme-d1" href=${'#🔐' + (this.id != this.whoami ? this.id : '')}>
|
||||||
|
Open Private Chat
|
||||||
|
</a>
|
||||||
${edit}
|
${edit}
|
||||||
${follow}
|
${follow}
|
||||||
${block}
|
${block}
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ const tf = css`
|
|||||||
border-left: 4px solid #fff;
|
border-left: 4px solid #fff;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
time_range: {type: Array},
|
time_range: {type: Array},
|
||||||
time_loading: {type: Array},
|
time_loading: {type: Array},
|
||||||
private_messages: {type: Array},
|
private_messages: {type: Array},
|
||||||
|
grouped_private_messages: {type: Object},
|
||||||
recent_reactions: {type: Array},
|
recent_reactions: {type: Array},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -106,6 +107,12 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fetch_messages(start_time, end_time) {
|
async fetch_messages(start_time, end_time) {
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent('loadmessages', {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
this.time_loading = [start_time, end_time];
|
this.time_loading = [start_time, end_time];
|
||||||
let result;
|
let result;
|
||||||
const k_max_results = 64;
|
const k_max_results = 64;
|
||||||
@@ -202,7 +209,9 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
console.log(
|
console.log(
|
||||||
`load of ${result.length} rows took ${(t2 - t0) / 1000} (${(t1 - t0) / 1000} to find ${initial_messages.length} initial messages, ${(t2 - t1) / 1000} to find ${result.length} total messages) following=${this.following.length} st=${start_time} et=${end_time}`
|
`load of ${result.length} rows took ${(t2 - t0) / 1000} (${(t1 - t0) / 1000} to find ${initial_messages.length} initial messages, ${(t2 - t1) / 1000} to find ${result.length} total messages) following=${this.following.length} st=${start_time} et=${end_time}`
|
||||||
);
|
);
|
||||||
} else if (this.hash == '#🔐') {
|
} else if (this.hash.startsWith('#🔐')) {
|
||||||
|
let ids =
|
||||||
|
this.hash == '#🔐' ? [] : this.hash.substring('#🔐'.length).split(',');
|
||||||
result = await tfrpc.rpc.query(
|
result = await tfrpc.rpc.query(
|
||||||
`
|
`
|
||||||
SELECT TRUE AS is_primary, messages.rowid, messages.id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
|
SELECT TRUE AS is_primary, messages.rowid, messages.id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
|
||||||
@@ -214,7 +223,11 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
ORDER BY messages.rowid DESC LIMIT ?4
|
ORDER BY messages.rowid DESC LIMIT ?4
|
||||||
`,
|
`,
|
||||||
[
|
[
|
||||||
JSON.stringify(this.private_messages),
|
JSON.stringify(
|
||||||
|
this.grouped_private_messages?.[JSON.stringify(ids)]?.map(
|
||||||
|
(x) => x.id
|
||||||
|
) ?? []
|
||||||
|
),
|
||||||
start_time,
|
start_time,
|
||||||
end_time,
|
end_time,
|
||||||
k_max_results,
|
k_max_results,
|
||||||
@@ -373,12 +386,16 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
let self = this;
|
let self = this;
|
||||||
this.loading++;
|
this.loading++;
|
||||||
let messages = [];
|
let messages = [];
|
||||||
|
let original_hash = this.hash;
|
||||||
try {
|
try {
|
||||||
if (this._messages_hash !== this.hash) {
|
if (this._messages_hash !== this.hash) {
|
||||||
this.messages = [];
|
this.messages = [];
|
||||||
this._messages_hash = this.hash;
|
this._messages_hash = this.hash;
|
||||||
}
|
}
|
||||||
this._messages_following = this.following;
|
this._messages_following = JSON.stringify(this.following);
|
||||||
|
this._private_messages =
|
||||||
|
JSON.stringify(this.private_messages) +
|
||||||
|
JSON.stringify(this.grouped_private_messages);
|
||||||
let now = new Date().valueOf();
|
let now = new Date().valueOf();
|
||||||
let start_time = now - 24 * 60 * 60 * 1000;
|
let start_time = now - 24 * 60 * 60 * 1000;
|
||||||
this.start_time = start_time;
|
this.start_time = start_time;
|
||||||
@@ -391,7 +408,9 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
} finally {
|
} finally {
|
||||||
this.loading--;
|
this.loading--;
|
||||||
}
|
}
|
||||||
this.messages = this.merge_messages(this.messages, messages);
|
if (this.hash == original_hash) {
|
||||||
|
this.messages = this.merge_messages(this.messages, messages);
|
||||||
|
}
|
||||||
this.time_loading = undefined;
|
this.time_loading = undefined;
|
||||||
console.log(
|
console.log(
|
||||||
`loading ${messages.length} messages done for ${self.whoami} in ${(new Date() - start_time) / 1000}s`
|
`loading ${messages.length} messages done for ${self.whoami} in ${(new Date() - start_time) / 1000}s`
|
||||||
@@ -417,12 +436,42 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
close_private_chat() {
|
||||||
|
this.mark_all_read();
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent('closeprivatechat', {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
detail: {
|
||||||
|
key: JSON.stringify(
|
||||||
|
this.hash == '#🔐'
|
||||||
|
? []
|
||||||
|
: this.hash.substring('#🔐'.length).split(',')
|
||||||
|
),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
tfrpc.rpc.setHash('#');
|
||||||
|
}
|
||||||
|
|
||||||
|
render_close_chat_button() {
|
||||||
|
if (this.hash.startsWith('#🔐')) {
|
||||||
|
return html`
|
||||||
|
<button class="w3-button w3-theme-d1" @click=${this.close_private_chat}>
|
||||||
|
Close Chat
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (
|
if (
|
||||||
!this.messages ||
|
!this.messages ||
|
||||||
this._messages_hash !== this.hash ||
|
this._messages_hash !== this.hash ||
|
||||||
JSON.stringify(this._messages_following) !==
|
this._messages_following !== JSON.stringify(this.following) ||
|
||||||
JSON.stringify(this.following)
|
this._private_messages !==
|
||||||
|
JSON.stringify(this.private_messages) +
|
||||||
|
JSON.stringify(this.grouped_private_messages)
|
||||||
) {
|
) {
|
||||||
console.log(
|
console.log(
|
||||||
`loading messages for ${this.whoami} (following ${this.following.length})`
|
`loading messages for ${this.whoami} (following ${this.following.length})`
|
||||||
@@ -482,6 +531,7 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
Mark All Read
|
Mark All Read
|
||||||
</button>`
|
</button>`
|
||||||
: undefined}
|
: undefined}
|
||||||
|
${this.render_close_chat_button()}
|
||||||
<tf-news
|
<tf-news
|
||||||
id="news"
|
id="news"
|
||||||
whoami=${this.whoami}
|
whoami=${this.whoami}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class TfTabNewsElement extends LitElement {
|
|||||||
channels_latest: {type: Object},
|
channels_latest: {type: Object},
|
||||||
connections: {type: Array},
|
connections: {type: Array},
|
||||||
private_messages: {type: Array},
|
private_messages: {type: Array},
|
||||||
|
grouped_private_messages: {type: Object},
|
||||||
recent_reactions: {type: Array},
|
recent_reactions: {type: Array},
|
||||||
peer_exchange: {type: Boolean},
|
peer_exchange: {type: Boolean},
|
||||||
is_administrator: {type: Boolean},
|
is_administrator: {type: Boolean},
|
||||||
@@ -115,6 +116,19 @@ class TfTabNewsElement extends LitElement {
|
|||||||
) {
|
) {
|
||||||
return '✉️ ';
|
return '✉️ ';
|
||||||
}
|
}
|
||||||
|
} else if (channel?.startsWith('🔐')) {
|
||||||
|
let key = JSON.stringify(channel.substring('🔐'.length).split(','));
|
||||||
|
if (this.grouped_private_messages?.[key]) {
|
||||||
|
let grouped_latest = Math.max(
|
||||||
|
...this.grouped_private_messages?.[key]?.map((x) => x.rowid)
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
this.channels_unread[channel] === undefined ||
|
||||||
|
grouped_latest > this.channels_unread[channel]
|
||||||
|
) {
|
||||||
|
return '✉️ ';
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (
|
} else if (
|
||||||
this.channels_latest[channel] &&
|
this.channels_latest[channel] &&
|
||||||
this.channels_latest[channel] > 0 &&
|
this.channels_latest[channel] > 0 &&
|
||||||
@@ -180,6 +194,10 @@ class TfTabNewsElement extends LitElement {
|
|||||||
await this.check_peer_exchange();
|
await this.check_peer_exchange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_loading() {
|
||||||
|
return this.shadowRoot?.getElementById('news')?.loading;
|
||||||
|
}
|
||||||
|
|
||||||
render_sidebar() {
|
render_sidebar() {
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
@@ -253,12 +271,29 @@ class TfTabNewsElement extends LitElement {
|
|||||||
style=${this.hash == '#👍' ? 'font-weight: bold' : undefined}
|
style=${this.hash == '#👍' ? 'font-weight: bold' : undefined}
|
||||||
>${this.unread_status('👍')}👍votes</a
|
>${this.unread_status('👍')}👍votes</a
|
||||||
>
|
>
|
||||||
<a
|
${Object.keys(this?.grouped_private_messages ?? [])
|
||||||
href="#🔐"
|
?.sort()
|
||||||
class="w3-bar-item w3-button"
|
?.map(
|
||||||
style=${this.hash == '#🔐' ? 'font-weight: bold' : undefined}
|
(key) => html`
|
||||||
>${this.unread_status('🔐')}🔐private</a
|
<a
|
||||||
>
|
href=${'#🔐' + JSON.parse(key).join(',')}
|
||||||
|
class="w3-bar-item w3-button"
|
||||||
|
style=${this.hash == '#🔐' + JSON.parse(key).join(',')
|
||||||
|
? 'font-weight: bold'
|
||||||
|
: undefined}
|
||||||
|
>${this.unread_status('🔐' + JSON.parse(key).join(','))}
|
||||||
|
${(key != '[]' ? JSON.parse(key) : [this.whoami]).map(
|
||||||
|
(id) => html`
|
||||||
|
<tf-user
|
||||||
|
id=${id}
|
||||||
|
nolink="true"
|
||||||
|
.users=${this.users}
|
||||||
|
></tf-user>
|
||||||
|
`
|
||||||
|
)}</a
|
||||||
|
>
|
||||||
|
`
|
||||||
|
)}
|
||||||
${Object.keys(this.drafts)
|
${Object.keys(this.drafts)
|
||||||
.sort()
|
.sort()
|
||||||
.map(
|
.map(
|
||||||
@@ -376,7 +411,7 @@ class TfTabNewsElement extends LitElement {
|
|||||||
return cache(html`
|
return cache(html`
|
||||||
${this.render_sidebar()}
|
${this.render_sidebar()}
|
||||||
<div
|
<div
|
||||||
style="margin-left: 2in; padding: 0px; top: 0; max-height: 100%; overflow: auto; contain: layout"
|
style="margin-left: 2in; padding: 0px; top: 0; height: 100vh; max-height: 100%; overflow: auto; contain: layout"
|
||||||
id="main"
|
id="main"
|
||||||
class="w3-main"
|
class="w3-main"
|
||||||
>
|
>
|
||||||
@@ -414,6 +449,9 @@ class TfTabNewsElement extends LitElement {
|
|||||||
.drafts=${this.drafts}
|
.drafts=${this.drafts}
|
||||||
@tf-draft=${this.draft}
|
@tf-draft=${this.draft}
|
||||||
.channel=${this.channel()}
|
.channel=${this.channel()}
|
||||||
|
.recipients=${this.hash.startsWith('#🔐')
|
||||||
|
? this.hash.substring('#🔐'.length).split(',')
|
||||||
|
: undefined}
|
||||||
></tf-compose>
|
></tf-compose>
|
||||||
</div>
|
</div>
|
||||||
${profile}
|
${profile}
|
||||||
@@ -430,6 +468,7 @@ class TfTabNewsElement extends LitElement {
|
|||||||
.channels_unread=${this.channels_unread}
|
.channels_unread=${this.channels_unread}
|
||||||
.channels_latest=${this.channels_latest}
|
.channels_latest=${this.channels_latest}
|
||||||
.private_messages=${this.private_messages}
|
.private_messages=${this.private_messages}
|
||||||
|
.grouped_private_messages=${this.grouped_private_messages}
|
||||||
.recent_reactions=${this.recent_reactions}
|
.recent_reactions=${this.recent_reactions}
|
||||||
></tf-tab-news-feed>
|
></tf-tab-news-feed>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ class TfUserElement extends LitElement {
|
|||||||
fallback_name: {type: String},
|
fallback_name: {type: String},
|
||||||
icon_only: {type: Boolean},
|
icon_only: {type: Boolean},
|
||||||
users: {type: Object},
|
users: {type: Object},
|
||||||
|
nolink: {type: Boolean},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +38,9 @@ class TfUserElement extends LitElement {
|
|||||||
let name_string = name ?? this.fallback_name ?? this.id;
|
let name_string = name ?? this.fallback_name ?? this.id;
|
||||||
name = this.icon_only
|
name = this.icon_only
|
||||||
? undefined
|
? undefined
|
||||||
: html`<a target="_top" href=${'#' + this.id}>${name_string}</a>`;
|
: !this.nolink
|
||||||
|
? html`<a target="_top" href=${'#' + this.id}>${name_string}</a>`
|
||||||
|
: html`<span>${name_string}</span>`;
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
let image_link = user.image;
|
let image_link = user.image;
|
||||||
@@ -56,7 +59,8 @@ class TfUserElement extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return html` <div
|
return html` <div
|
||||||
style="display: inline-block; vertical-align: middle; font-weight: bold; text-wrap: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis"
|
style=${'display: inline-block; vertical-align: middle; text-wrap: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis' +
|
||||||
|
(this.nolink ? '' : '; font-weight: bold')}
|
||||||
>
|
>
|
||||||
${image} ${name}
|
${image} ${name}
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|||||||
@@ -104,12 +104,12 @@ export function markdown(md) {
|
|||||||
node.destination.startsWith('@') &&
|
node.destination.startsWith('@') &&
|
||||||
node.destination.endsWith('.ed25519')
|
node.destination.endsWith('.ed25519')
|
||||||
) {
|
) {
|
||||||
node.destination = '#' + node.destination;
|
node.destination = '#' + encodeURIComponent(node.destination);
|
||||||
} else if (
|
} else if (
|
||||||
node.destination.startsWith('%') &&
|
node.destination.startsWith('%') &&
|
||||||
node.destination.endsWith('.sha256')
|
node.destination.endsWith('.sha256')
|
||||||
) {
|
) {
|
||||||
node.destination = '#' + node.destination;
|
node.destination = '#' + encodeURIComponent(node.destination);
|
||||||
} else if (
|
} else if (
|
||||||
node.destination.startsWith('&') &&
|
node.destination.startsWith('&') &&
|
||||||
node.destination.endsWith('.sha256')
|
node.destination.endsWith('.sha256')
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "👋",
|
"emoji": "👋",
|
||||||
"previous": "&5NkMRSgcMqCYF3xcLOBmaytkoxfV9zx4br7JladKPTs=.sha256"
|
"previous": "&ijyL/pyTwguBd9njagU7Vpc/1EyRermZuzrlq1mnzbY=.sha256"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,7 +104,7 @@
|
|||||||
src="googleplay.svg"
|
src="googleplay.svg"
|
||||||
style="height: 2em; margin: 0"
|
style="height: 2em; margin: 0"
|
||||||
/>
|
/>
|
||||||
Get it on Google Play (Open Testing)
|
Get it on Google Play
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||||
@@ -298,7 +298,7 @@
|
|||||||
|
|
||||||
<!-- Technlology Section -->
|
<!-- Technlology Section -->
|
||||||
<div class="w3-container w3-padding-64 w3-light-grey w3-center">
|
<div class="w3-container w3-padding-64 w3-light-grey w3-center">
|
||||||
<h1 class="w3-jumbo"><b>Built the Old Fashioned Way</b></h1>
|
<h1 class="w3-jumbo"><b>Built to Last</b></h1>
|
||||||
<p>
|
<p>
|
||||||
Tilde Friends strives to use only simple and widely adopted dependencies
|
Tilde Friends strives to use only simple and widely adopted dependencies
|
||||||
in order to keep it easy to build for all sorts of platforms and
|
in order to keep it easy to build for all sorts of platforms and
|
||||||
|
|||||||
39
core/app.js
39
core/app.js
@@ -1,7 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \defgroup tfapp Tilde Friends App JS
|
||||||
|
* Tilde Friends server-side app wrapper.
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** \cond */
|
||||||
import * as core from './core.js';
|
import * as core from './core.js';
|
||||||
|
|
||||||
let gSessionIndex = 0;
|
export {App};
|
||||||
|
/** \endcond */
|
||||||
|
|
||||||
|
/** A sequence number of apps. */
|
||||||
|
let g_session_index = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
** App constructor.
|
||||||
|
** @return An app instance.
|
||||||
|
*/
|
||||||
function App() {
|
function App() {
|
||||||
this._send_queue = [];
|
this._send_queue = [];
|
||||||
this.calls = {};
|
this.calls = {};
|
||||||
@@ -9,6 +25,12 @@ function App() {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Create a function wrapper that when called invokes a function on the app
|
||||||
|
** itself.
|
||||||
|
** @param api The function and argument names.
|
||||||
|
** @return A function.
|
||||||
|
*/
|
||||||
App.prototype.makeFunction = function (api) {
|
App.prototype.makeFunction = function (api) {
|
||||||
let self = this;
|
let self = this;
|
||||||
let result = function () {
|
let result = function () {
|
||||||
@@ -32,6 +54,10 @@ App.prototype.makeFunction = function (api) {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Send a message to the app.
|
||||||
|
** @param message The message to send.
|
||||||
|
*/
|
||||||
App.prototype.send = function (message) {
|
App.prototype.send = function (message) {
|
||||||
if (this._send_queue) {
|
if (this._send_queue) {
|
||||||
if (this._on_output) {
|
if (this._on_output) {
|
||||||
@@ -46,6 +72,11 @@ App.prototype.send = function (message) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
** App socket handler.
|
||||||
|
** @param request The HTTP request of the WebSocket connection.
|
||||||
|
** @param response The HTTP response.
|
||||||
|
*/
|
||||||
exports.app_socket = async function socket(request, response) {
|
exports.app_socket = async function socket(request, response) {
|
||||||
let process;
|
let process;
|
||||||
let options = {};
|
let options = {};
|
||||||
@@ -118,7 +149,7 @@ exports.app_socket = async function socket(request, response) {
|
|||||||
parentApp: parentApp,
|
parentApp: parentApp,
|
||||||
id: blobId,
|
id: blobId,
|
||||||
},
|
},
|
||||||
await ssb.getIdentityInfo(
|
await ssb_internal.getIdentityInfo(
|
||||||
credentials?.session?.name,
|
credentials?.session?.name,
|
||||||
packageOwner,
|
packageOwner,
|
||||||
packageName
|
packageName
|
||||||
@@ -133,7 +164,7 @@ exports.app_socket = async function socket(request, response) {
|
|||||||
options.packageOwner = packageOwner;
|
options.packageOwner = packageOwner;
|
||||||
options.packageName = packageName;
|
options.packageName = packageName;
|
||||||
options.url = message.url;
|
options.url = message.url;
|
||||||
let sessionId = 'session_' + (gSessionIndex++).toString();
|
let sessionId = 'session_' + (g_session_index++).toString();
|
||||||
if (blobId) {
|
if (blobId) {
|
||||||
if (message.edit_only) {
|
if (message.edit_only) {
|
||||||
response.send(
|
response.send(
|
||||||
@@ -218,4 +249,4 @@ exports.app_socket = async function socket(request, response) {
|
|||||||
response.upgrade(100, {});
|
response.upgrade(100, {});
|
||||||
};
|
};
|
||||||
|
|
||||||
export {App};
|
/** @} */
|
||||||
|
|||||||
128
core/client.js
128
core/client.js
@@ -72,7 +72,7 @@ class TfNavigationElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Toggle editor visibility.
|
||||||
* @param event The HTML event.
|
* @param event The HTML event.
|
||||||
*/
|
*/
|
||||||
toggle_edit(event) {
|
toggle_edit(event) {
|
||||||
@@ -85,7 +85,7 @@ class TfNavigationElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Remove a stored permission.
|
||||||
* @param key The permission to reset.
|
* @param key The permission to reset.
|
||||||
*/
|
*/
|
||||||
reset_permission(key) {
|
reset_permission(key) {
|
||||||
@@ -93,7 +93,7 @@ class TfNavigationElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Get or create a spark line.
|
||||||
* @param key The spark line identifier.
|
* @param key The spark line identifier.
|
||||||
* @param options Spark line options.
|
* @param options Spark line options.
|
||||||
* @return A spark line HTML element.
|
* @return A spark line HTML element.
|
||||||
@@ -262,8 +262,8 @@ class TfNavigationElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Render the permissions popup.
|
||||||
* @returns
|
* @return Lit HTML.
|
||||||
*/
|
*/
|
||||||
render_permissions() {
|
render_permissions() {
|
||||||
if (this.show_permissions) {
|
if (this.show_permissions) {
|
||||||
@@ -312,8 +312,8 @@ class TfNavigationElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Render the navigation bar.
|
||||||
* @returns
|
* @return Lit HTML.
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
let self = this;
|
let self = this;
|
||||||
@@ -438,10 +438,13 @@ class TfNavigationElement extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a tf-navigation element.
|
||||||
|
*/
|
||||||
customElements.define('tf-navigation', TfNavigationElement);
|
customElements.define('tf-navigation', TfNavigationElement);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* A file in the files sidebar.
|
||||||
*/
|
*/
|
||||||
class TfFilesElement extends LitElement {
|
class TfFilesElement extends LitElement {
|
||||||
/**
|
/**
|
||||||
@@ -467,7 +470,7 @@ class TfFilesElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Select a clicked file.
|
||||||
* @param file The file.
|
* @param file The file.
|
||||||
*/
|
*/
|
||||||
file_click(file) {
|
file_click(file) {
|
||||||
@@ -483,9 +486,9 @@ class TfFilesElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Render a single file in the file list.
|
||||||
* @param file The file.
|
* @param file The file.
|
||||||
* @returns Lit HTML.
|
* @return Lit HTML.
|
||||||
*/
|
*/
|
||||||
render_file(file) {
|
render_file(file) {
|
||||||
let classes = ['file'];
|
let classes = ['file'];
|
||||||
@@ -507,7 +510,7 @@ class TfFilesElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Create a file entry for a dropped file.
|
||||||
* @param event The event.
|
* @param event The event.
|
||||||
*/
|
*/
|
||||||
async drop(event) {
|
async drop(event) {
|
||||||
@@ -533,7 +536,7 @@ class TfFilesElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Called when a file starts being dragged over the file.
|
||||||
* @param event The event.
|
* @param event The event.
|
||||||
*/
|
*/
|
||||||
drag_enter(event) {
|
drag_enter(event) {
|
||||||
@@ -543,7 +546,7 @@ class TfFilesElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Called when a file stops being dragged over the file.
|
||||||
* @param event The event.
|
* @param event The event.
|
||||||
*/
|
*/
|
||||||
drag_leave(event) {
|
drag_leave(event) {
|
||||||
@@ -554,7 +557,7 @@ class TfFilesElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Drag over event.
|
* Called when a file is being dragged over the file.
|
||||||
* @param event The event.
|
* @param event The event.
|
||||||
*/
|
*/
|
||||||
drag_over(event) {
|
drag_over(event) {
|
||||||
@@ -562,8 +565,8 @@ class TfFilesElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Render the file.
|
||||||
* @returns
|
* @return Lit HTML.
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
let self = this;
|
let self = this;
|
||||||
@@ -610,7 +613,7 @@ class TfFilesElement extends LitElement {
|
|||||||
customElements.define('tf-files', TfFilesElement);
|
customElements.define('tf-files', TfFilesElement);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* The files pane element.
|
||||||
*/
|
*/
|
||||||
class TfFilesPaneElement extends LitElement {
|
class TfFilesPaneElement extends LitElement {
|
||||||
/**
|
/**
|
||||||
@@ -635,7 +638,7 @@ class TfFilesPaneElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Set whether the files pane is expanded.
|
||||||
* @param expanded Whether the files pane is expanded.
|
* @param expanded Whether the files pane is expanded.
|
||||||
*/
|
*/
|
||||||
set_expanded(expanded) {
|
set_expanded(expanded) {
|
||||||
@@ -644,8 +647,8 @@ class TfFilesPaneElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Render the files pane element.
|
||||||
* @returns
|
* @return Lit HTML.
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
let self = this;
|
let self = this;
|
||||||
@@ -704,7 +707,7 @@ class TfFilesPaneElement extends LitElement {
|
|||||||
customElements.define('tf-files-pane', TfFilesPaneElement);
|
customElements.define('tf-files-pane', TfFilesPaneElement);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* A tiny graph.
|
||||||
*/
|
*/
|
||||||
class TfSparkLineElement extends LitElement {
|
class TfSparkLineElement extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@@ -724,9 +727,9 @@ class TfSparkLineElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Add a data point to the graph.
|
||||||
* @param {*} key
|
* @param key The line to which the point applies.
|
||||||
* @param {*} value
|
* @param value The numeric value of the data point.
|
||||||
*/
|
*/
|
||||||
append(key, value) {
|
append(key, value) {
|
||||||
let line = null;
|
let line = null;
|
||||||
@@ -753,9 +756,9 @@ class TfSparkLineElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Render a single series line.
|
||||||
* @param {*} line
|
* @param line The line data.
|
||||||
* @returns
|
* @return Lit HTML.
|
||||||
*/
|
*/
|
||||||
render_line(line) {
|
render_line(line) {
|
||||||
if (line?.values?.length >= 2) {
|
if (line?.values?.length >= 2) {
|
||||||
@@ -771,8 +774,8 @@ class TfSparkLineElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Render the graph.
|
||||||
* @returns
|
* @return Lit HTML.
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
let max =
|
let max =
|
||||||
@@ -799,7 +802,9 @@ class TfSparkLineElement extends LitElement {
|
|||||||
|
|
||||||
customElements.define('tf-sparkline', TfSparkLineElement);
|
customElements.define('tf-sparkline', TfSparkLineElement);
|
||||||
|
|
||||||
// TODOC
|
/**
|
||||||
|
* A keyboard key is pressed down.
|
||||||
|
*/
|
||||||
window.addEventListener('keydown', function (event) {
|
window.addEventListener('keydown', function (event) {
|
||||||
if (event.keyCode == 83 && (event.altKey || event.ctrlKey)) {
|
if (event.keyCode == 83 && (event.altKey || event.ctrlKey)) {
|
||||||
if (editing()) {
|
if (editing()) {
|
||||||
@@ -860,24 +865,23 @@ function ensureLoaded(nodes, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Check whether the editior is currently visible.
|
||||||
* @returns
|
* @return true if the editor is visible.
|
||||||
*/
|
*/
|
||||||
function editing() {
|
function editing() {
|
||||||
return document.getElementById('editPane').style.display != 'none';
|
return document.getElementById('editPane').style.display != 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Check whether only the editor is visible and the app is hidden.
|
||||||
* @returns
|
* @return true if the editor is visible and the app is not.
|
||||||
*/
|
*/
|
||||||
function is_edit_only() {
|
function is_edit_only() {
|
||||||
return window.location.search == '?editonly=1' || window.innerWidth < 1024;
|
return window.location.search == '?editonly=1' || window.innerWidth < 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Show the editor.
|
||||||
* @returns
|
|
||||||
*/
|
*/
|
||||||
async function edit() {
|
async function edit() {
|
||||||
if (editing()) {
|
if (editing()) {
|
||||||
@@ -904,7 +908,7 @@ async function edit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Open a performance trace.
|
||||||
*/
|
*/
|
||||||
function trace() {
|
function trace() {
|
||||||
window.open(`/speedscope/#profileURL=${encodeURIComponent('/trace')}`);
|
window.open(`/speedscope/#profileURL=${encodeURIComponent('/trace')}`);
|
||||||
@@ -982,7 +986,7 @@ async function load(path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Hide the editor.
|
||||||
*/
|
*/
|
||||||
function closeEditor() {
|
function closeEditor() {
|
||||||
window.localStorage.setItem('editing', '0');
|
window.localStorage.setItem('editing', '0');
|
||||||
@@ -990,14 +994,6 @@ function closeEditor() {
|
|||||||
document.getElementById('viewPane').style.display = 'flex';
|
document.getElementById('viewPane').style.display = 'flex';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODOC
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
function explodePath() {
|
|
||||||
return /^\/~([^\/]+)\/([^\/]+)(.*)/.exec(window.location.pathname);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the app.
|
* Save the app.
|
||||||
* @param save_to An optional path to which to save the app.
|
* @param save_to An optional path to which to save the app.
|
||||||
@@ -1111,7 +1107,7 @@ function save(save_to) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Prompt to set the app icon.
|
||||||
*/
|
*/
|
||||||
function changeIcon() {
|
function changeIcon() {
|
||||||
let value = prompt('Enter a new app icon emoji:');
|
let value = prompt('Enter a new app icon emoji:');
|
||||||
@@ -1122,7 +1118,7 @@ function changeIcon() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Prompt to delete the current app.
|
||||||
*/
|
*/
|
||||||
function deleteApp() {
|
function deleteApp() {
|
||||||
let name = document.getElementById('name');
|
let name = document.getElementById('name');
|
||||||
@@ -1143,8 +1139,8 @@ function deleteApp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Get the current app URL.
|
||||||
* @returns
|
* @return The app URL.
|
||||||
*/
|
*/
|
||||||
function url() {
|
function url() {
|
||||||
let hash = window.location.href.indexOf('#');
|
let hash = window.location.href.indexOf('#');
|
||||||
@@ -1162,8 +1158,8 @@ function url() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Get the window hash without the lone '#' if it is empty.
|
||||||
* @returns
|
* @return The hash.
|
||||||
*/
|
*/
|
||||||
function hash() {
|
function hash() {
|
||||||
return window.location.hash != '#' ? window.location.hash : '';
|
return window.location.hash != '#' ? window.location.hash : '';
|
||||||
@@ -1188,7 +1184,7 @@ function api_postMessage(message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Show an error.
|
||||||
* @param error The error.
|
* @param error The error.
|
||||||
*/
|
*/
|
||||||
function api_error(error) {
|
function api_error(error) {
|
||||||
@@ -1293,7 +1289,7 @@ function api_requestPermission(permission, id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Log from the app to the console.
|
||||||
*/
|
*/
|
||||||
function api_print() {
|
function api_print() {
|
||||||
console.log('app>', ...arguments);
|
console.log('app>', ...arguments);
|
||||||
@@ -1308,7 +1304,7 @@ function api_setHash(hash) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Process an incoming WebSocket message.
|
||||||
* @param message The message.
|
* @param message The message.
|
||||||
*/
|
*/
|
||||||
function _receive_websocket_message(message) {
|
function _receive_websocket_message(message) {
|
||||||
@@ -1432,14 +1428,14 @@ function send(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Notify the app of the window hash changing.
|
||||||
*/
|
*/
|
||||||
function hashChange() {
|
function hashChange() {
|
||||||
send({event: 'hashChange', hash: window.location.hash});
|
send({event: 'hashChange', hash: window.location.hash});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Make sure the app is connected on window focus, and notify the app.
|
||||||
*/
|
*/
|
||||||
function focus() {
|
function focus() {
|
||||||
if (gSocket && gSocket.readyState == gSocket.CLOSED) {
|
if (gSocket && gSocket.readyState == gSocket.CLOSED) {
|
||||||
@@ -1450,7 +1446,7 @@ function focus() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Notify the app of lost focus.
|
||||||
*/
|
*/
|
||||||
function blur() {
|
function blur() {
|
||||||
if (gSocket && gSocket.readyState == gSocket.OPEN) {
|
if (gSocket && gSocket.readyState == gSocket.OPEN) {
|
||||||
@@ -1617,7 +1613,7 @@ function openFile(name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Refresh the files list.
|
||||||
*/
|
*/
|
||||||
function updateFiles() {
|
function updateFiles() {
|
||||||
let files = document.getElementsByTagName('tf-files-pane')[0];
|
let files = document.getElementsByTagName('tf-files-pane')[0];
|
||||||
@@ -1650,7 +1646,7 @@ function makeNewFile(name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Prompt to create a new file.
|
||||||
*/
|
*/
|
||||||
function newFile() {
|
function newFile() {
|
||||||
let name = prompt('Name of new file:', 'file.js');
|
let name = prompt('Name of new file:', 'file.js');
|
||||||
@@ -1660,7 +1656,7 @@ function newFile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Prompt to remove a file.
|
||||||
*/
|
*/
|
||||||
function removeFile() {
|
function removeFile() {
|
||||||
if (confirm('Remove ' + gCurrentFile + '?')) {
|
if (confirm('Remove ' + gCurrentFile + '?')) {
|
||||||
@@ -1670,7 +1666,7 @@ function removeFile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Export the app to a zip file, which is downloaded by the browser.
|
||||||
*/
|
*/
|
||||||
async function appExport() {
|
async function appExport() {
|
||||||
let JsZip = (await import('/static/jszip.min.js')).default;
|
let JsZip = (await import('/static/jszip.min.js')).default;
|
||||||
@@ -1728,7 +1724,7 @@ async function save_file_to_blob_id(name, file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Prompt to import an app from a zip file.
|
||||||
*/
|
*/
|
||||||
async function appImport() {
|
async function appImport() {
|
||||||
let JsZip = (await import('/static/jszip.min.js')).default;
|
let JsZip = (await import('/static/jszip.min.js')).default;
|
||||||
@@ -1855,7 +1851,9 @@ function toggleVisibleWhitespace() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODOC
|
/**
|
||||||
|
* Register event handlers and connect the WebSocket on load.
|
||||||
|
*/
|
||||||
window.addEventListener('load', function () {
|
window.addEventListener('load', function () {
|
||||||
window.addEventListener('hashchange', hashChange);
|
window.addEventListener('hashchange', hashChange);
|
||||||
window.addEventListener('focus', focus);
|
window.addEventListener('focus', focus);
|
||||||
|
|||||||
41
core/core.js
41
core/core.js
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
/** \cond */
|
/** \cond */
|
||||||
import * as app from './app.js';
|
import * as app from './app.js';
|
||||||
import * as http from './http.js';
|
|
||||||
|
|
||||||
export {invoke, getProcessBlob};
|
export {invoke, getProcessBlob};
|
||||||
/** \endcond */
|
/** \endcond */
|
||||||
@@ -199,23 +198,6 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
let imports = {
|
let imports = {
|
||||||
core: {
|
core: {
|
||||||
broadcast: broadcast.bind(process),
|
broadcast: broadcast.bind(process),
|
||||||
register: function (eventName, handler) {
|
|
||||||
if (!process.eventHandlers[eventName]) {
|
|
||||||
process.eventHandlers[eventName] = [];
|
|
||||||
}
|
|
||||||
process.eventHandlers[eventName].push(handler);
|
|
||||||
},
|
|
||||||
unregister: function (eventName, handler) {
|
|
||||||
if (process.eventHandlers[eventName]) {
|
|
||||||
let index = process.eventHandlers[eventName].indexOf(handler);
|
|
||||||
if (index != -1) {
|
|
||||||
process.eventHandlers[eventName].splice(index, 1);
|
|
||||||
}
|
|
||||||
if (process.eventHandlers[eventName].length == 0) {
|
|
||||||
delete process.eventHandlers[eventName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
user: getUser(process, process),
|
user: getUser(process, process),
|
||||||
users: async function () {
|
users: async function () {
|
||||||
try {
|
try {
|
||||||
@@ -257,7 +239,6 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
let settings = await loadSettings();
|
let settings = await loadSettings();
|
||||||
return settings?.permissions?.[user] ?? [];
|
return settings?.permissions?.[user] ?? [];
|
||||||
},
|
},
|
||||||
getSockets: getSockets,
|
|
||||||
permissionTest: async function (permission) {
|
permissionTest: async function (permission) {
|
||||||
let user = process?.credentials?.session?.name;
|
let user = process?.credentials?.session?.name;
|
||||||
let settings = await loadSettings();
|
let settings = await loadSettings();
|
||||||
@@ -331,7 +312,7 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
{
|
{
|
||||||
action: 'identities',
|
action: 'identities',
|
||||||
},
|
},
|
||||||
await ssb.getIdentityInfo(
|
await ssb_internal.getIdentityInfo(
|
||||||
process?.credentials?.session?.name,
|
process?.credentials?.session?.name,
|
||||||
options?.packageOwner,
|
options?.packageOwner,
|
||||||
options?.packageName
|
options?.packageName
|
||||||
@@ -570,13 +551,6 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
imports.ssb.addEventListener = undefined;
|
|
||||||
imports.ssb.removeEventListener = undefined;
|
|
||||||
imports.ssb.getIdentityInfo = undefined;
|
|
||||||
imports.fetch = async function (url, options) {
|
|
||||||
let settings = await loadSettings();
|
|
||||||
return http.fetch(url, options, settings?.fetch_hosts);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
process.credentials &&
|
process.credentials &&
|
||||||
@@ -703,15 +677,22 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
return process;
|
return process;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssb.addEventListener('message', function () {
|
/**
|
||||||
|
* SSB message added callback.
|
||||||
|
*/
|
||||||
|
ssb_internal.addEventListener('message', function () {
|
||||||
broadcastEvent('onMessage', [...arguments]);
|
broadcastEvent('onMessage', [...arguments]);
|
||||||
});
|
});
|
||||||
|
|
||||||
ssb.addEventListener('broadcasts', function () {
|
ssb_internal.addEventListener('blob', function () {
|
||||||
|
broadcastEvent('onBlob', [...arguments]);
|
||||||
|
});
|
||||||
|
|
||||||
|
ssb_internal.addEventListener('broadcasts', function () {
|
||||||
broadcastEvent('onBroadcastsChanged', []);
|
broadcastEvent('onBroadcastsChanged', []);
|
||||||
});
|
});
|
||||||
|
|
||||||
ssb.addEventListener('connections', function () {
|
ssb_internal.addEventListener('connections', function () {
|
||||||
broadcastEvent('onConnectionsChanged', []);
|
broadcastEvent('onConnectionsChanged', []);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
113
core/http.js
113
core/http.js
@@ -1,113 +0,0 @@
|
|||||||
/**
|
|
||||||
* TODOC
|
|
||||||
* TODO: document so we can improve this
|
|
||||||
* @param {*} url
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
function parseUrl(url) {
|
|
||||||
// XXX: Hack.
|
|
||||||
let match = url.match(new RegExp('(\\w+)://([^/:]+)(?::(\\d+))?(.*)'));
|
|
||||||
return {
|
|
||||||
protocol: match[1],
|
|
||||||
host: match[2],
|
|
||||||
path: match[4],
|
|
||||||
port: match[3] ? parseInt(match[3]) : match[1] == 'http' ? 80 : 443,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODOC
|
|
||||||
* @param {*} data
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
function parseResponse(data) {
|
|
||||||
let firstLine;
|
|
||||||
let headers = {};
|
|
||||||
while (true) {
|
|
||||||
let endLine = data.indexOf('\r\n');
|
|
||||||
let line = data.substring(0, endLine);
|
|
||||||
data = data.substring(endLine + 2);
|
|
||||||
if (!line.length) {
|
|
||||||
break;
|
|
||||||
} else if (!firstLine) {
|
|
||||||
firstLine = line;
|
|
||||||
} else {
|
|
||||||
let colon = line.indexOf(':');
|
|
||||||
headers[line.substring(colon)] = line.substring(colon + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {body: data};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODOC
|
|
||||||
* @param {*} url
|
|
||||||
* @param {*} options
|
|
||||||
* @param {*} allowed_hosts
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function fetch(url, options, allowed_hosts) {
|
|
||||||
let parsed = parseUrl(url);
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
if ((allowed_hosts ?? []).indexOf(parsed.host) == -1) {
|
|
||||||
throw new Error(`fetch() request to host ${parsed.host} is not allowed.`);
|
|
||||||
}
|
|
||||||
let socket = new Socket();
|
|
||||||
let buffer = new Uint8Array(0);
|
|
||||||
|
|
||||||
return socket
|
|
||||||
.connect(parsed.host, parsed.port)
|
|
||||||
.then(function () {
|
|
||||||
socket.read(function (data) {
|
|
||||||
if (data && data.length) {
|
|
||||||
let newBuffer = new Uint8Array(buffer.length + data.length);
|
|
||||||
newBuffer.set(buffer, 0);
|
|
||||||
newBuffer.set(data, buffer.length);
|
|
||||||
buffer = newBuffer;
|
|
||||||
} else {
|
|
||||||
let result = parseHttpResponse(buffer);
|
|
||||||
if (!result) {
|
|
||||||
reject(new Exception('Parse failed.'));
|
|
||||||
}
|
|
||||||
if (typeof result == 'number') {
|
|
||||||
if (result == -2) {
|
|
||||||
reject('Incomplete request.');
|
|
||||||
} else {
|
|
||||||
reject('Bad request.');
|
|
||||||
}
|
|
||||||
} else if (typeof result == 'object') {
|
|
||||||
resolve({
|
|
||||||
body: buffer.slice(result.bytes_parsed),
|
|
||||||
status: result.status,
|
|
||||||
message: result.message,
|
|
||||||
headers: result.headers,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
reject(new Exception('Unexpected parse result.'));
|
|
||||||
}
|
|
||||||
resolve(parseResponse(utf8Decode(buffer)));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (parsed.port == 443) {
|
|
||||||
return socket.startTls();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(function () {
|
|
||||||
let body =
|
|
||||||
typeof options?.body == 'string'
|
|
||||||
? utf8Encode(options.body)
|
|
||||||
: options.body || new Uint8Array(0);
|
|
||||||
let headers = utf8Encode(
|
|
||||||
`${options?.method ?? 'GET'} ${parsed.path} HTTP/1.0\r\nHost: ${parsed.host}\r\nConnection: close\r\nContent-Length: ${body.length}\r\n\r\n`
|
|
||||||
);
|
|
||||||
let fullRequest = new Uint8Array(headers.length + body.length);
|
|
||||||
fullRequest.set(headers, 0);
|
|
||||||
fullRequest.set(body, headers.length);
|
|
||||||
socket.write(fullRequest);
|
|
||||||
})
|
|
||||||
.catch(function (error) {
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -15,8 +15,8 @@ let g_next_id = 1;
|
|||||||
let g_calls = {};
|
let g_calls = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* Check if being called from a browser vs. server-side.
|
||||||
* @returns
|
* @return true if called from a browser.
|
||||||
*/
|
*/
|
||||||
function get_is_browser() {
|
function get_is_browser() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -25,14 +25,14 @@
|
|||||||
}:
|
}:
|
||||||
pkgs.stdenv.mkDerivation rec {
|
pkgs.stdenv.mkDerivation rec {
|
||||||
pname = "tildefriends";
|
pname = "tildefriends";
|
||||||
version = "0.0.32";
|
version = "0.2025.9";
|
||||||
|
|
||||||
src = pkgs.fetchFromGitea {
|
src = pkgs.fetchFromGitea {
|
||||||
domain = "dev.tildefriends.net";
|
domain = "dev.tildefriends.net";
|
||||||
owner = "cory";
|
owner = "cory";
|
||||||
repo = "tildefriends";
|
repo = "tildefriends";
|
||||||
rev = "v${version}";
|
rev = "v${version}";
|
||||||
hash = "sha256-Dk0NOEQIg2LeENySK0+MgpZEtfsClGq6dZL+eOOpE0U=";
|
hash = "sha256-1nhsfhdOO5HIiiTMb+uROB8nDPL/UpOYm52hZ/OpPyk=";
|
||||||
fetchSubmodules = true;
|
fetchSubmodules = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
2
deps/codemirror/cm6.js
vendored
2
deps/codemirror/cm6.js
vendored
File diff suppressed because one or more lines are too long
270
deps/codemirror_src/package-lock.json
generated
vendored
270
deps/codemirror_src/package-lock.json
generated
vendored
@@ -19,9 +19,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/autocomplete": {
|
"node_modules/@codemirror/autocomplete": {
|
||||||
"version": "6.18.6",
|
"version": "6.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.19.0.tgz",
|
||||||
"integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==",
|
"integrity": "sha512-61Hfv3cF07XvUxNeC3E7jhG8XNi1Yom1G0lRC936oLnlF+jrbrv8rc/J98XlYzcsAoTVupfsf5fLej1aI8kyIg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/language": "^6.0.0",
|
"@codemirror/language": "^6.0.0",
|
||||||
@@ -56,9 +56,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/lang-html": {
|
"node_modules/@codemirror/lang-html": {
|
||||||
"version": "6.4.9",
|
"version": "6.4.10",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.9.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.10.tgz",
|
||||||
"integrity": "sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==",
|
"integrity": "sha512-h/SceTVsN5r+WE+TVP2g3KDvNoSzbSrtZXCKo4vkKdbfT5t4otuVgngGdFukOO/rwRD2++pCxoh6xD4TEVMkQA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.0.0",
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
@@ -98,9 +98,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/language": {
|
"node_modules/@codemirror/language": {
|
||||||
"version": "6.11.2",
|
"version": "6.11.3",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.2.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.3.tgz",
|
||||||
"integrity": "sha512-p44TsNArL4IVXDTbapUmEkAlvWs2CFQbcfc0ymDsis1kH2wh0gcY96AS29c/vp2d0y2Tquk1EDSaawpzilUiAw==",
|
"integrity": "sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.0.0",
|
"@codemirror/state": "^6.0.0",
|
||||||
@@ -155,9 +155,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/view": {
|
"node_modules/@codemirror/view": {
|
||||||
"version": "6.38.1",
|
"version": "6.38.4",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.1.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.4.tgz",
|
||||||
"integrity": "sha512-RmTOkE7hRU3OVREqFVITWHz6ocgBjv08GoePscAakgVQfciA3SGCEk7mb9IzwW61cKKmlTpHXG6DUE5Ubx+MGQ==",
|
"integrity": "sha512-hduz0suCcUSC/kM8Fq3A9iLwInJDl8fD1xLpTIk+5xkNm8z/FT7UsIa9sOXrkpChh+XXc18RzswE8QqELsVl+g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.5.0",
|
"@codemirror/state": "^6.5.0",
|
||||||
@@ -167,9 +167,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/gen-mapping": {
|
"node_modules/@jridgewell/gen-mapping": {
|
||||||
"version": "0.3.12",
|
"version": "0.3.13",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||||
"integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==",
|
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -188,9 +188,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/source-map": {
|
"node_modules/@jridgewell/source-map": {
|
||||||
"version": "0.3.10",
|
"version": "0.3.11",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.10.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
|
||||||
"integrity": "sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==",
|
"integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -199,16 +199,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/sourcemap-codec": {
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
"version": "1.5.4",
|
"version": "1.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||||
"integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==",
|
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/trace-mapping": {
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
"version": "0.3.29",
|
"version": "0.3.31",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
|
||||||
"integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==",
|
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -243,9 +243,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@lezer/html": {
|
"node_modules/@lezer/html": {
|
||||||
"version": "1.3.10",
|
"version": "1.3.12",
|
||||||
"resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.10.tgz",
|
"resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.12.tgz",
|
||||||
"integrity": "sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==",
|
"integrity": "sha512-RJ7eRWdaJe3bsiiLLHjCFT1JMk8m1YP9kaUbvu2rMLEoOnke9mcTVDyfOslsln0LtujdWespjJ39w6zo+RsQYw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lezer/common": "^1.2.0",
|
"@lezer/common": "^1.2.0",
|
||||||
@@ -254,9 +254,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@lezer/javascript": {
|
"node_modules/@lezer/javascript": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz",
|
||||||
"integrity": "sha512-ATOImjeVJuvgm3JQ/bpo2Tmv55HSScE2MTPnKRMRIPx2cLhHGyX2VnqpHhtIV1tVzIjZDbcWQm+NCTF40ggZVw==",
|
"integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lezer/common": "^1.2.0",
|
"@lezer/common": "^1.2.0",
|
||||||
@@ -338,9 +338,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/pluginutils": {
|
"node_modules/@rollup/pluginutils": {
|
||||||
"version": "5.2.0",
|
"version": "5.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
|
||||||
"integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==",
|
"integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "^1.0.0",
|
"@types/estree": "^1.0.0",
|
||||||
@@ -360,9 +360,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.3.tgz",
|
||||||
"integrity": "sha512-9f3nSTFI2ivfxc7/tHBHcJ8pRnp8ROrELvsVprlQPVvcZ+j5zztYd+PTJGpyIOAdTvNwNrpCXswKSeoQcyGjMQ==",
|
"integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -373,9 +373,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm64": {
|
"node_modules/@rollup/rollup-android-arm64": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.3.tgz",
|
||||||
"integrity": "sha512-tFZSEhqJ8Yrpe50TzOdeoYi72gi/jsnT7y8Qrozf3cNu28WX+s6I3XzEPUAqoaT9SAS8Xz9AzGTFlxxCH/w20w==",
|
"integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -386,9 +386,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.3.tgz",
|
||||||
"integrity": "sha512-+DikIIs+p6yU2hF51UaWG8BnHbq90X0QIOt5zqSKSZxY+G3qqdLih214e9InJal21af2PuuxkDectetGfbVPJw==",
|
"integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -399,9 +399,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-x64": {
|
"node_modules/@rollup/rollup-darwin-x64": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.3.tgz",
|
||||||
"integrity": "sha512-5a+NofhdEB/WimSlFMskbFQn1vqz1FWryYpA99trmZGO6qEmiS0IsX6w4B3d91U878Q2ZQdiaFF1gxX4P147og==",
|
"integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -412,9 +412,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.3.tgz",
|
||||||
"integrity": "sha512-igr/RlKPS3OCy4jD3XBmAmo3UAcNZkJSubRsw1JeM8bAbwf15k/3eMZXD91bnjheijJiOJcga3kfCLKjV8IXNg==",
|
"integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -425,9 +425,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.3.tgz",
|
||||||
"integrity": "sha512-MdigWzPSHlQzB1xZ+MdFDWTAH+kcn7UxjEBoOKuaso7z1DRlnAnrknB1mTtNOQ+GdPI8xgExAGwHeqQjntR0Cg==",
|
"integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -438,9 +438,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.3.tgz",
|
||||||
"integrity": "sha512-dmZseE0ZwA/4yy1+BwFrDqFTjjNg24GO9xSrb1weVbt6AFkhp5pz1gVS7IMtfIvoWy8yp6q/zN0bKnefRUImvQ==",
|
"integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -451,9 +451,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.3.tgz",
|
||||||
"integrity": "sha512-fzhfn6p9Cfm3W8UrWKIa4l7Wfjs/KGdgaswMBBE3KY3Ta43jg2XsPrAtfezHpsRk0Nx+TFuS3hZk/To2N5kFPQ==",
|
"integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -464,9 +464,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.3.tgz",
|
||||||
"integrity": "sha512-vVDD+iPDPmJQ5nAQ5Tifq3ywdv60FartglFI8VOCK+hcU9aoG0qlQTsDJP97O5yiTaTqlneZWoARMcVC5nyUoQ==",
|
"integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -477,9 +477,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.3.tgz",
|
||||||
"integrity": "sha512-0d0jx08fzDHCzXqrtCMEEyxKU0SvJrWmUjUDE2/KDQ2UDJql0tfiwYvEx1oHELClKO8CNdE+AGJj+RqXscZpdQ==",
|
"integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -489,10 +489,10 @@
|
|||||||
"linux"
|
"linux"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.3.tgz",
|
||||||
"integrity": "sha512-XBYu9oW9eKJadWn8M7hkTZsD4yG+RrsTrVEgyKwb4L72cpJjRbRboTG9Lg9fec8MxJp/cfTHAocg4mnismQR8A==",
|
"integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
@@ -503,9 +503,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.3.tgz",
|
||||||
"integrity": "sha512-wJaRvcT17PoOK6Ggcfo3nouFlybHvARBS4jzT0PC/lg17fIJHcDS2fZz3sD+iA4nRlho2zE6OGbU0HvwATdokQ==",
|
"integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -516,9 +516,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.3.tgz",
|
||||||
"integrity": "sha512-GZ5bkMFteAGkcmh8x0Ok4LSa+L62Ez0tMsHPX6JtR0wl4Xc3bQcrFHDiR5DGLEDFtGrXih4Nd/UDaFqs968/wA==",
|
"integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@@ -529,9 +529,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.3.tgz",
|
||||||
"integrity": "sha512-7CjPw6FflFsVOUfWOrVrREiV3IYXG4RzZ1ZQUaT3BtSK8YXN6x286o+sruPZJESIaPebYuFowmg54ZdrkVBYog==",
|
"integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@@ -542,9 +542,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.3.tgz",
|
||||||
"integrity": "sha512-nmvnl0ZiuysltcB/cKjUh40Rx4FbSyueERDsl2FLvLYr6pCgSsvGr3SocUT84svSpmloS7f1DRWqtRha74Gi1w==",
|
"integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@@ -555,9 +555,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.3.tgz",
|
||||||
"integrity": "sha512-Cv+moII5C8RM6gZbR3cb21o6rquVDZrN2o81maROg1LFzBz2dZUwIQSxFA8GtGZ/F2KtsqQ2z3eFPBb6akvQNg==",
|
"integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -568,9 +568,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.3.tgz",
|
||||||
"integrity": "sha512-PHcMG8DZTM9RCIjp8QIfN0VYtX0TtBPnWOTRurFhoCDoi9zptUZL2k7pCs+5rgut7JAiUsYy+huyhVKPcmxoog==",
|
"integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -580,10 +580,23 @@
|
|||||||
"linux"
|
"linux"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@rollup/rollup-openharmony-arm64": {
|
||||||
|
"version": "4.52.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.3.tgz",
|
||||||
|
"integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openharmony"
|
||||||
|
]
|
||||||
|
},
|
||||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.3.tgz",
|
||||||
"integrity": "sha512-1SI/Rd47e8aQJeFWMDg16ET+fjvCcD/CzeaRmIEPmb05hx+3cCcwIF4ebUag4yTt/D1peE+Mgp0+Po3M358cAA==",
|
"integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -594,9 +607,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.3.tgz",
|
||||||
"integrity": "sha512-JwOCYxmumFDfDhx4kNyz6kTVK3gWzBIvVdMNzQMRDubcoGRDniOOmo6DDNP42qwZx3Bp9/6vWJ+kNzNqXoHmeA==",
|
"integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@@ -606,10 +619,23 @@
|
|||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
||||||
|
"version": "4.52.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.3.tgz",
|
||||||
|
"integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
]
|
||||||
|
},
|
||||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.3.tgz",
|
||||||
"integrity": "sha512-IPMIfrfkG1GaEXi+JSsQEx8x9b4b+hRZXO7KYc2pKio3zO2/VDXDs6B9Ts/nnO+25Fk1tdAVtUn60HKKPPzDig==",
|
"integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -799,9 +825,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.46.0",
|
"version": "4.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz",
|
||||||
"integrity": "sha512-ONmkT3Ud3IfW15nl7l4qAZko5/2iZ5ALVBDh02ZSZ5IGVLJSYkRcRa3iB58VyEIyoofs9m2xdVrm+lTi97+3pw==",
|
"integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "1.0.8"
|
"@types/estree": "1.0.8"
|
||||||
@@ -814,26 +840,28 @@
|
|||||||
"npm": ">=8.0.0"
|
"npm": ">=8.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rollup/rollup-android-arm-eabi": "4.46.0",
|
"@rollup/rollup-android-arm-eabi": "4.52.3",
|
||||||
"@rollup/rollup-android-arm64": "4.46.0",
|
"@rollup/rollup-android-arm64": "4.52.3",
|
||||||
"@rollup/rollup-darwin-arm64": "4.46.0",
|
"@rollup/rollup-darwin-arm64": "4.52.3",
|
||||||
"@rollup/rollup-darwin-x64": "4.46.0",
|
"@rollup/rollup-darwin-x64": "4.52.3",
|
||||||
"@rollup/rollup-freebsd-arm64": "4.46.0",
|
"@rollup/rollup-freebsd-arm64": "4.52.3",
|
||||||
"@rollup/rollup-freebsd-x64": "4.46.0",
|
"@rollup/rollup-freebsd-x64": "4.52.3",
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": "4.46.0",
|
"@rollup/rollup-linux-arm-gnueabihf": "4.52.3",
|
||||||
"@rollup/rollup-linux-arm-musleabihf": "4.46.0",
|
"@rollup/rollup-linux-arm-musleabihf": "4.52.3",
|
||||||
"@rollup/rollup-linux-arm64-gnu": "4.46.0",
|
"@rollup/rollup-linux-arm64-gnu": "4.52.3",
|
||||||
"@rollup/rollup-linux-arm64-musl": "4.46.0",
|
"@rollup/rollup-linux-arm64-musl": "4.52.3",
|
||||||
"@rollup/rollup-linux-loongarch64-gnu": "4.46.0",
|
"@rollup/rollup-linux-loong64-gnu": "4.52.3",
|
||||||
"@rollup/rollup-linux-ppc64-gnu": "4.46.0",
|
"@rollup/rollup-linux-ppc64-gnu": "4.52.3",
|
||||||
"@rollup/rollup-linux-riscv64-gnu": "4.46.0",
|
"@rollup/rollup-linux-riscv64-gnu": "4.52.3",
|
||||||
"@rollup/rollup-linux-riscv64-musl": "4.46.0",
|
"@rollup/rollup-linux-riscv64-musl": "4.52.3",
|
||||||
"@rollup/rollup-linux-s390x-gnu": "4.46.0",
|
"@rollup/rollup-linux-s390x-gnu": "4.52.3",
|
||||||
"@rollup/rollup-linux-x64-gnu": "4.46.0",
|
"@rollup/rollup-linux-x64-gnu": "4.52.3",
|
||||||
"@rollup/rollup-linux-x64-musl": "4.46.0",
|
"@rollup/rollup-linux-x64-musl": "4.52.3",
|
||||||
"@rollup/rollup-win32-arm64-msvc": "4.46.0",
|
"@rollup/rollup-openharmony-arm64": "4.52.3",
|
||||||
"@rollup/rollup-win32-ia32-msvc": "4.46.0",
|
"@rollup/rollup-win32-arm64-msvc": "4.52.3",
|
||||||
"@rollup/rollup-win32-x64-msvc": "4.46.0",
|
"@rollup/rollup-win32-ia32-msvc": "4.52.3",
|
||||||
|
"@rollup/rollup-win32-x64-gnu": "4.52.3",
|
||||||
|
"@rollup/rollup-win32-x64-msvc": "4.52.3",
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -915,14 +943,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/terser": {
|
"node_modules/terser": {
|
||||||
"version": "5.43.1",
|
"version": "5.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz",
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz",
|
||||||
"integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==",
|
"integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/source-map": "^0.3.3",
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
"acorn": "^8.14.0",
|
"acorn": "^8.15.0",
|
||||||
"commander": "^2.20.0",
|
"commander": "^2.20.0",
|
||||||
"source-map-support": "~0.5.20"
|
"source-map-support": "~0.5.20"
|
||||||
},
|
},
|
||||||
|
|||||||
2
deps/libbacktrace
vendored
2
deps/libbacktrace
vendored
Submodule deps/libbacktrace updated: f1104f3270...2f67a3abfd
2
deps/openssl_src
vendored
2
deps/openssl_src
vendored
Submodule deps/openssl_src updated: aea7aaf2ab...7b371d80d9
2
deps/quickjs
vendored
2
deps/quickjs
vendored
Submodule deps/quickjs updated: 19abf1888d...fa628f8c52
2
deps/speedscope/index.html
vendored
2
deps/speedscope/index.html
vendored
@@ -11,7 +11,7 @@
|
|||||||
<link rel="icon" type="image/x-icon" href="favicon-FOKUP5Y5.ico">
|
<link rel="icon" type="image/x-icon" href="favicon-FOKUP5Y5.ico">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script src="speedscope-7YPLLUY2.js"></script>
|
<script src="speedscope-HCR63FMT.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
6
deps/speedscope/release.txt
vendored
6
deps/speedscope/release.txt
vendored
@@ -1,3 +1,3 @@
|
|||||||
speedscope@1.23.0
|
speedscope@1.23.1
|
||||||
Sun Jul 6 20:04:28 PDT 2025
|
Mon Aug 11 11:43:09 PDT 2025
|
||||||
aa9bef50789a2989746b576fff182b6f01dfce6a
|
0cec0f82c334aed6cf19d43cabeadcda0d95e0fc
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -45,7 +45,6 @@ options:
|
|||||||
out_http_port_file (default: ""): File to which to write bound HTTP port.
|
out_http_port_file (default: ""): File to which to write bound HTTP port.
|
||||||
blob_fetch_age_seconds (default: -1): Only blobs mentioned more recently than this age will be automatically fetched.
|
blob_fetch_age_seconds (default: -1): Only blobs mentioned more recently than this age will be automatically fetched.
|
||||||
blob_expire_age_seconds (default: -1): Blobs older than this will be automatically deleted.
|
blob_expire_age_seconds (default: -1): Blobs older than this will be automatically deleted.
|
||||||
fetch_hosts (default: ""): Comma-separated list of host names to which HTTP fetch requests are allowed. None if empty.
|
|
||||||
http_redirect (default: ""): If connecting by HTTP and HTTPS is configured, Location header prefix (ie, "http://example.com")
|
http_redirect (default: ""): If connecting by HTTP and HTTPS is configured, Location header prefix (ie, "http://example.com")
|
||||||
index (default: "/~core/intro/"): Default path.
|
index (default: "/~core/intro/"): Default path.
|
||||||
index_map (default: ""): Mappings from hostname to redirect path, one per line, as in: "www.tildefriends.net=/~core/index/"
|
index_map (default: ""): Mappings from hostname to redirect path, one per line, as in: "www.tildefriends.net=/~core/index/"
|
||||||
|
|||||||
6
flake.lock
generated
6
flake.lock
generated
@@ -20,11 +20,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1750622754,
|
"lastModified": 1758589230,
|
||||||
"narHash": "sha256-kMhs+YzV4vPGfuTpD3mwzibWUE6jotw5Al2wczI0Pv8=",
|
"narHash": "sha256-zMTCFGe8aVGTEr2RqUi/QzC1nOIQ0N1HRsbqB4f646k=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "c7ab75210cb8cb16ddd8f290755d9558edde7ee1",
|
"rev": "d1d883129b193f0b495d75c148c2c3a7d95789a0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
9
metadata/en-US/changelogs/42.txt
Normal file
9
metadata/en-US/changelogs/42.txt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
* Private messages interface overhaul in progress.
|
||||||
|
* Added a loading indicator.
|
||||||
|
* Documented the core JavaScript.
|
||||||
|
* Fixed @-completion.
|
||||||
|
* Covered up launch on Android with the splash screen.
|
||||||
|
* Update:
|
||||||
|
* CodeMirror
|
||||||
|
* OpenSSL 3.5.2
|
||||||
|
* speedscope 1.23.1
|
||||||
8
metadata/en-US/changelogs/43.txt
Normal file
8
metadata/en-US/changelogs/43.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
* Fixed multiple issues with blob replication.
|
||||||
|
* Fixed some link encoding issues.
|
||||||
|
* Fixed some context menus being cut off.
|
||||||
|
* Minor Android fixes.
|
||||||
|
* Updates:
|
||||||
|
* CodeMirror
|
||||||
|
* OpenSSL 3.5.3
|
||||||
|
* QuickJS 2025-09-13
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.unprompted.tildefriends"
|
package="com.unprompted.tildefriends"
|
||||||
android:versionCode="40"
|
android:versionCode="44"
|
||||||
android:versionName="0.0.33">
|
android:versionName="0.2025.10-wip">
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<application
|
<application
|
||||||
|
|||||||
@@ -19,12 +19,12 @@ import android.os.RemoteException;
|
|||||||
import android.os.StrictMode;
|
import android.os.StrictMode;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup.LayoutParams;
|
import android.view.ViewGroup.LayoutParams;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
|
import android.view.ViewTreeObserver;
|
||||||
import android.webkit.CookieManager;
|
import android.webkit.CookieManager;
|
||||||
import android.webkit.DownloadListener;
|
import android.webkit.DownloadListener;
|
||||||
import android.webkit.JsPromptResult;
|
import android.webkit.JsPromptResult;
|
||||||
@@ -43,6 +43,7 @@ import java.io.File;
|
|||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class TildeFriendsActivity extends Activity {
|
public class TildeFriendsActivity extends Activity {
|
||||||
@@ -52,28 +53,42 @@ public class TildeFriendsActivity extends Activity {
|
|||||||
String port_file_path;
|
String port_file_path;
|
||||||
Thread create_thread;
|
Thread create_thread;
|
||||||
Thread server_thread;
|
Thread server_thread;
|
||||||
|
Thread log_thread;
|
||||||
ServiceConnection service_connection;
|
ServiceConnection service_connection;
|
||||||
FileObserver observer;
|
FileObserver observer;
|
||||||
|
LinkedBlockingQueue<String> log_queue = new LinkedBlockingQueue<String>();
|
||||||
|
|
||||||
private ValueCallback<Uri[]> upload_message;
|
private ValueCallback<Uri[]> upload_message;
|
||||||
private final static int FILECHOOSER_RESULT = 1;
|
private final static int FILECHOOSER_RESULT = 1;
|
||||||
private float touch_down_y;
|
private float touch_down_y;
|
||||||
|
private boolean ready = false;
|
||||||
|
private boolean loaded = false;
|
||||||
|
private boolean shutting_down = false;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Log.w("tildefriends", "Calling system.loadLibrary().");
|
log("Calling system.loadLibrary().");
|
||||||
System.loadLibrary("tildefriends");
|
System.loadLibrary("tildefriends");
|
||||||
Log.w("tildefriends", "system.loadLibrary() completed.");
|
log("system.loadLibrary() completed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static native int tf_server_main(String files_dir, String apk_path, String out_port_file_path, ConnectivityManager connectivity_manager);
|
public static native int tf_server_main(String files_dir, String apk_path, String out_port_file_path, ConnectivityManager connectivity_manager);
|
||||||
public static native int tf_sandbox_main(int pipe_fd);
|
public static native int tf_sandbox_main(int pipe_fd);
|
||||||
|
|
||||||
|
public static void log(String message) {
|
||||||
|
if (s_activity != null && s_activity.log_queue != null && message != null) {
|
||||||
|
try {
|
||||||
|
s_activity.log_queue.put(message);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
android.util.Log.w("tildefriends", message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void createThread() {
|
private void createThread() {
|
||||||
web_view = (TildeFriendsWebView)findViewById(R.id.web);
|
web_view = (TildeFriendsWebView)findViewById(R.id.web);
|
||||||
set_status("Extracting executable...");
|
log(String.format("getFilesDir() is %s", getFilesDir().toString()));
|
||||||
Log.w("tildefriends", String.format("getFilesDir() is %s", getFilesDir().toString()));
|
log(String.format("getPackageResourcePath() is %s", getPackageResourcePath().toString()));
|
||||||
Log.w("tildefriends", String.format("getPackageResourcePath() is %s", getPackageResourcePath().toString()));
|
log(String.format("nativeLibraryDir is %s", getApplicationInfo().nativeLibraryDir));
|
||||||
Log.w("tildefriends", String.format("nativeLibraryDir is %s", getApplicationInfo().nativeLibraryDir));
|
|
||||||
|
|
||||||
port_file_path = getFilesDir().toString() + "/port.txt";
|
port_file_path = getFilesDir().toString() + "/port.txt";
|
||||||
new File(port_file_path).delete();
|
new File(port_file_path).delete();
|
||||||
@@ -81,21 +96,20 @@ public class TildeFriendsActivity extends Activity {
|
|||||||
|
|
||||||
TildeFriendsActivity activity = this;
|
TildeFriendsActivity activity = this;
|
||||||
|
|
||||||
Log.w("tildefriends", "Watching for changes in: " + getFilesDir().toString());
|
|
||||||
observer = make_file_observer(getFilesDir().toString(), port_file_path);
|
|
||||||
observer.startWatching();
|
|
||||||
|
|
||||||
set_status("Starting server...");
|
|
||||||
server_thread = new Thread(new Runnable() {
|
server_thread = new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Log.w("tildefriends", "Calling tf_server_main.");
|
log("Watching for changes in: " + getFilesDir().toString());
|
||||||
|
observer = make_file_observer(getFilesDir().toString(), port_file_path);
|
||||||
|
observer.startWatching();
|
||||||
|
|
||||||
|
log("Calling tf_server_main.");
|
||||||
int result = tf_server_main(
|
int result = tf_server_main(
|
||||||
getFilesDir().toString(),
|
getFilesDir().toString(),
|
||||||
getPackageResourcePath().toString(),
|
getPackageResourcePath().toString(),
|
||||||
port_file_path,
|
port_file_path,
|
||||||
(ConnectivityManager)getApplicationContext().getSystemService(CONNECTIVITY_SERVICE));
|
(ConnectivityManager)getApplicationContext().getSystemService(CONNECTIVITY_SERVICE));
|
||||||
Log.w("tildefriends", "tf_server_main returned " + result + ".");
|
log("tf_server_main returned " + result + ".");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
server_thread.start();
|
server_thread.start();
|
||||||
@@ -109,17 +123,17 @@ public class TildeFriendsActivity extends Activity {
|
|||||||
|
|
||||||
web_view.setDownloadListener(new DownloadListener() {
|
web_view.setDownloadListener(new DownloadListener() {
|
||||||
public void onDownloadStart(String url, String userAgent, String content_disposition, String mime_type, long content_length) {
|
public void onDownloadStart(String url, String userAgent, String content_disposition, String mime_type, long content_length) {
|
||||||
Log.w("tildefriends", "Let's download: " + url + " (" + content_disposition + ")");
|
log("Let's download: " + url + " (" + content_disposition + ")");
|
||||||
String file_name = URLUtil.guessFileName(url, content_disposition, mime_type);
|
String file_name = URLUtil.guessFileName(url, content_disposition, mime_type);
|
||||||
if (url.startsWith("data:") && url.indexOf(',') != -1) {
|
if (url.startsWith("data:") && url.indexOf(',') != -1) {
|
||||||
String b64 = url.substring(url.indexOf(',') + 1);
|
String b64 = url.substring(url.indexOf(',') + 1);
|
||||||
byte[] data = Base64.decode(b64, Base64.DEFAULT);
|
byte[] data = Base64.decode(b64, Base64.DEFAULT);
|
||||||
Log.w("tildefriends", "Downloaded " + data.length + " bytes.");
|
log("Downloaded " + data.length + " bytes.");
|
||||||
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||||
try (OutputStream stream = new FileOutputStream(new File(path, file_name))) {
|
try (OutputStream stream = new FileOutputStream(new File(path, file_name))) {
|
||||||
stream.write(data);
|
stream.write(data);
|
||||||
} catch (java.io.IOException e) {
|
} catch (java.io.IOException e) {
|
||||||
Log.w("tildefriends", "IOException: " + e.toString());
|
log("IOException: " + e.toString());
|
||||||
}
|
}
|
||||||
Toast.makeText(getApplicationContext(), "Downloaded File", Toast.LENGTH_LONG).show();
|
Toast.makeText(getApplicationContext(), "Downloaded File", Toast.LENGTH_LONG).show();
|
||||||
} else {
|
} else {
|
||||||
@@ -227,12 +241,13 @@ public class TildeFriendsActivity extends Activity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onConsoleMessage(android.webkit.ConsoleMessage consoleMessage) {
|
public boolean onConsoleMessage(android.webkit.ConsoleMessage consoleMessage) {
|
||||||
Log.d("tildefriends", consoleMessage.message() + " -- From line " + consoleMessage.lineNumber() + " of " + consoleMessage.sourceId());
|
log(consoleMessage.message() + " -- From line " + consoleMessage.lineNumber() + " of " + consoleMessage.sourceId());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
web_view.setWebViewClient(new WebViewClient() {
|
web_view.setWebViewClient(new WebViewClient() {
|
||||||
|
@Override
|
||||||
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request)
|
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request)
|
||||||
{
|
{
|
||||||
if (request.getUrl() != null && request.getUrl().toString().startsWith(base_url)) {
|
if (request.getUrl() != null && request.getUrl().toString().startsWith(base_url)) {
|
||||||
@@ -242,6 +257,11 @@ public class TildeFriendsActivity extends Activity {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageFinished(WebView view, String url) {
|
||||||
|
s_activity.loaded = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -252,6 +272,23 @@ public class TildeFriendsActivity extends Activity {
|
|||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
s_activity = this;
|
s_activity = this;
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
log_thread = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (!s_activity.shutting_down) {
|
||||||
|
try {
|
||||||
|
String message = log_queue.take();
|
||||||
|
if (message != null) {
|
||||||
|
android.util.Log.w("tildefriends", message);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
log_thread.start();
|
||||||
StrictMode.setThreadPolicy(
|
StrictMode.setThreadPolicy(
|
||||||
new StrictMode.ThreadPolicy.Builder()
|
new StrictMode.ThreadPolicy.Builder()
|
||||||
.detectAll()
|
.detectAll()
|
||||||
@@ -271,6 +308,21 @@ public class TildeFriendsActivity extends Activity {
|
|||||||
refresh.setVisibility(View.GONE);
|
refresh.setVisibility(View.GONE);
|
||||||
refresh.setText("REFRESH");
|
refresh.setText("REFRESH");
|
||||||
|
|
||||||
|
final View content = findViewById(android.R.id.content);
|
||||||
|
content.getViewTreeObserver().addOnPreDrawListener(
|
||||||
|
new ViewTreeObserver.OnPreDrawListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreDraw() {
|
||||||
|
if (s_activity.ready && s_activity.loaded) {
|
||||||
|
content.getViewTreeObserver().removeOnPreDrawListener(this);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
create_thread = new Thread(new Runnable() {
|
create_thread = new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -283,8 +335,17 @@ public class TildeFriendsActivity extends Activity {
|
|||||||
@Override
|
@Override
|
||||||
protected void onDestroy()
|
protected void onDestroy()
|
||||||
{
|
{
|
||||||
super.onDestroy();
|
try {
|
||||||
|
shutting_down = true;
|
||||||
|
if (log_queue != null) {
|
||||||
|
log_queue.put("Goodbye.");
|
||||||
|
}
|
||||||
|
log_thread.join();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
log_thread = null;
|
||||||
s_activity = null;
|
s_activity = null;
|
||||||
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -376,46 +437,33 @@ public class TildeFriendsActivity extends Activity {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void set_status(String text) {
|
|
||||||
TextView text_view = (TextView)findViewById(R.id.text);
|
|
||||||
web_view.setVisibility(View.GONE);
|
|
||||||
text_view.setVisibility(View.VISIBLE);
|
|
||||||
text_view.setText(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void hide_status() {
|
|
||||||
TextView text_view = (TextView)findViewById(R.id.text);
|
|
||||||
web_view.setVisibility(View.VISIBLE);
|
|
||||||
text_view.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void start_sandbox(int pipe_fd) {
|
public static void start_sandbox(int pipe_fd) {
|
||||||
Log.w("tildefriends", "starting service with fd: " + pipe_fd);
|
log("starting service with fd: " + pipe_fd);
|
||||||
Intent intent = new Intent(s_activity, TildeFriendsSandboxService.class);
|
Intent intent = new Intent(s_activity, TildeFriendsSandboxService.class);
|
||||||
s_activity.service_connection = new ServiceConnection() {
|
s_activity.service_connection = new ServiceConnection() {
|
||||||
@Override
|
@Override
|
||||||
public void onBindingDied(ComponentName name) {
|
public void onBindingDied(ComponentName name) {
|
||||||
Log.w("tildefriends", "onBindingDied");
|
log("onBindingDied");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNullBinding(ComponentName name) {
|
public void onNullBinding(ComponentName name) {
|
||||||
Log.w("tildefriends", "onNullBinding");
|
log("onNullBinding");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onServiceConnected(ComponentName name, IBinder binder) {
|
public void onServiceConnected(ComponentName name, IBinder binder) {
|
||||||
Log.w("tildefriends", "onServiceConnected");
|
log("onServiceConnected");
|
||||||
Parcel data = Parcel.obtain();
|
Parcel data = Parcel.obtain();
|
||||||
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromFd(pipe_fd)) {
|
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromFd(pipe_fd)) {
|
||||||
data.writeParcelable(pfd, 0);
|
data.writeParcelable(pfd, 0);
|
||||||
} catch (java.io.IOException e) {
|
} catch (java.io.IOException e) {
|
||||||
Log.w("tildefriends", "IOException: " + e);
|
log("IOException: " + e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
binder.transact(TildeFriendsSandboxService.START_CALL, data, null, IBinder.FLAG_ONEWAY);
|
binder.transact(TildeFriendsSandboxService.START_CALL, data, null, IBinder.FLAG_ONEWAY);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.w("tildefriends", "RemoteException");
|
log("RemoteException");
|
||||||
} finally {
|
} finally {
|
||||||
data.recycle();
|
data.recycle();
|
||||||
}
|
}
|
||||||
@@ -423,14 +471,14 @@ public class TildeFriendsActivity extends Activity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onServiceDisconnected(ComponentName name) {
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
Log.w("tildefriends", "onServiceDisconnected");
|
log("onServiceDisconnected");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
s_activity.bindService(intent, s_activity.service_connection, BIND_AUTO_CREATE);
|
s_activity.bindService(intent, s_activity.service_connection, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void stop_sandbox() {
|
public static void stop_sandbox() {
|
||||||
Log.w("tildefriends", "stop_sandbox");
|
log("stop_sandbox");
|
||||||
if (s_activity.service_connection != null) {
|
if (s_activity.service_connection != null) {
|
||||||
s_activity.unbindService(s_activity.service_connection);
|
s_activity.unbindService(s_activity.service_connection);
|
||||||
s_activity.service_connection = null;
|
s_activity.service_connection = null;
|
||||||
@@ -442,14 +490,11 @@ public class TildeFriendsActivity extends Activity {
|
|||||||
if (port >= 0) {
|
if (port >= 0) {
|
||||||
base_url = "http://127.0.0.1:" + String.valueOf(port) + "/";
|
base_url = "http://127.0.0.1:" + String.valueOf(port) + "/";
|
||||||
runOnUiThread(() -> {
|
runOnUiThread(() -> {
|
||||||
hide_status();
|
ready = true;
|
||||||
web_view.loadUrl(base_url + "login/auto");
|
web_view.loadUrl(base_url + "login/auto");
|
||||||
});
|
});
|
||||||
|
observer.stopWatching();
|
||||||
observer = null;
|
observer = null;
|
||||||
} else {
|
|
||||||
runOnUiThread(() -> {
|
|
||||||
set_status("Waiting to connect...");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import android.os.Binder;
|
|||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
public class TildeFriendsSandboxService extends Service {
|
public class TildeFriendsSandboxService extends Service {
|
||||||
public static final int START_CALL = IBinder.FIRST_CALL_TRANSACTION;
|
public static final int START_CALL = IBinder.FIRST_CALL_TRANSACTION;
|
||||||
@@ -14,12 +13,12 @@ public class TildeFriendsSandboxService extends Service {
|
|||||||
Thread thread;
|
Thread thread;
|
||||||
|
|
||||||
public int onStartCommand(Intent intent, int flags, int start_id) {
|
public int onStartCommand(Intent intent, int flags, int start_id) {
|
||||||
Log.w("tildefriends", "TildeFriendsSandboxService: onStartCommand");
|
TildeFriendsActivity.log("TildeFriendsSandboxService: onStartCommand");
|
||||||
return super.onStartCommand(intent, flags, start_id);
|
return super.onStartCommand(intent, flags, start_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
Log.w("tildefriends", "TildeFriendsSandboxService: onDestroy");
|
TildeFriendsActivity.log("TildeFriendsSandboxService: onDestroy");
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,9 +26,9 @@ public class TildeFriendsSandboxService extends Service {
|
|||||||
thread = new Thread(new Runnable() {
|
thread = new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Log.w("tildefriends", "Calling tf_sandbox_main.");
|
TildeFriendsActivity.log("Calling tf_sandbox_main.");
|
||||||
int result = TildeFriendsActivity.tf_sandbox_main(pipe_fd);
|
int result = TildeFriendsActivity.tf_sandbox_main(pipe_fd);
|
||||||
Log.w("tildefriends", "tf_sandbox_main returned " + result + ".");
|
TildeFriendsActivity.log("tf_sandbox_main returned " + result + ".");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
thread.start();
|
thread.start();
|
||||||
@@ -43,7 +42,7 @@ public class TildeFriendsSandboxService extends Service {
|
|||||||
if (code == START_CALL) {
|
if (code == START_CALL) {
|
||||||
ParcelFileDescriptor pfd = read_pfd(data);
|
ParcelFileDescriptor pfd = read_pfd(data);
|
||||||
if (pfd != null) {
|
if (pfd != null) {
|
||||||
Log.w("tildefriends", "fd is " + pfd.getFd());
|
TildeFriendsActivity.log("fd is " + pfd.getFd());
|
||||||
start_thread(pfd.detachFd());
|
start_thread(pfd.detachFd());
|
||||||
try {
|
try {
|
||||||
pfd.close();
|
pfd.close();
|
||||||
|
|||||||
@@ -10,11 +10,6 @@
|
|||||||
android:id="@+id/web"
|
android:id="@+id/web"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"/>
|
android:layout_height="match_parent"/>
|
||||||
<TextView
|
|
||||||
android:id="@+id/text"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:gravity="center_horizontal|center_vertical"/>
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/refresh"
|
android:id="@+id/refresh"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
67
src/api.js.c
67
src/api.js.c
@@ -74,7 +74,7 @@ static void _tf_api_core_apps_after_work(tf_ssb_t* ssb, int status, void* user_d
|
|||||||
JSValue result = JS_NewObject(context);
|
JSValue result = JS_NewObject(context);
|
||||||
for (int i = 0; i < work->count; i++)
|
for (int i = 0; i < work->count; i++)
|
||||||
{
|
{
|
||||||
JS_SetPropertyStr(context, result, work->apps[i].app, JS_NewString(context, work->apps[i].path));
|
JS_SetPropertyStr(context, result, work->apps[i].app, JS_NewString(context, work->apps[i].path ? work->apps[i].path : ""));
|
||||||
}
|
}
|
||||||
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result);
|
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result);
|
||||||
tf_util_report_error(context, error);
|
tf_util_report_error(context, error);
|
||||||
@@ -147,12 +147,77 @@ static JSValue _tf_api_core_apps(JSContext* context, JSValueConst this_val, int
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_api_core_register(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
||||||
|
{
|
||||||
|
JSValue event_name = argv[0];
|
||||||
|
JSValue handler = argv[1];
|
||||||
|
JSValue process = data[0];
|
||||||
|
JSValue event_handlers = JS_GetPropertyStr(context, process, "eventHandlers");
|
||||||
|
JSAtom atom = JS_ValueToAtom(context, event_name);
|
||||||
|
JSValue array = JS_GetProperty(context, event_handlers, atom);
|
||||||
|
if (!JS_IsArray(context, array))
|
||||||
|
{
|
||||||
|
JS_FreeValue(context, array);
|
||||||
|
array = JS_NewArray(context);
|
||||||
|
JS_SetProperty(context, event_handlers, atom, JS_DupValue(context, array));
|
||||||
|
}
|
||||||
|
JS_SetPropertyUint32(context, array, tf_util_get_length(context, array), JS_DupValue(context, handler));
|
||||||
|
JS_FreeValue(context, array);
|
||||||
|
JS_FreeAtom(context, atom);
|
||||||
|
JS_FreeValue(context, event_handlers);
|
||||||
|
return JS_UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_api_core_unregister(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
||||||
|
{
|
||||||
|
JSValue event_name = argv[0];
|
||||||
|
JSValue handler = argv[1];
|
||||||
|
JSValue process = data[0];
|
||||||
|
JSValue event_handlers = JS_GetPropertyStr(context, process, "eventHandlers");
|
||||||
|
JSAtom atom = JS_ValueToAtom(context, event_name);
|
||||||
|
JSValue array = JS_GetProperty(context, event_handlers, atom);
|
||||||
|
|
||||||
|
if (JS_IsArray(context, array))
|
||||||
|
{
|
||||||
|
JSValue index_of = JS_GetPropertyStr(context, array, "indexOf");
|
||||||
|
|
||||||
|
JSValue index = JS_Call(context, index_of, array, 1, &handler);
|
||||||
|
int int_index = -1;
|
||||||
|
JS_ToInt32(context, &int_index, index);
|
||||||
|
if (int_index != -1)
|
||||||
|
{
|
||||||
|
JSValue splice = JS_GetPropertyStr(context, array, "splice");
|
||||||
|
JSValue splice_args[] = {
|
||||||
|
index,
|
||||||
|
JS_NewInt32(context, 1),
|
||||||
|
};
|
||||||
|
JSValue result = JS_Call(context, splice, array, 2, splice_args);
|
||||||
|
JS_FreeValue(context, result);
|
||||||
|
JS_FreeValue(context, splice);
|
||||||
|
}
|
||||||
|
JS_FreeValue(context, index);
|
||||||
|
JS_FreeValue(context, index_of);
|
||||||
|
|
||||||
|
if (tf_util_get_length(context, array) == 0)
|
||||||
|
{
|
||||||
|
JS_DeleteProperty(context, event_handlers, atom, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JS_FreeValue(context, array);
|
||||||
|
JS_FreeAtom(context, atom);
|
||||||
|
JS_FreeValue(context, event_handlers);
|
||||||
|
return JS_UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
static JSValue _tf_api_register_imports(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
static JSValue _tf_api_register_imports(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
{
|
{
|
||||||
JSValue imports = argv[0];
|
JSValue imports = argv[0];
|
||||||
JSValue process = argv[1];
|
JSValue process = argv[1];
|
||||||
JSValue core = JS_GetPropertyStr(context, imports, "core");
|
JSValue core = JS_GetPropertyStr(context, imports, "core");
|
||||||
JS_SetPropertyStr(context, core, "apps", JS_NewCFunctionData(context, _tf_api_core_apps, 1, 0, 1, &process));
|
JS_SetPropertyStr(context, core, "apps", JS_NewCFunctionData(context, _tf_api_core_apps, 1, 0, 1, &process));
|
||||||
|
JS_SetPropertyStr(context, core, "register", JS_NewCFunctionData(context, _tf_api_core_register, 2, 0, 1, &process));
|
||||||
|
JS_SetPropertyStr(context, core, "unregister", JS_NewCFunctionData(context, _tf_api_core_unregister, 2, 0, 1, &process));
|
||||||
JS_FreeValue(context, core);
|
JS_FreeValue(context, core);
|
||||||
return JS_UNDEFINED;
|
return JS_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
#include "bcrypt.js.h"
|
|
||||||
|
|
||||||
#include "task.h"
|
|
||||||
|
|
||||||
#include "ow-crypt.h"
|
|
||||||
#include "quickjs.h"
|
|
||||||
#include "uv.h"
|
|
||||||
|
|
||||||
static JSValue _crypt_hashpw(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
|
||||||
{
|
|
||||||
const char* key = JS_ToCString(context, argv[0]);
|
|
||||||
const char* salt = JS_ToCString(context, argv[1]);
|
|
||||||
char output[7 + 22 + 31 + 1];
|
|
||||||
char* hash = crypt_rn(key, salt, output, sizeof(output));
|
|
||||||
JSValue result = JS_NewString(context, hash);
|
|
||||||
JS_FreeCString(context, key);
|
|
||||||
JS_FreeCString(context, salt);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static JSValue _crypt_gensalt(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
|
||||||
{
|
|
||||||
int length = 0;
|
|
||||||
JS_ToInt32(context, &length, argv[0]);
|
|
||||||
char buffer[16];
|
|
||||||
tf_task_t* task = tf_task_get(context);
|
|
||||||
size_t bytes = uv_random(tf_task_get_loop(task), &(uv_random_t) { 0 }, buffer, sizeof(buffer), 0, NULL) == 0 ? sizeof(buffer) : 0;
|
|
||||||
char output[7 + 22 + 1];
|
|
||||||
char* salt = crypt_gensalt_rn("$2b$", length, buffer, bytes, output, sizeof(output));
|
|
||||||
JSValue result = JS_NewString(context, salt);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void tf_bcrypt_register(JSContext* context)
|
|
||||||
{
|
|
||||||
JSValue global = JS_GetGlobalObject(context);
|
|
||||||
JSValue bcrypt = JS_NewObject(context);
|
|
||||||
JS_SetPropertyStr(context, global, "bCrypt", bcrypt);
|
|
||||||
JS_SetPropertyStr(context, bcrypt, "hashpw", JS_NewCFunction(context, _crypt_hashpw, "hashpw", 2));
|
|
||||||
JS_SetPropertyStr(context, bcrypt, "gensalt", JS_NewCFunction(context, _crypt_gensalt, "gensalt", 1));
|
|
||||||
JS_FreeValue(context, global);
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
/**
|
|
||||||
** \defgroup bcrypt_js bCrypt
|
|
||||||
** Exposes bcrypt to script, where it is used for hashing and verifying
|
|
||||||
** passwords.
|
|
||||||
** @{
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** A JS context. */
|
|
||||||
typedef struct JSContext JSContext;
|
|
||||||
|
|
||||||
/**
|
|
||||||
** Register the bcrypt script interface.
|
|
||||||
** @param context The JS context.
|
|
||||||
*/
|
|
||||||
void tf_bcrypt_register(JSContext* context);
|
|
||||||
|
|
||||||
/** @} */
|
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "http.h"
|
#include "http.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
|
#include "sha1.h"
|
||||||
#include "ssb.db.h"
|
#include "ssb.db.h"
|
||||||
#include "task.h"
|
#include "task.h"
|
||||||
#include "tls.h"
|
#include "tls.h"
|
||||||
@@ -14,8 +15,6 @@
|
|||||||
#include "sodium/crypto_sign.h"
|
#include "sodium/crypto_sign.h"
|
||||||
#include "sodium/utils.h"
|
#include "sodium/utils.h"
|
||||||
|
|
||||||
#include <openssl/sha.h>
|
|
||||||
|
|
||||||
#define CYAN "\e[1;36m"
|
#define CYAN "\e[1;36m"
|
||||||
#define MAGENTA "\e[1;35m"
|
#define MAGENTA "\e[1;35m"
|
||||||
#define YELLOW "\e[1;33m"
|
#define YELLOW "\e[1;33m"
|
||||||
@@ -169,8 +168,13 @@ static JSValue _httpd_websocket_upgrade(JSContext* context, JSValueConst this_va
|
|||||||
uint8_t* key_magic = alloca(size);
|
uint8_t* key_magic = alloca(size);
|
||||||
memcpy(key_magic, header_sec_websocket_key, key_length);
|
memcpy(key_magic, header_sec_websocket_key, key_length);
|
||||||
memcpy(key_magic + key_length, k_magic, 36);
|
memcpy(key_magic + key_length, k_magic, 36);
|
||||||
|
|
||||||
uint8_t digest[20];
|
uint8_t digest[20];
|
||||||
SHA1(key_magic, size, digest);
|
SHA1_CTX sha1 = { 0 };
|
||||||
|
SHA1Init(&sha1);
|
||||||
|
SHA1Update(&sha1, key_magic, size);
|
||||||
|
SHA1Final(digest, &sha1);
|
||||||
|
|
||||||
char key[41] = { 0 };
|
char key[41] = { 0 };
|
||||||
tf_base64_encode(digest, sizeof(digest), key, sizeof(key));
|
tf_base64_encode(digest, sizeof(digest), key, sizeof(key));
|
||||||
|
|
||||||
|
|||||||
@@ -13,13 +13,13 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.0.33</string>
|
<string>0.2025.10</string>
|
||||||
<key>CFBundleSupportedPlatforms</key>
|
<key>CFBundleSupportedPlatforms</key>
|
||||||
<array>
|
<array>
|
||||||
<string>iPhoneOS</string>
|
<string>iPhoneOS</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>15</string>
|
<string>18</string>
|
||||||
<key>DTPlatformName</key>
|
<key>DTPlatformName</key>
|
||||||
<string>iphoneos</string>
|
<string>iphoneos</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
|||||||
BIN
src/ios/tildefriends512.png
Normal file
BIN
src/ios/tildefriends512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
18
src/main.c
18
src/main.c
@@ -1578,28 +1578,28 @@ static void _shed_privileges()
|
|||||||
if (setrlimit(RLIMIT_FSIZE, &zeroLimit) != 0)
|
if (setrlimit(RLIMIT_FSIZE, &zeroLimit) != 0)
|
||||||
{
|
{
|
||||||
perror("setrlimit(RLIMIT_FSIZE, {0, 0})");
|
perror("setrlimit(RLIMIT_FSIZE, {0, 0})");
|
||||||
exit(-1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
if (setrlimit(RLIMIT_NOFILE, &zeroLimit) != 0)
|
if (setrlimit(RLIMIT_NOFILE, &zeroLimit) != 0)
|
||||||
{
|
{
|
||||||
perror("setrlimit(RLIMIT_NOFILE, {0, 0})");
|
perror("setrlimit(RLIMIT_NOFILE, {0, 0})");
|
||||||
exit(-1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
if (setrlimit(RLIMIT_NPROC, &zeroLimit) != 0)
|
if (setrlimit(RLIMIT_NPROC, &zeroLimit) != 0)
|
||||||
{
|
{
|
||||||
perror("setrlimit(RLIMIT_NPROC, {0, 0})");
|
perror("setrlimit(RLIMIT_NPROC, {0, 0})");
|
||||||
exit(-1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
#if !defined(__MACH__) && !defined(__OpenBSD__)
|
#if !defined(__MACH__) && !defined(__OpenBSD__)
|
||||||
if (setrlimit(RLIMIT_LOCKS, &zeroLimit) != 0)
|
if (setrlimit(RLIMIT_LOCKS, &zeroLimit) != 0)
|
||||||
{
|
{
|
||||||
perror("setrlimit(RLIMIT_LOCKS, {0, 0})");
|
perror("setrlimit(RLIMIT_LOCKS, {0, 0})");
|
||||||
exit(-1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
if (setrlimit(RLIMIT_MSGQUEUE, &zeroLimit) != 0)
|
if (setrlimit(RLIMIT_MSGQUEUE, &zeroLimit) != 0)
|
||||||
{
|
{
|
||||||
perror("setrlimit(RLIMIT_MSGQUEUE, {0, 0})");
|
perror("setrlimit(RLIMIT_MSGQUEUE, {0, 0})");
|
||||||
exit(-1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@@ -1609,12 +1609,12 @@ static void _shed_privileges()
|
|||||||
if (unveil("/dev/null", "r") || unveil(NULL, NULL))
|
if (unveil("/dev/null", "r") || unveil(NULL, NULL))
|
||||||
{
|
{
|
||||||
perror("unveil");
|
perror("unveil");
|
||||||
exit(-1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
if (pledge("stdio unveil", NULL))
|
if (pledge("stdio unveil", NULL))
|
||||||
{
|
{
|
||||||
perror("pledge");
|
perror("pledge");
|
||||||
exit(-1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -1831,7 +1831,7 @@ static void _error_handler(int sig)
|
|||||||
const char* stack = tf_util_backtrace_string();
|
const char* stack = tf_util_backtrace_string();
|
||||||
tf_printf("ERROR:\n%s\n", stack);
|
tf_printf("ERROR:\n%s\n", stack);
|
||||||
tf_free((void*)stack);
|
tf_free((void*)stack);
|
||||||
_exit(1);
|
_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
@@ -1843,7 +1843,7 @@ static LONG WINAPI _win32_exception_handler(EXCEPTION_POINTERS* info)
|
|||||||
const char* stack = tf_util_backtrace_string();
|
const char* stack = tf_util_backtrace_string();
|
||||||
tf_printf("ERROR:\n%s\n", stack);
|
tf_printf("ERROR:\n%s\n", stack);
|
||||||
tf_free((void*)stack);
|
tf_free((void*)stack);
|
||||||
_exit(1);
|
_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
|
|||||||
329
src/sha1.c
Normal file
329
src/sha1.c
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
/*
|
||||||
|
* SHA1 hash implementation and interface functions
|
||||||
|
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sha1.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* ===== start - public domain SHA1 implementation ===== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
SHA-1 in C
|
||||||
|
By Steve Reid <sreid@sea-to-sky.net>
|
||||||
|
100% Public Domain
|
||||||
|
|
||||||
|
-----------------
|
||||||
|
Modified 7/98
|
||||||
|
By James H. Brown <jbrown@burgoyne.com>
|
||||||
|
Still 100% Public Domain
|
||||||
|
|
||||||
|
Corrected a problem which generated improper hash values on 16 bit machines
|
||||||
|
Routine SHA1Update changed from
|
||||||
|
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
|
||||||
|
len)
|
||||||
|
to
|
||||||
|
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
|
||||||
|
long len)
|
||||||
|
|
||||||
|
The 'len' parameter was declared an int which works fine on 32 bit machines.
|
||||||
|
However, on 16 bit machines an int is too small for the shifts being done
|
||||||
|
against it. This caused the hash function to generate incorrect values if len
|
||||||
|
was greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
|
||||||
|
|
||||||
|
Since the file IO in main() reads 16K at a time, any file 8K or larger would be
|
||||||
|
guaranteed to generate the wrong hash (e.g. Test Vector #3, a million "a"s).
|
||||||
|
|
||||||
|
I also changed the declaration of variables i & j in SHA1Update to unsigned
|
||||||
|
long from unsigned int for the same reason.
|
||||||
|
|
||||||
|
These changes should make no difference to any 32 bit implementations since an
|
||||||
|
int and a long are the same size in those environments.
|
||||||
|
|
||||||
|
--
|
||||||
|
I also corrected a few compiler warnings generated by Borland C.
|
||||||
|
1. Added #include <process.h> for exit() prototype
|
||||||
|
2. Removed unused variable 'j' in SHA1Final
|
||||||
|
3. Changed exit(0) to return(0) at end of main.
|
||||||
|
|
||||||
|
ALL changes I made can be located by searching for comments containing 'JHB'
|
||||||
|
-----------------
|
||||||
|
Modified 8/98
|
||||||
|
By Steve Reid <sreid@sea-to-sky.net>
|
||||||
|
Still 100% public domain
|
||||||
|
|
||||||
|
1- Removed #include <process.h> and used return() instead of exit()
|
||||||
|
2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
|
||||||
|
3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
|
||||||
|
|
||||||
|
-----------------
|
||||||
|
Modified 4/01
|
||||||
|
By Saul Kravitz <Saul.Kravitz@celera.com>
|
||||||
|
Still 100% PD
|
||||||
|
Modified to run on Compaq Alpha hardware.
|
||||||
|
|
||||||
|
-----------------
|
||||||
|
Modified 4/01
|
||||||
|
By Jouni Malinen <j@w1.fi>
|
||||||
|
Minor changes to match the coding style used in Dynamics.
|
||||||
|
|
||||||
|
Modified September 24, 2004
|
||||||
|
By Jouni Malinen <j@w1.fi>
|
||||||
|
Fixed alignment issue in SHA1Transform when SHA1HANDSOFF is defined.
|
||||||
|
|
||||||
|
-----------------
|
||||||
|
Modified September 29, 2025
|
||||||
|
By Cory McWilliams <cory@tildefriends.net>
|
||||||
|
Adapted from
|
||||||
|
https://web.mit.edu/freebsd/head/contrib/wpa/src/crypto/sha1-internal.c.
|
||||||
|
Modified to build outside of FreeBSD. Updated with clang-format.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Test Vectors (from FIPS PUB 180-1)
|
||||||
|
"abc"
|
||||||
|
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
||||||
|
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
||||||
|
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
||||||
|
A million repetitions of "a"
|
||||||
|
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define SHA1HANDSOFF
|
||||||
|
|
||||||
|
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||||
|
|
||||||
|
/* blk0() and blk() perform the initial expand. */
|
||||||
|
/* I got the idea of expanding during the round function from SSLeay */
|
||||||
|
#ifndef WORDS_BIGENDIAN
|
||||||
|
#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | (rol(block->l[i], 8) & 0x00FF00FF))
|
||||||
|
#else
|
||||||
|
#define blk0(i) block->l[i]
|
||||||
|
#endif
|
||||||
|
#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1))
|
||||||
|
|
||||||
|
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
|
||||||
|
#define R0(v, w, x, y, z, i) \
|
||||||
|
z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
|
||||||
|
w = rol(w, 30);
|
||||||
|
#define R1(v, w, x, y, z, i) \
|
||||||
|
z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
|
||||||
|
w = rol(w, 30);
|
||||||
|
#define R2(v, w, x, y, z, i) \
|
||||||
|
z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \
|
||||||
|
w = rol(w, 30);
|
||||||
|
#define R3(v, w, x, y, z, i) \
|
||||||
|
z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
|
||||||
|
w = rol(w, 30);
|
||||||
|
#define R4(v, w, x, y, z, i) \
|
||||||
|
z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
|
||||||
|
w = rol(w, 30);
|
||||||
|
|
||||||
|
#ifdef VERBOSE /* SAK */
|
||||||
|
void SHAPrintContext(SHA1_CTX* context, char* msg)
|
||||||
|
{
|
||||||
|
printf("%s (%d,%d) %x %x %x %x %x\n", msg, context->count[0], context->count[1], context->state[0], context->state[1], context->state[2], context->state[3], context->state[4]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Hash a single 512-bit block. This is the core of the algorithm. */
|
||||||
|
|
||||||
|
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
|
||||||
|
{
|
||||||
|
uint32_t a, b, c, d, e;
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
unsigned char c[64];
|
||||||
|
uint32_t l[16];
|
||||||
|
} CHAR64LONG16;
|
||||||
|
CHAR64LONG16* block;
|
||||||
|
#ifdef SHA1HANDSOFF
|
||||||
|
CHAR64LONG16 workspace;
|
||||||
|
block = &workspace;
|
||||||
|
memcpy(block, buffer, 64);
|
||||||
|
#else
|
||||||
|
block = (CHAR64LONG16*)buffer;
|
||||||
|
#endif
|
||||||
|
/* Copy context->state[] to working vars */
|
||||||
|
a = state[0];
|
||||||
|
b = state[1];
|
||||||
|
c = state[2];
|
||||||
|
d = state[3];
|
||||||
|
e = state[4];
|
||||||
|
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||||
|
R0(a, b, c, d, e, 0);
|
||||||
|
R0(e, a, b, c, d, 1);
|
||||||
|
R0(d, e, a, b, c, 2);
|
||||||
|
R0(c, d, e, a, b, 3);
|
||||||
|
R0(b, c, d, e, a, 4);
|
||||||
|
R0(a, b, c, d, e, 5);
|
||||||
|
R0(e, a, b, c, d, 6);
|
||||||
|
R0(d, e, a, b, c, 7);
|
||||||
|
R0(c, d, e, a, b, 8);
|
||||||
|
R0(b, c, d, e, a, 9);
|
||||||
|
R0(a, b, c, d, e, 10);
|
||||||
|
R0(e, a, b, c, d, 11);
|
||||||
|
R0(d, e, a, b, c, 12);
|
||||||
|
R0(c, d, e, a, b, 13);
|
||||||
|
R0(b, c, d, e, a, 14);
|
||||||
|
R0(a, b, c, d, e, 15);
|
||||||
|
R1(e, a, b, c, d, 16);
|
||||||
|
R1(d, e, a, b, c, 17);
|
||||||
|
R1(c, d, e, a, b, 18);
|
||||||
|
R1(b, c, d, e, a, 19);
|
||||||
|
R2(a, b, c, d, e, 20);
|
||||||
|
R2(e, a, b, c, d, 21);
|
||||||
|
R2(d, e, a, b, c, 22);
|
||||||
|
R2(c, d, e, a, b, 23);
|
||||||
|
R2(b, c, d, e, a, 24);
|
||||||
|
R2(a, b, c, d, e, 25);
|
||||||
|
R2(e, a, b, c, d, 26);
|
||||||
|
R2(d, e, a, b, c, 27);
|
||||||
|
R2(c, d, e, a, b, 28);
|
||||||
|
R2(b, c, d, e, a, 29);
|
||||||
|
R2(a, b, c, d, e, 30);
|
||||||
|
R2(e, a, b, c, d, 31);
|
||||||
|
R2(d, e, a, b, c, 32);
|
||||||
|
R2(c, d, e, a, b, 33);
|
||||||
|
R2(b, c, d, e, a, 34);
|
||||||
|
R2(a, b, c, d, e, 35);
|
||||||
|
R2(e, a, b, c, d, 36);
|
||||||
|
R2(d, e, a, b, c, 37);
|
||||||
|
R2(c, d, e, a, b, 38);
|
||||||
|
R2(b, c, d, e, a, 39);
|
||||||
|
R3(a, b, c, d, e, 40);
|
||||||
|
R3(e, a, b, c, d, 41);
|
||||||
|
R3(d, e, a, b, c, 42);
|
||||||
|
R3(c, d, e, a, b, 43);
|
||||||
|
R3(b, c, d, e, a, 44);
|
||||||
|
R3(a, b, c, d, e, 45);
|
||||||
|
R3(e, a, b, c, d, 46);
|
||||||
|
R3(d, e, a, b, c, 47);
|
||||||
|
R3(c, d, e, a, b, 48);
|
||||||
|
R3(b, c, d, e, a, 49);
|
||||||
|
R3(a, b, c, d, e, 50);
|
||||||
|
R3(e, a, b, c, d, 51);
|
||||||
|
R3(d, e, a, b, c, 52);
|
||||||
|
R3(c, d, e, a, b, 53);
|
||||||
|
R3(b, c, d, e, a, 54);
|
||||||
|
R3(a, b, c, d, e, 55);
|
||||||
|
R3(e, a, b, c, d, 56);
|
||||||
|
R3(d, e, a, b, c, 57);
|
||||||
|
R3(c, d, e, a, b, 58);
|
||||||
|
R3(b, c, d, e, a, 59);
|
||||||
|
R4(a, b, c, d, e, 60);
|
||||||
|
R4(e, a, b, c, d, 61);
|
||||||
|
R4(d, e, a, b, c, 62);
|
||||||
|
R4(c, d, e, a, b, 63);
|
||||||
|
R4(b, c, d, e, a, 64);
|
||||||
|
R4(a, b, c, d, e, 65);
|
||||||
|
R4(e, a, b, c, d, 66);
|
||||||
|
R4(d, e, a, b, c, 67);
|
||||||
|
R4(c, d, e, a, b, 68);
|
||||||
|
R4(b, c, d, e, a, 69);
|
||||||
|
R4(a, b, c, d, e, 70);
|
||||||
|
R4(e, a, b, c, d, 71);
|
||||||
|
R4(d, e, a, b, c, 72);
|
||||||
|
R4(c, d, e, a, b, 73);
|
||||||
|
R4(b, c, d, e, a, 74);
|
||||||
|
R4(a, b, c, d, e, 75);
|
||||||
|
R4(e, a, b, c, d, 76);
|
||||||
|
R4(d, e, a, b, c, 77);
|
||||||
|
R4(c, d, e, a, b, 78);
|
||||||
|
R4(b, c, d, e, a, 79);
|
||||||
|
/* Add the working vars back into context.state[] */
|
||||||
|
state[0] += a;
|
||||||
|
state[1] += b;
|
||||||
|
state[2] += c;
|
||||||
|
state[3] += d;
|
||||||
|
state[4] += e;
|
||||||
|
/* Wipe variables */
|
||||||
|
a = b = c = d = e = 0;
|
||||||
|
#ifdef SHA1HANDSOFF
|
||||||
|
memset(block, 0, 64);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SHA1Init - Initialize new context */
|
||||||
|
|
||||||
|
void SHA1Init(SHA1_CTX* context)
|
||||||
|
{
|
||||||
|
/* SHA1 initialization constants */
|
||||||
|
context->state[0] = 0x67452301;
|
||||||
|
context->state[1] = 0xEFCDAB89;
|
||||||
|
context->state[2] = 0x98BADCFE;
|
||||||
|
context->state[3] = 0x10325476;
|
||||||
|
context->state[4] = 0xC3D2E1F0;
|
||||||
|
context->count[0] = context->count[1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Run your data through this. */
|
||||||
|
|
||||||
|
void SHA1Update(SHA1_CTX* context, const void* _data, uint32_t len)
|
||||||
|
{
|
||||||
|
uint32_t i, j;
|
||||||
|
const unsigned char* data = _data;
|
||||||
|
|
||||||
|
#ifdef VERBOSE
|
||||||
|
SHAPrintContext(context, "before");
|
||||||
|
#endif
|
||||||
|
j = (context->count[0] >> 3) & 63;
|
||||||
|
if ((context->count[0] += len << 3) < (len << 3))
|
||||||
|
context->count[1]++;
|
||||||
|
context->count[1] += (len >> 29);
|
||||||
|
if ((j + len) > 63)
|
||||||
|
{
|
||||||
|
memcpy(&context->buffer[j], data, (i = 64 - j));
|
||||||
|
SHA1Transform(context->state, context->buffer);
|
||||||
|
for (; i + 63 < len; i += 64)
|
||||||
|
{
|
||||||
|
SHA1Transform(context->state, &data[i]);
|
||||||
|
}
|
||||||
|
j = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
i = 0;
|
||||||
|
memcpy(&context->buffer[j], &data[i], len - i);
|
||||||
|
#ifdef VERBOSE
|
||||||
|
SHAPrintContext(context, "after ");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add padding and return the message digest. */
|
||||||
|
|
||||||
|
void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
unsigned char finalcount[8];
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
/* Endian independent */
|
||||||
|
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255);
|
||||||
|
}
|
||||||
|
SHA1Update(context, (unsigned char*)"\200", 1);
|
||||||
|
while ((context->count[0] & 504) != 448)
|
||||||
|
{
|
||||||
|
SHA1Update(context, (unsigned char*)"\0", 1);
|
||||||
|
}
|
||||||
|
/* Should cause a SHA1Transform() */
|
||||||
|
SHA1Update(context, finalcount, 8);
|
||||||
|
for (i = 0; i < 20; i++)
|
||||||
|
{
|
||||||
|
digest[i] = (unsigned char)((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
|
||||||
|
}
|
||||||
|
/* Wipe variables */
|
||||||
|
i = 0;
|
||||||
|
memset(context->buffer, 0, 64);
|
||||||
|
memset(context->state, 0, 20);
|
||||||
|
memset(context->count, 0, 8);
|
||||||
|
memset(finalcount, 0, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== end - public domain SHA1 implementation ===== */
|
||||||
71
src/sha1.h
Normal file
71
src/sha1.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* SHA1 internal definitions
|
||||||
|
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
** \defgroup sha1 SHA1
|
||||||
|
** SHA1 API.
|
||||||
|
** Adapted from
|
||||||
|
** https://web.mit.edu/freebsd/head/contrib/wpa/src/crypto/sha1_i.h by Cory
|
||||||
|
** McWilliams 2025-09-28.
|
||||||
|
** @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SHA1_I_H
|
||||||
|
#define SHA1_I_H
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
** SHA1 context struct.
|
||||||
|
*/
|
||||||
|
struct SHA1Context
|
||||||
|
{
|
||||||
|
/** SHA1 state. */
|
||||||
|
uint32_t state[5];
|
||||||
|
/** SHA1 count. */
|
||||||
|
uint32_t count[2];
|
||||||
|
/** SHA1 buffer. */
|
||||||
|
unsigned char buffer[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
** SHA1 context.
|
||||||
|
*/
|
||||||
|
typedef struct SHA1Context SHA1_CTX;
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Initialize a SHA1 context.
|
||||||
|
** @param context The context.
|
||||||
|
*/
|
||||||
|
void SHA1Init(struct SHA1Context* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Calculate an ongoing hash for a block of data.
|
||||||
|
** @param context The SHA1 context.
|
||||||
|
** @param data The data to hash.
|
||||||
|
** @param len The length of data.
|
||||||
|
*/
|
||||||
|
void SHA1Update(struct SHA1Context* context, const void* data, uint32_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Calculate the final hash digest.
|
||||||
|
** @param digest Populated with the digest.
|
||||||
|
** @param context The SHA1 context.
|
||||||
|
*/
|
||||||
|
void SHA1Final(unsigned char digest[20], struct SHA1Context* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Perform a SHA1 transformation.
|
||||||
|
** @param state The SHA1 state.
|
||||||
|
** @param buffer The data.
|
||||||
|
*/
|
||||||
|
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
|
||||||
|
|
||||||
|
#endif /* SHA1_I_H */
|
||||||
|
|
||||||
|
/** @} */
|
||||||
1165
src/socket.js.c
1165
src/socket.js.c
File diff suppressed because it is too large
Load Diff
@@ -1,30 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
/**
|
|
||||||
** \defgroup socket_js Socket Interface
|
|
||||||
** Exposes network sockets to script.
|
|
||||||
** @{
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "quickjs.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
** Register the socket script interface.
|
|
||||||
** @param context The JS context.
|
|
||||||
** @return The Socket constructor.
|
|
||||||
*/
|
|
||||||
JSValue tf_socket_register(JSContext* context);
|
|
||||||
|
|
||||||
/**
|
|
||||||
** Get the number of active socket objects.
|
|
||||||
** @return The count.
|
|
||||||
*/
|
|
||||||
int tf_socket_get_count();
|
|
||||||
|
|
||||||
/**
|
|
||||||
** Get the number of connected socket objects.
|
|
||||||
** @return the count.
|
|
||||||
*/
|
|
||||||
int tf_socket_get_open_count();
|
|
||||||
|
|
||||||
/** @} */
|
|
||||||
87
src/ssb.c
87
src/ssb.c
@@ -133,6 +133,15 @@ typedef struct _tf_ssb_message_added_callback_node_t
|
|||||||
tf_ssb_message_added_callback_node_t* next;
|
tf_ssb_message_added_callback_node_t* next;
|
||||||
} tf_ssb_message_added_callback_node_t;
|
} tf_ssb_message_added_callback_node_t;
|
||||||
|
|
||||||
|
typedef struct _tf_ssb_blob_stored_callback_node_t tf_ssb_blob_stored_callback_node_t;
|
||||||
|
typedef struct _tf_ssb_blob_stored_callback_node_t
|
||||||
|
{
|
||||||
|
tf_ssb_blob_stored_callback_t* callback;
|
||||||
|
tf_ssb_callback_cleanup_t* cleanup;
|
||||||
|
void* user_data;
|
||||||
|
tf_ssb_blob_stored_callback_node_t* next;
|
||||||
|
} tf_ssb_blob_stored_callback_node_t;
|
||||||
|
|
||||||
typedef struct _tf_ssb_blob_want_added_callback_node_t tf_ssb_blob_want_added_callback_node_t;
|
typedef struct _tf_ssb_blob_want_added_callback_node_t tf_ssb_blob_want_added_callback_node_t;
|
||||||
typedef struct _tf_ssb_blob_want_added_callback_node_t
|
typedef struct _tf_ssb_blob_want_added_callback_node_t
|
||||||
{
|
{
|
||||||
@@ -235,6 +244,9 @@ typedef struct _tf_ssb_t
|
|||||||
tf_ssb_message_added_callback_node_t* message_added;
|
tf_ssb_message_added_callback_node_t* message_added;
|
||||||
int message_added_count;
|
int message_added_count;
|
||||||
|
|
||||||
|
tf_ssb_blob_stored_callback_node_t* blob_stored;
|
||||||
|
int blob_stored_count;
|
||||||
|
|
||||||
tf_ssb_blob_want_added_callback_node_t* blob_want_added;
|
tf_ssb_blob_want_added_callback_node_t* blob_want_added;
|
||||||
int blob_want_added_count;
|
int blob_want_added_count;
|
||||||
|
|
||||||
@@ -2741,6 +2753,17 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
|
|||||||
}
|
}
|
||||||
tf_free(node);
|
tf_free(node);
|
||||||
}
|
}
|
||||||
|
while (ssb->blob_stored)
|
||||||
|
{
|
||||||
|
tf_ssb_blob_stored_callback_node_t* node = ssb->blob_stored;
|
||||||
|
ssb->blob_stored = node->next;
|
||||||
|
ssb->blob_stored_count--;
|
||||||
|
if (node->cleanup)
|
||||||
|
{
|
||||||
|
node->cleanup(ssb, node->user_data);
|
||||||
|
}
|
||||||
|
tf_free(node);
|
||||||
|
}
|
||||||
while (ssb->blob_want_added)
|
while (ssb->blob_want_added)
|
||||||
{
|
{
|
||||||
tf_ssb_blob_want_added_callback_node_t* node = ssb->blob_want_added;
|
tf_ssb_blob_want_added_callback_node_t* node = ssb->blob_want_added;
|
||||||
@@ -2783,6 +2806,16 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
|
|||||||
|
|
||||||
uv_run(ssb->loop, UV_RUN_NOWAIT);
|
uv_run(ssb->loop, UV_RUN_NOWAIT);
|
||||||
|
|
||||||
|
if (ssb->own_context)
|
||||||
|
{
|
||||||
|
if (!ssb->quiet)
|
||||||
|
{
|
||||||
|
tf_printf("closing ssb context\n");
|
||||||
|
}
|
||||||
|
JS_FreeContext(ssb->context);
|
||||||
|
JS_FreeRuntime(ssb->runtime);
|
||||||
|
ssb->own_context = false;
|
||||||
|
}
|
||||||
if (ssb->loop == &ssb->own_loop)
|
if (ssb->loop == &ssb->own_loop)
|
||||||
{
|
{
|
||||||
if (!ssb->quiet)
|
if (!ssb->quiet)
|
||||||
@@ -2799,16 +2832,6 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
|
|||||||
{
|
{
|
||||||
tf_printf("uv loop closed.\n");
|
tf_printf("uv loop closed.\n");
|
||||||
}
|
}
|
||||||
if (ssb->own_context)
|
|
||||||
{
|
|
||||||
if (!ssb->quiet)
|
|
||||||
{
|
|
||||||
tf_printf("closing ssb context\n");
|
|
||||||
}
|
|
||||||
JS_FreeContext(ssb->context);
|
|
||||||
JS_FreeRuntime(ssb->runtime);
|
|
||||||
ssb->own_context = false;
|
|
||||||
}
|
|
||||||
while (ssb->broadcasts)
|
while (ssb->broadcasts)
|
||||||
{
|
{
|
||||||
tf_ssb_broadcast_t* broadcast = ssb->broadcasts;
|
tf_ssb_broadcast_t* broadcast = ssb->broadcasts;
|
||||||
@@ -3960,9 +3983,53 @@ void tf_ssb_remove_message_added_callback(tf_ssb_t* ssb, tf_ssb_message_added_ca
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tf_ssb_add_blob_stored_callback(tf_ssb_t* ssb, tf_ssb_blob_stored_callback_t* callback, void (*cleanup)(tf_ssb_t* ssb, void* user_data), void* user_data)
|
||||||
|
{
|
||||||
|
tf_ssb_blob_stored_callback_node_t* node = tf_malloc(sizeof(tf_ssb_blob_stored_callback_node_t));
|
||||||
|
*node = (tf_ssb_blob_stored_callback_node_t) {
|
||||||
|
.callback = callback,
|
||||||
|
.cleanup = cleanup,
|
||||||
|
.user_data = user_data,
|
||||||
|
.next = ssb->blob_stored,
|
||||||
|
};
|
||||||
|
ssb->blob_stored = node;
|
||||||
|
ssb->blob_stored_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tf_ssb_remove_blob_stored_callback(tf_ssb_t* ssb, tf_ssb_blob_stored_callback_t* callback, void* user_data)
|
||||||
|
{
|
||||||
|
tf_ssb_blob_stored_callback_node_t** it = &ssb->blob_stored;
|
||||||
|
while (*it)
|
||||||
|
{
|
||||||
|
if ((*it)->callback == callback && (*it)->user_data == user_data)
|
||||||
|
{
|
||||||
|
tf_ssb_blob_stored_callback_node_t* node = *it;
|
||||||
|
*it = node->next;
|
||||||
|
ssb->blob_stored_count--;
|
||||||
|
if (node->cleanup)
|
||||||
|
{
|
||||||
|
node->cleanup(ssb, node->user_data);
|
||||||
|
}
|
||||||
|
tf_free(node);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
it = &(*it)->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void tf_ssb_notify_blob_stored(tf_ssb_t* ssb, const char* id)
|
void tf_ssb_notify_blob_stored(tf_ssb_t* ssb, const char* id)
|
||||||
{
|
{
|
||||||
|
tf_ssb_blob_stored_callback_node_t* next = NULL;
|
||||||
ssb->blobs_stored++;
|
ssb->blobs_stored++;
|
||||||
|
for (tf_ssb_blob_stored_callback_node_t* node = ssb->blob_stored; node; node = next)
|
||||||
|
{
|
||||||
|
next = node->next;
|
||||||
|
tf_trace_begin(ssb->trace, "blob stored callback");
|
||||||
|
node->callback(ssb, id, node->user_data);
|
||||||
|
tf_trace_end(ssb->trace);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* author, int32_t sequence, const char* id, JSValue message_keys)
|
void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* author, int32_t sequence, const char* id, JSValue message_keys)
|
||||||
|
|||||||
24
src/ssb.db.c
24
src/ssb.db.c
@@ -421,6 +421,7 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
_tf_ssb_db_exec(db, "COMMIT TRANSACTION");
|
_tf_ssb_db_exec(db, "COMMIT TRANSACTION");
|
||||||
tf_printf("Done.\n");
|
tf_printf("Done.\n");
|
||||||
}
|
}
|
||||||
|
_tf_ssb_db_exec(db, "DELETE FROM blob_wants_cache WHERE blob_wants_cache.id IN (SELECT blobs.id FROM blobs)");
|
||||||
if (!_tf_ssb_db_has_rows(db, "SELECT * FROM sqlite_schema WHERE type = 'index' AND name = 'blob_wants_cache_source_id_unique_index'"))
|
if (!_tf_ssb_db_has_rows(db, "SELECT * FROM sqlite_schema WHERE type = 'index' AND name = 'blob_wants_cache_source_id_unique_index'"))
|
||||||
{
|
{
|
||||||
tf_printf("Creating blob_wants_cache UNIQUE constraint.\n");
|
tf_printf("Creating blob_wants_cache UNIQUE constraint.\n");
|
||||||
@@ -436,8 +437,11 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
_tf_ssb_db_exec(db,
|
_tf_ssb_db_exec(db,
|
||||||
"CREATE TRIGGER IF NOT EXISTS messages_ai_blob_wants_cache AFTER INSERT ON messages_refs BEGIN "
|
"CREATE TRIGGER IF NOT EXISTS messages_ai_blob_wants_cache AFTER INSERT ON messages_refs BEGIN "
|
||||||
"INSERT INTO blob_wants_cache (source, id, timestamp) "
|
"INSERT INTO blob_wants_cache (source, id, timestamp) "
|
||||||
"SELECT messages.id, new.ref, messages.timestamp FROM messages WHERE messages.id = new.message AND "
|
"SELECT messages.id, new.ref, messages.timestamp FROM messages "
|
||||||
"LENGTH(new.ref) = 52 AND new.ref LIKE '&%.sha256' "
|
"JOIN blobs ON new.ref = blobs.id "
|
||||||
|
"WHERE messages.id = new.message AND "
|
||||||
|
"LENGTH(new.ref) = 52 AND new.ref LIKE '&%.sha256' AND "
|
||||||
|
"blobs.content IS NULL "
|
||||||
"ON CONFLICT (source, id) DO NOTHING; END");
|
"ON CONFLICT (source, id) DO NOTHING; END");
|
||||||
_tf_ssb_db_exec(db, "DROP TRIGGER IF EXISTS blobs_refs_ai_blob_wants_cache");
|
_tf_ssb_db_exec(db, "DROP TRIGGER IF EXISTS blobs_refs_ai_blob_wants_cache");
|
||||||
_tf_ssb_db_exec(db,
|
_tf_ssb_db_exec(db,
|
||||||
@@ -445,6 +449,8 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
"INSERT INTO blob_wants_cache (source, id, timestamp) "
|
"INSERT INTO blob_wants_cache (source, id, timestamp) "
|
||||||
"SELECT messages.id, new.ref, messages.timestamp FROM messages "
|
"SELECT messages.id, new.ref, messages.timestamp FROM messages "
|
||||||
"JOIN blob_wants_cache bwc ON bwc.source = messages.id AND bwc.id = new.blob "
|
"JOIN blob_wants_cache bwc ON bwc.source = messages.id AND bwc.id = new.blob "
|
||||||
|
"JOIN blobs ON bwc.id = blobs.id "
|
||||||
|
"WHERE blobs.content IS NULL "
|
||||||
"ON CONFLICT (source, id) DO NOTHING; END");
|
"ON CONFLICT (source, id) DO NOTHING; END");
|
||||||
_tf_ssb_db_exec(db,
|
_tf_ssb_db_exec(db,
|
||||||
"CREATE TRIGGER IF NOT EXISTS messages_ad_blob_wants_cache AFTER DELETE ON messages BEGIN "
|
"CREATE TRIGGER IF NOT EXISTS messages_ad_blob_wants_cache AFTER DELETE ON messages BEGIN "
|
||||||
@@ -574,16 +580,15 @@ static int64_t _tf_ssb_db_store_message_raw(sqlite3* db, const char* id, const c
|
|||||||
return last_row_id;
|
return last_row_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char* _tf_ssb_db_get_message_blob_wants(tf_ssb_t* ssb, int64_t rowid)
|
static char* _tf_ssb_db_get_message_blob_wants(sqlite3* db, int64_t rowid)
|
||||||
{
|
{
|
||||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
|
||||||
sqlite3_stmt* statement;
|
sqlite3_stmt* statement;
|
||||||
char* result = NULL;
|
char* result = NULL;
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
|
|
||||||
if (sqlite3_prepare_v2(db,
|
if (sqlite3_prepare_v2(db,
|
||||||
"SELECT DISTINCT json.value FROM messages, json_tree(messages.content) AS json LEFT OUTER JOIN blobs ON json.value = blobs.id WHERE messages.rowid = ?1 AND "
|
"SELECT DISTINCT json.value FROM messages, json_tree(messages.content) AS json LEFT OUTER JOIN blobs ON json.value = blobs.id WHERE messages.rowid = ?1 AND "
|
||||||
"json.value LIKE '&%.sha256' AND length(json.value) = ?2 AND blobs.content IS NULL",
|
"length(json.value) = ?2 AND json.value LIKE '&%.sha256' AND blobs.content IS NULL",
|
||||||
-1, &statement, NULL) == SQLITE_OK)
|
-1, &statement, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
if (sqlite3_bind_int64(statement, 1, rowid) == SQLITE_OK && sqlite3_bind_int(statement, 2, k_blob_id_len - 1) == SQLITE_OK)
|
if (sqlite3_bind_int64(statement, 1, rowid) == SQLITE_OK && sqlite3_bind_int(statement, 2, k_blob_id_len - 1) == SQLITE_OK)
|
||||||
@@ -615,7 +620,6 @@ static char* _tf_ssb_db_get_message_blob_wants(tf_ssb_t* ssb, int64_t rowid)
|
|||||||
result = tf_realloc(result, size + 1);
|
result = tf_realloc(result, size + 1);
|
||||||
result[size] = '\0';
|
result[size] = '\0';
|
||||||
|
|
||||||
tf_ssb_release_db_reader(ssb, db);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -653,7 +657,7 @@ static void _tf_ssb_db_store_message_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
if (last_row_id != -1)
|
if (last_row_id != -1)
|
||||||
{
|
{
|
||||||
store->out_stored = true;
|
store->out_stored = true;
|
||||||
store->out_blob_wants = _tf_ssb_db_get_message_blob_wants(ssb, last_row_id);
|
store->out_blob_wants = _tf_ssb_db_get_message_blob_wants(db, last_row_id);
|
||||||
}
|
}
|
||||||
store = store->next;
|
store = store->next;
|
||||||
}
|
}
|
||||||
@@ -903,10 +907,6 @@ void tf_ssb_db_add_blob_wants(sqlite3* db, const char* id)
|
|||||||
{
|
{
|
||||||
tf_printf("blob wants cache update failed: %s.\n", sqlite3_errmsg(db));
|
tf_printf("blob wants cache update failed: %s.\n", sqlite3_errmsg(db));
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
tf_printf("want: %s\n", id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sqlite3_finalize(statement);
|
sqlite3_finalize(statement);
|
||||||
}
|
}
|
||||||
@@ -976,7 +976,7 @@ static void _tf_ssb_db_blob_store_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
static void _tf_ssb_db_blob_store_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
static void _tf_ssb_db_blob_store_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
{
|
{
|
||||||
blob_store_work_t* blob_work = user_data;
|
blob_store_work_t* blob_work = user_data;
|
||||||
if (status == 0 && *blob_work->id)
|
if (status == 0 && *blob_work->id && blob_work->is_new)
|
||||||
{
|
{
|
||||||
tf_ssb_notify_blob_stored(ssb, blob_work->id);
|
tf_ssb_notify_blob_stored(ssb, blob_work->id);
|
||||||
}
|
}
|
||||||
|
|||||||
25
src/ssb.h
25
src/ssb.h
@@ -680,6 +680,31 @@ void tf_ssb_remove_message_added_callback(tf_ssb_t* ssb, tf_ssb_message_added_ca
|
|||||||
*/
|
*/
|
||||||
void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* author, int32_t sequence, const char* id, JSValue message_with_keys);
|
void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* author, int32_t sequence, const char* id, JSValue message_with_keys);
|
||||||
|
|
||||||
|
/**
|
||||||
|
** A callback called when a blob is added to the database.
|
||||||
|
** @param ssb The SSB instance.
|
||||||
|
** @param id The blob identifier.
|
||||||
|
** @param user_data The user data.
|
||||||
|
*/
|
||||||
|
typedef void(tf_ssb_blob_stored_callback_t)(tf_ssb_t* ssb, const char* id, void* user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Register a callback called when a blob is added to the database.
|
||||||
|
** @param ssb The SSB instance.
|
||||||
|
** @param callback The callback function.
|
||||||
|
** @param cleanup A function to call when the callback is removed.
|
||||||
|
** @param user_data User data to pass to the callback.
|
||||||
|
*/
|
||||||
|
void tf_ssb_add_blob_stored_callback(tf_ssb_t* ssb, tf_ssb_blob_stored_callback_t* callback, tf_ssb_callback_cleanup_t* cleanup, void* user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Remove a callback registered for when a blob is added to the database.
|
||||||
|
** @param ssb The SSB instance.
|
||||||
|
** @param callback The callback function.
|
||||||
|
** @param user_data User data registered with the callback.
|
||||||
|
*/
|
||||||
|
void tf_ssb_remove_blob_stored_callback(tf_ssb_t* ssb, tf_ssb_blob_stored_callback_t* callback, void* user_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Record that a new blob was stored.
|
** Record that a new blob was stored.
|
||||||
** @param ssb The SSB instance.
|
** @param ssb The SSB instance.
|
||||||
|
|||||||
34
src/ssb.js.c
34
src/ssb.js.c
@@ -1627,6 +1627,20 @@ static void _tf_ssb_on_message_added_callback(tf_ssb_t* ssb, const char* author,
|
|||||||
JS_FreeValue(context, string);
|
JS_FreeValue(context, string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_on_blob_stored_callback(tf_ssb_t* ssb, const char* id, void* user_data)
|
||||||
|
{
|
||||||
|
JSContext* context = tf_ssb_get_context(ssb);
|
||||||
|
JSValue callback = JS_MKPTR(JS_TAG_OBJECT, user_data);
|
||||||
|
JSValue string = JS_NewString(context, id);
|
||||||
|
JSValue response = JS_Call(context, callback, JS_UNDEFINED, 1, &string);
|
||||||
|
if (tf_util_report_error(context, response))
|
||||||
|
{
|
||||||
|
tf_ssb_remove_blob_stored_callback(ssb, _tf_ssb_on_blob_stored_callback, user_data);
|
||||||
|
}
|
||||||
|
JS_FreeValue(context, response);
|
||||||
|
JS_FreeValue(context, string);
|
||||||
|
}
|
||||||
|
|
||||||
static void _tf_ssb_on_blob_want_added_callback(tf_ssb_t* ssb, const char* id, void* user_data)
|
static void _tf_ssb_on_blob_want_added_callback(tf_ssb_t* ssb, const char* id, void* user_data)
|
||||||
{
|
{
|
||||||
JSContext* context = tf_ssb_get_context(ssb);
|
JSContext* context = tf_ssb_get_context(ssb);
|
||||||
@@ -1747,6 +1761,11 @@ static JSValue _tf_ssb_add_event_listener(JSContext* context, JSValueConst this_
|
|||||||
void* ptr = JS_VALUE_GET_PTR(JS_DupValue(context, callback));
|
void* ptr = JS_VALUE_GET_PTR(JS_DupValue(context, callback));
|
||||||
tf_ssb_add_message_added_callback(ssb, _tf_ssb_on_message_added_callback, _tf_ssb_cleanup_value, ptr);
|
tf_ssb_add_message_added_callback(ssb, _tf_ssb_on_message_added_callback, _tf_ssb_cleanup_value, ptr);
|
||||||
}
|
}
|
||||||
|
else if (strcmp(event_name, "blob") == 0)
|
||||||
|
{
|
||||||
|
void* ptr = JS_VALUE_GET_PTR(JS_DupValue(context, callback));
|
||||||
|
tf_ssb_add_blob_stored_callback(ssb, _tf_ssb_on_blob_stored_callback, _tf_ssb_cleanup_value, ptr);
|
||||||
|
}
|
||||||
else if (strcmp(event_name, "blob_want_added") == 0)
|
else if (strcmp(event_name, "blob_want_added") == 0)
|
||||||
{
|
{
|
||||||
void* ptr = JS_VALUE_GET_PTR(JS_DupValue(context, callback));
|
void* ptr = JS_VALUE_GET_PTR(JS_DupValue(context, callback));
|
||||||
@@ -1790,6 +1809,11 @@ static JSValue _tf_ssb_remove_event_listener(JSContext* context, JSValueConst th
|
|||||||
void* ptr = JS_VALUE_GET_PTR(JS_DupValue(context, callback));
|
void* ptr = JS_VALUE_GET_PTR(JS_DupValue(context, callback));
|
||||||
tf_ssb_remove_message_added_callback(ssb, _tf_ssb_on_message_added_callback, ptr);
|
tf_ssb_remove_message_added_callback(ssb, _tf_ssb_on_message_added_callback, ptr);
|
||||||
}
|
}
|
||||||
|
else if (strcmp(event_name, "blob") == 0)
|
||||||
|
{
|
||||||
|
void* ptr = JS_VALUE_GET_PTR(JS_DupValue(context, callback));
|
||||||
|
tf_ssb_remove_blob_stored_callback(ssb, _tf_ssb_on_blob_stored_callback, ptr);
|
||||||
|
}
|
||||||
else if (strcmp(event_name, "blob_want_added") == 0)
|
else if (strcmp(event_name, "blob_want_added") == 0)
|
||||||
{
|
{
|
||||||
void* ptr = JS_VALUE_GET_PTR(JS_DupValue(context, callback));
|
void* ptr = JS_VALUE_GET_PTR(JS_DupValue(context, callback));
|
||||||
@@ -2346,6 +2370,10 @@ void tf_ssb_register(JSContext* context, tf_ssb_t* ssb)
|
|||||||
JS_SetPropertyStr(context, global, "ssb", object);
|
JS_SetPropertyStr(context, global, "ssb", object);
|
||||||
JS_SetOpaque(object, ssb);
|
JS_SetOpaque(object, ssb);
|
||||||
|
|
||||||
|
JSValue object_internal = JS_NewObjectClass(context, _tf_ssb_classId);
|
||||||
|
JS_SetPropertyStr(context, global, "ssb_internal", object_internal);
|
||||||
|
JS_SetOpaque(object_internal, ssb);
|
||||||
|
|
||||||
/* Requires an identity. */
|
/* Requires an identity. */
|
||||||
JS_SetPropertyStr(context, object, "createIdentity", JS_NewCFunction(context, _tf_ssb_createIdentity, "createIdentity", 1));
|
JS_SetPropertyStr(context, object, "createIdentity", JS_NewCFunction(context, _tf_ssb_createIdentity, "createIdentity", 1));
|
||||||
JS_SetPropertyStr(context, object, "addIdentity", JS_NewCFunction(context, _tf_ssb_addIdentity, "addIdentity", 2));
|
JS_SetPropertyStr(context, object, "addIdentity", JS_NewCFunction(context, _tf_ssb_addIdentity, "addIdentity", 2));
|
||||||
@@ -2363,7 +2391,6 @@ void tf_ssb_register(JSContext* context, tf_ssb_t* ssb)
|
|||||||
JS_SetPropertyStr(context, object, "getServerIdentity", JS_NewCFunction(context, _tf_ssb_getServerIdentity, "getServerIdentity", 0));
|
JS_SetPropertyStr(context, object, "getServerIdentity", JS_NewCFunction(context, _tf_ssb_getServerIdentity, "getServerIdentity", 0));
|
||||||
JS_SetPropertyStr(context, object, "getAllIdentities", JS_NewCFunction(context, _tf_ssb_getAllIdentities, "getAllIdentities", 0));
|
JS_SetPropertyStr(context, object, "getAllIdentities", JS_NewCFunction(context, _tf_ssb_getAllIdentities, "getAllIdentities", 0));
|
||||||
JS_SetPropertyStr(context, object, "getActiveIdentity", JS_NewCFunction(context, _tf_ssb_getActiveIdentity, "getActiveIdentity", 3));
|
JS_SetPropertyStr(context, object, "getActiveIdentity", JS_NewCFunction(context, _tf_ssb_getActiveIdentity, "getActiveIdentity", 3));
|
||||||
JS_SetPropertyStr(context, object, "getIdentityInfo", JS_NewCFunction(context, _tf_ssb_getIdentityInfo, "getIdentityInfo", 3));
|
|
||||||
JS_SetPropertyStr(context, object, "blobGet", JS_NewCFunction(context, _tf_ssb_blobGet, "blobGet", 1));
|
JS_SetPropertyStr(context, object, "blobGet", JS_NewCFunction(context, _tf_ssb_blobGet, "blobGet", 1));
|
||||||
JS_SetPropertyStr(context, object, "connections", JS_NewCFunction(context, _tf_ssb_connections, "connections", 0));
|
JS_SetPropertyStr(context, object, "connections", JS_NewCFunction(context, _tf_ssb_connections, "connections", 0));
|
||||||
JS_SetPropertyStr(context, object, "storedConnections", JS_NewCFunction(context, _tf_ssb_storedConnections, "storedConnections", 0));
|
JS_SetPropertyStr(context, object, "storedConnections", JS_NewCFunction(context, _tf_ssb_storedConnections, "storedConnections", 0));
|
||||||
@@ -2382,8 +2409,9 @@ void tf_ssb_register(JSContext* context, tf_ssb_t* ssb)
|
|||||||
JS_SetPropertyStr(context, object, "blobStore", JS_NewCFunction(context, _tf_ssb_blobStore, "blobStore", 1));
|
JS_SetPropertyStr(context, object, "blobStore", JS_NewCFunction(context, _tf_ssb_blobStore, "blobStore", 1));
|
||||||
|
|
||||||
/* Trusted only. */
|
/* Trusted only. */
|
||||||
JS_SetPropertyStr(context, object, "addEventListener", JS_NewCFunction(context, _tf_ssb_add_event_listener, "addEventListener", 2));
|
JS_SetPropertyStr(context, object_internal, "getIdentityInfo", JS_NewCFunction(context, _tf_ssb_getIdentityInfo, "getIdentityInfo", 3));
|
||||||
JS_SetPropertyStr(context, object, "removeEventListener", JS_NewCFunction(context, _tf_ssb_remove_event_listener, "removeEventListener", 2));
|
JS_SetPropertyStr(context, object_internal, "addEventListener", JS_NewCFunction(context, _tf_ssb_add_event_listener, "addEventListener", 2));
|
||||||
|
JS_SetPropertyStr(context, object_internal, "removeEventListener", JS_NewCFunction(context, _tf_ssb_remove_event_listener, "removeEventListener", 2));
|
||||||
|
|
||||||
JS_FreeValue(context, global);
|
JS_FreeValue(context, global);
|
||||||
}
|
}
|
||||||
|
|||||||
200
src/ssb.tests.c
200
src/ssb.tests.c
@@ -152,6 +152,12 @@ static void _wait_stored(tf_ssb_t* ssb, bool* stored)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _blob_stored(tf_ssb_t* ssb, const char* id, void* user_data)
|
||||||
|
{
|
||||||
|
tf_printf("blob stored %s\n", id);
|
||||||
|
*(bool*)user_data = true;
|
||||||
|
}
|
||||||
|
|
||||||
void tf_ssb_test_ssb(const tf_test_options_t* options)
|
void tf_ssb_test_ssb(const tf_test_options_t* options)
|
||||||
{
|
{
|
||||||
tf_printf("Testing SSB.\n");
|
tf_printf("Testing SSB.\n");
|
||||||
@@ -224,8 +230,13 @@ void tf_ssb_test_ssb(const tf_test_options_t* options)
|
|||||||
|
|
||||||
char blob_id[k_id_base64_len] = { 0 };
|
char blob_id[k_id_base64_len] = { 0 };
|
||||||
const char* k_blob = "Hello, blob!";
|
const char* k_blob = "Hello, blob!";
|
||||||
|
bool blob_stored = false;
|
||||||
|
tf_ssb_add_blob_stored_callback(ssb0, _blob_stored, NULL, &blob_stored);
|
||||||
b = tf_ssb_db_blob_store(ssb0, (const uint8_t*)k_blob, strlen(k_blob), blob_id, sizeof(blob_id), NULL);
|
b = tf_ssb_db_blob_store(ssb0, (const uint8_t*)k_blob, strlen(k_blob), blob_id, sizeof(blob_id), NULL);
|
||||||
|
tf_ssb_notify_blob_stored(ssb0, blob_id);
|
||||||
|
tf_ssb_remove_blob_stored_callback(ssb0, _blob_stored, &blob_stored);
|
||||||
assert(b);
|
assert(b);
|
||||||
|
assert(blob_stored);
|
||||||
|
|
||||||
JSContext* context0 = tf_ssb_get_context(ssb0);
|
JSContext* context0 = tf_ssb_get_context(ssb0);
|
||||||
JSValue obj = JS_NewObject(context0);
|
JSValue obj = JS_NewObject(context0);
|
||||||
@@ -1117,6 +1128,11 @@ void tf_ssb_test_replicate(const tf_test_options_t* options)
|
|||||||
tf_printf("%s user %d = %s private=%s\n", added ? "added" : "failed", i, public[i], private[i]);
|
tf_printf("%s user %d = %s private=%s\n", added ? "added" : "failed", i, public[i], private[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char blob_id[k_id_base64_len] = { 0 };
|
||||||
|
const char* k_blob = "Hello, new blob!";
|
||||||
|
b = tf_ssb_db_blob_store(ssb0, (const uint8_t*)k_blob, strlen(k_blob), blob_id, sizeof(blob_id), NULL);
|
||||||
|
assert(b);
|
||||||
|
|
||||||
JSContext* context0 = tf_ssb_get_context(ssb0);
|
JSContext* context0 = tf_ssb_get_context(ssb0);
|
||||||
for (int i = 0; i < k_key_count - 1; i++)
|
for (int i = 0; i < k_key_count - 1; i++)
|
||||||
{
|
{
|
||||||
@@ -1141,6 +1157,7 @@ void tf_ssb_test_replicate(const tf_test_options_t* options)
|
|||||||
obj = JS_NewObject(context0);
|
obj = JS_NewObject(context0);
|
||||||
JS_SetPropertyStr(context0, obj, "type", JS_NewString(context0, "post"));
|
JS_SetPropertyStr(context0, obj, "type", JS_NewString(context0, "post"));
|
||||||
JS_SetPropertyStr(context0, obj, "text", JS_NewString(context0, "Hello, world!"));
|
JS_SetPropertyStr(context0, obj, "text", JS_NewString(context0, "Hello, world!"));
|
||||||
|
JS_SetPropertyStr(context0, obj, "arbitrary_reference", JS_NewString(context0, blob_id));
|
||||||
stored = false;
|
stored = false;
|
||||||
signed_message = tf_ssb_sign_message(ssb0, self, private_bin, obj, NULL, 0);
|
signed_message = tf_ssb_sign_message(ssb0, self, private_bin, obj, NULL, 0);
|
||||||
tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored);
|
tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored);
|
||||||
@@ -1202,6 +1219,189 @@ void tf_ssb_test_replicate(const tf_test_options_t* options)
|
|||||||
tf_ssb_remove_message_added_callback(ssb1, _message_added, &count1);
|
tf_ssb_remove_message_added_callback(ssb1, _message_added, &count1);
|
||||||
tf_printf("done\n");
|
tf_printf("done\n");
|
||||||
|
|
||||||
|
tf_printf("Waiting for blob.\n");
|
||||||
|
while (!tf_ssb_db_blob_get(ssb1, blob_id, NULL, NULL))
|
||||||
|
{
|
||||||
|
tf_ssb_set_main_thread(ssb1, true);
|
||||||
|
uv_run(&loop, UV_RUN_ONCE);
|
||||||
|
tf_ssb_set_main_thread(ssb1, false);
|
||||||
|
}
|
||||||
|
tf_printf("done\n");
|
||||||
|
|
||||||
|
tf_ssb_send_close(ssb1);
|
||||||
|
|
||||||
|
uv_close((uv_handle_t*)&idle0, NULL);
|
||||||
|
uv_close((uv_handle_t*)&idle1, NULL);
|
||||||
|
|
||||||
|
tf_printf("final run\n");
|
||||||
|
tf_ssb_set_main_thread(ssb0, true);
|
||||||
|
tf_ssb_set_main_thread(ssb1, true);
|
||||||
|
uv_run(&loop, UV_RUN_DEFAULT);
|
||||||
|
tf_ssb_set_main_thread(ssb0, false);
|
||||||
|
tf_ssb_set_main_thread(ssb1, false);
|
||||||
|
tf_printf("done\n");
|
||||||
|
|
||||||
|
tf_printf("destroy 0\n");
|
||||||
|
tf_ssb_destroy(ssb0);
|
||||||
|
tf_printf("destroy 1\n");
|
||||||
|
tf_ssb_destroy(ssb1);
|
||||||
|
|
||||||
|
tf_printf("close\n");
|
||||||
|
uv_loop_close(&loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tf_ssb_test_replicate_blob(const tf_test_options_t* options)
|
||||||
|
{
|
||||||
|
tf_printf("Testing blob replication.\n");
|
||||||
|
|
||||||
|
uv_loop_t loop = { 0 };
|
||||||
|
uv_loop_init(&loop);
|
||||||
|
|
||||||
|
unlink("out/test_db0.sqlite");
|
||||||
|
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:out/test_db0.sqlite", NULL);
|
||||||
|
tf_ssb_register(tf_ssb_get_context(ssb0), ssb0);
|
||||||
|
unlink("out/test_db1.sqlite");
|
||||||
|
tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, "file:out/test_db1.sqlite", NULL);
|
||||||
|
tf_ssb_register(tf_ssb_get_context(ssb1), ssb1);
|
||||||
|
|
||||||
|
uv_idle_t idle0 = { .data = ssb0 };
|
||||||
|
uv_idle_init(&loop, &idle0);
|
||||||
|
uv_idle_start(&idle0, _ssb_test_idle);
|
||||||
|
|
||||||
|
uv_idle_t idle1 = { .data = ssb1 };
|
||||||
|
uv_idle_init(&loop, &idle1);
|
||||||
|
uv_idle_start(&idle1, _ssb_test_idle);
|
||||||
|
|
||||||
|
test_t test = {
|
||||||
|
.ssb0 = ssb0,
|
||||||
|
.ssb1 = ssb1,
|
||||||
|
};
|
||||||
|
|
||||||
|
tf_ssb_add_connections_changed_callback(ssb0, _ssb_test_connections_changed, NULL, &test);
|
||||||
|
tf_ssb_add_connections_changed_callback(ssb1, _ssb_test_connections_changed, NULL, &test);
|
||||||
|
|
||||||
|
tf_ssb_generate_keys(ssb0);
|
||||||
|
tf_ssb_generate_keys(ssb1);
|
||||||
|
|
||||||
|
uint8_t priv0[crypto_sign_SECRETKEYBYTES] = { 0 };
|
||||||
|
uint8_t priv1[crypto_sign_SECRETKEYBYTES] = { 0 };
|
||||||
|
tf_ssb_get_private_key(ssb0, priv0, sizeof(priv0));
|
||||||
|
tf_ssb_get_private_key(ssb1, priv1, sizeof(priv1));
|
||||||
|
|
||||||
|
char id0[k_id_base64_len] = { 0 };
|
||||||
|
char id1[k_id_base64_len] = { 0 };
|
||||||
|
bool b = tf_ssb_whoami(ssb0, id0, sizeof(id0));
|
||||||
|
(void)b;
|
||||||
|
assert(b);
|
||||||
|
b = tf_ssb_whoami(ssb1, id1, sizeof(id1));
|
||||||
|
assert(b);
|
||||||
|
tf_printf("ID %s and %s\n", id0, id1);
|
||||||
|
|
||||||
|
char priv0_str[512] = { 0 };
|
||||||
|
char priv1_str[512] = { 0 };
|
||||||
|
tf_base64_encode(priv0, sizeof(priv0), priv0_str, sizeof(priv0_str));
|
||||||
|
tf_base64_encode(priv1, sizeof(priv0), priv1_str, sizeof(priv1_str));
|
||||||
|
tf_ssb_db_identity_add(ssb0, "test", id0 + 1, priv0_str);
|
||||||
|
tf_ssb_db_identity_add(ssb1, "test", id1 + 1, priv1_str);
|
||||||
|
|
||||||
|
static const int k_key_count = 5;
|
||||||
|
char public[k_key_count][k_id_base64_len - 1];
|
||||||
|
char private[k_key_count][512];
|
||||||
|
for (int i = 0; i < k_key_count; i++)
|
||||||
|
{
|
||||||
|
tf_ssb_generate_keys_buffer(public[i], sizeof(public[i]), private[i], sizeof(private[i]));
|
||||||
|
bool added = tf_ssb_db_identity_add(ssb0, "test", public[i], private[i]);
|
||||||
|
tf_printf("%s user %d = %s private=%s\n", added ? "added" : "failed", i, public[i], private[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
tf_printf("ssb0\n");
|
||||||
|
tf_ssb_db_identity_visit_all(ssb0, _test_print_identity, ssb0);
|
||||||
|
tf_printf("ssb1\n");
|
||||||
|
tf_ssb_db_identity_visit_all(ssb1, _test_print_identity, ssb1);
|
||||||
|
|
||||||
|
tf_ssb_server_open(ssb0, 12347);
|
||||||
|
|
||||||
|
uint8_t id0bin[k_id_bin_len];
|
||||||
|
tf_ssb_id_str_to_bin(id0bin, id0);
|
||||||
|
tf_ssb_connect(ssb1, "127.0.0.1", 12347, id0bin, 0, NULL, NULL);
|
||||||
|
|
||||||
|
tf_printf("Waiting for connection.\n");
|
||||||
|
while (test.connection_count0 != 1 || test.connection_count1 != 1)
|
||||||
|
{
|
||||||
|
tf_ssb_set_main_thread(ssb0, true);
|
||||||
|
tf_ssb_set_main_thread(ssb1, true);
|
||||||
|
uv_run(&loop, UV_RUN_ONCE);
|
||||||
|
tf_ssb_set_main_thread(ssb0, false);
|
||||||
|
tf_ssb_set_main_thread(ssb1, false);
|
||||||
|
}
|
||||||
|
tf_ssb_server_close(ssb0);
|
||||||
|
|
||||||
|
char blob_id[k_id_base64_len] = { 0 };
|
||||||
|
const char* k_blob = "Hello, new blob!";
|
||||||
|
b = tf_ssb_db_blob_store(ssb0, (const uint8_t*)k_blob, strlen(k_blob), blob_id, sizeof(blob_id), NULL);
|
||||||
|
assert(b);
|
||||||
|
|
||||||
|
JSContext* context0 = tf_ssb_get_context(ssb0);
|
||||||
|
for (int i = 0; i < k_key_count - 1; i++)
|
||||||
|
{
|
||||||
|
JSValue obj = JS_NewObject(context0);
|
||||||
|
JS_SetPropertyStr(context0, obj, "type", JS_NewString(context0, "contact"));
|
||||||
|
char self[k_id_base64_len];
|
||||||
|
snprintf(self, sizeof(self), "@%s", public[i]);
|
||||||
|
char contact[k_id_base64_len];
|
||||||
|
snprintf(contact, sizeof(contact), "@%s", public[i + 1]);
|
||||||
|
JS_SetPropertyStr(context0, obj, "contact", JS_NewString(context0, contact));
|
||||||
|
JS_SetPropertyStr(context0, obj, "following", JS_TRUE);
|
||||||
|
bool stored = false;
|
||||||
|
uint8_t private_bin[512] = { 0 };
|
||||||
|
tf_base64_decode(private[i], strlen(private[i]) - strlen(".ed25519"), private_bin, sizeof(private_bin));
|
||||||
|
tf_printf("ssb0 %s following %s\n", self, contact);
|
||||||
|
JSValue signed_message = tf_ssb_sign_message(ssb0, self, private_bin, obj, NULL, 0);
|
||||||
|
tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored);
|
||||||
|
JS_FreeValue(context0, signed_message);
|
||||||
|
_wait_stored(ssb0, &stored);
|
||||||
|
JS_FreeValue(context0, obj);
|
||||||
|
|
||||||
|
obj = JS_NewObject(context0);
|
||||||
|
JS_SetPropertyStr(context0, obj, "type", JS_NewString(context0, "post"));
|
||||||
|
JS_SetPropertyStr(context0, obj, "text", JS_NewString(context0, "Hello, world!"));
|
||||||
|
JS_SetPropertyStr(context0, obj, "arbitrary_reference", JS_NewString(context0, blob_id));
|
||||||
|
stored = false;
|
||||||
|
signed_message = tf_ssb_sign_message(ssb0, self, private_bin, obj, NULL, 0);
|
||||||
|
tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored);
|
||||||
|
JS_FreeValue(context0, signed_message);
|
||||||
|
_wait_stored(ssb0, &stored);
|
||||||
|
JS_FreeValue(context0, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSContext* context1 = tf_ssb_get_context(ssb1);
|
||||||
|
{
|
||||||
|
JSValue obj = JS_NewObject(context1);
|
||||||
|
JS_SetPropertyStr(context1, obj, "type", JS_NewString(context1, "contact"));
|
||||||
|
char self[k_id_base64_len];
|
||||||
|
tf_string_set(self, sizeof(self), id1);
|
||||||
|
char contact[k_id_base64_len];
|
||||||
|
snprintf(contact, sizeof(contact), "@%s", public[0]);
|
||||||
|
JS_SetPropertyStr(context1, obj, "contact", JS_NewString(context1, contact));
|
||||||
|
JS_SetPropertyStr(context1, obj, "following", JS_TRUE);
|
||||||
|
bool stored = false;
|
||||||
|
tf_printf("ssb1 %s following %s\n", self, contact);
|
||||||
|
JSValue signed_message = tf_ssb_sign_message(ssb1, self, priv1, obj, NULL, 0);
|
||||||
|
tf_ssb_verify_strip_and_store_message(ssb1, signed_message, _message_stored, &stored);
|
||||||
|
JS_FreeValue(context1, signed_message);
|
||||||
|
_wait_stored(ssb1, &stored);
|
||||||
|
JS_FreeValue(context1, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
tf_printf("Waiting for blob.\n");
|
||||||
|
while (!tf_ssb_db_blob_get(ssb1, blob_id, NULL, NULL))
|
||||||
|
{
|
||||||
|
tf_ssb_set_main_thread(ssb1, true);
|
||||||
|
uv_run(&loop, UV_RUN_ONCE);
|
||||||
|
tf_ssb_set_main_thread(ssb1, false);
|
||||||
|
}
|
||||||
|
tf_printf("done\n");
|
||||||
|
|
||||||
tf_ssb_send_close(ssb1);
|
tf_ssb_send_close(ssb1);
|
||||||
|
|
||||||
uv_close((uv_handle_t*)&idle0, NULL);
|
uv_close((uv_handle_t*)&idle0, NULL);
|
||||||
|
|||||||
@@ -66,13 +66,19 @@ void tf_ssb_test_peer_exchange(const tf_test_options_t* options);
|
|||||||
void tf_ssb_test_publish(const tf_test_options_t* options);
|
void tf_ssb_test_publish(const tf_test_options_t* options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Test connecting by string.
|
** Test message and replication.
|
||||||
** @param options The test options.
|
** @param options The test options.
|
||||||
*/
|
*/
|
||||||
void tf_ssb_test_replicate(const tf_test_options_t* options);
|
void tf_ssb_test_replicate(const tf_test_options_t* options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Test invites.
|
** Test blob replication for a message received while already connected.
|
||||||
|
** @param options The test options.
|
||||||
|
*/
|
||||||
|
void tf_ssb_test_replicate_blob(const tf_test_options_t* options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Test connecting by string.
|
||||||
** @param options The test options.
|
** @param options The test options.
|
||||||
*/
|
*/
|
||||||
void tf_ssb_test_connect_str(const tf_test_options_t* options);
|
void tf_ssb_test_connect_str(const tf_test_options_t* options);
|
||||||
|
|||||||
13
src/task.c
13
src/task.c
@@ -1,7 +1,6 @@
|
|||||||
#include "task.h"
|
#include "task.h"
|
||||||
|
|
||||||
#include "api.js.h"
|
#include "api.js.h"
|
||||||
#include "bcrypt.js.h"
|
|
||||||
#include "database.js.h"
|
#include "database.js.h"
|
||||||
#include "file.js.h"
|
#include "file.js.h"
|
||||||
#include "httpd.js.h"
|
#include "httpd.js.h"
|
||||||
@@ -9,12 +8,10 @@
|
|||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
#include "packetstream.h"
|
#include "packetstream.h"
|
||||||
#include "serialize.h"
|
#include "serialize.h"
|
||||||
#include "socket.js.h"
|
|
||||||
#include "ssb.db.h"
|
#include "ssb.db.h"
|
||||||
#include "ssb.h"
|
#include "ssb.h"
|
||||||
#include "ssb.js.h"
|
#include "ssb.js.h"
|
||||||
#include "taskstub.js.h"
|
#include "taskstub.js.h"
|
||||||
#include "tlscontext.js.h"
|
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "util.js.h"
|
#include "util.js.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
@@ -827,9 +824,6 @@ static JSValue _tf_task_getStats(JSContext* context, JSValueConst this_val, int
|
|||||||
JS_SetPropertyStr(context, result, "tls_malloc_percent", JS_NewFloat64(context, 100.0 * tf_mem_get_tls_malloc_size() / total_memory));
|
JS_SetPropertyStr(context, result, "tls_malloc_percent", JS_NewFloat64(context, 100.0 * tf_mem_get_tls_malloc_size() / total_memory));
|
||||||
JS_SetPropertyStr(context, result, "tf_malloc_percent", JS_NewFloat64(context, 100.0 * tf_mem_get_tf_malloc_size() / total_memory));
|
JS_SetPropertyStr(context, result, "tf_malloc_percent", JS_NewFloat64(context, 100.0 * tf_mem_get_tf_malloc_size() / total_memory));
|
||||||
|
|
||||||
JS_SetPropertyStr(context, result, "socket_count", JS_NewInt32(context, tf_socket_get_count()));
|
|
||||||
JS_SetPropertyStr(context, result, "socket_open_count", JS_NewInt32(context, tf_socket_get_open_count()));
|
|
||||||
|
|
||||||
if (task->_ssb)
|
if (task->_ssb)
|
||||||
{
|
{
|
||||||
tf_ssb_stats_t ssb_stats = { 0 };
|
tf_ssb_stats_t ssb_stats = { 0 };
|
||||||
@@ -1101,7 +1095,7 @@ void tf_task_on_receive_packet(int packetType, const char* begin, size_t length,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
exit(1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case kSetImports:
|
case kSetImports:
|
||||||
@@ -1666,8 +1660,6 @@ void tf_task_activate(tf_task_t* task)
|
|||||||
sqlite3_open(task->_db_path, &task->_db);
|
sqlite3_open(task->_db_path, &task->_db);
|
||||||
|
|
||||||
JS_SetPropertyStr(context, global, "Task", tf_taskstub_register(context));
|
JS_SetPropertyStr(context, global, "Task", tf_taskstub_register(context));
|
||||||
JS_SetPropertyStr(context, global, "Socket", tf_socket_register(context));
|
|
||||||
JS_SetPropertyStr(context, global, "TlsContext", tf_tls_context_register(context));
|
|
||||||
tf_file_register(context);
|
tf_file_register(context);
|
||||||
tf_database_register(context);
|
tf_database_register(context);
|
||||||
|
|
||||||
@@ -1699,7 +1691,7 @@ void tf_task_activate(tf_task_t* task)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
tf_printf("Assignment missing '=': %s.\n", assignment);
|
tf_printf("Assignment missing '=': %s.\n", assignment);
|
||||||
exit(1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tf_free(copy);
|
tf_free(copy);
|
||||||
@@ -1728,7 +1720,6 @@ void tf_task_activate(tf_task_t* task)
|
|||||||
tf_trace_set_write_callback(task->_trace, _tf_task_trace_to_parent, task);
|
tf_trace_set_write_callback(task->_trace, _tf_task_trace_to_parent, task);
|
||||||
}
|
}
|
||||||
|
|
||||||
tf_bcrypt_register(context);
|
|
||||||
tf_util_register(context);
|
tf_util_register(context);
|
||||||
JS_SetPropertyStr(context, global, "exit", JS_NewCFunction(context, _tf_task_exit, "exit", 1));
|
JS_SetPropertyStr(context, global, "exit", JS_NewCFunction(context, _tf_task_exit, "exit", 1));
|
||||||
JS_SetPropertyStr(context, global, "version", JS_NewCFunction(context, _tf_task_version, "version", 0));
|
JS_SetPropertyStr(context, global, "version", JS_NewCFunction(context, _tf_task_version, "version", 0));
|
||||||
|
|||||||
89
src/tests.c
89
src/tests.c
@@ -549,93 +549,6 @@ static void _test_float(const tf_test_options_t* options)
|
|||||||
unlink("out/child.js");
|
unlink("out/child.js");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _test_socket(const tf_test_options_t* options)
|
|
||||||
{
|
|
||||||
_write_file("out/test.js",
|
|
||||||
"'use strict';\n"
|
|
||||||
"\n"
|
|
||||||
"var s = new Socket();\n"
|
|
||||||
"print('connecting');\n"
|
|
||||||
"print('before connect', s.isConnected);\n"
|
|
||||||
"s.onError(function(e) {\n"
|
|
||||||
" print(e);\n"
|
|
||||||
"});\n"
|
|
||||||
"print('noDelay', s.noDelay);\n"
|
|
||||||
"s.noDelay = true;\n"
|
|
||||||
"s.connect('www.unprompted.com', 80).then(function() {\n"
|
|
||||||
" print('connected', 'www.unprompted.com', 80, s.isConnected);\n"
|
|
||||||
" print(s.peerName);\n"
|
|
||||||
" s.read(function(data) {\n"
|
|
||||||
" print('read', data ? data.length : null);\n"
|
|
||||||
" });\n"
|
|
||||||
" s.write('GET / HTTP/1.0\\r\\n\\r\\n');\n"
|
|
||||||
"}).then(function(e) {\n"
|
|
||||||
" print('closed 1');\n"
|
|
||||||
"});\n"
|
|
||||||
"\n"
|
|
||||||
"var s2 = new Socket();\n"
|
|
||||||
"print('connecting');\n"
|
|
||||||
"print('before connect', s2.isConnected);\n"
|
|
||||||
"s2.onError(function(e) {\n"
|
|
||||||
" print('error');\n"
|
|
||||||
" print(e);\n"
|
|
||||||
"});\n"
|
|
||||||
"print('noDelay', s2.noDelay);\n"
|
|
||||||
"s2.noDelay = true;\n"
|
|
||||||
"s2.connect('www.unprompted.com', 443).then(function() {\n"
|
|
||||||
" print('connected', 'www.unprompted.com', 443);\n"
|
|
||||||
" s2.read(function(data) {\n"
|
|
||||||
" print('read', data ? data.length : null);\n"
|
|
||||||
" });\n"
|
|
||||||
" return s2.startTls();\n"
|
|
||||||
"}).then(function() {\n"
|
|
||||||
" print('ready');\n"
|
|
||||||
" print(s2.peerName);\n"
|
|
||||||
" s2.write('GET / HTTP/1.0\\r\\nConnection: close\\r\\n\\r\\n').then(function() {\n"
|
|
||||||
" s2.shutdown();\n"
|
|
||||||
" });\n"
|
|
||||||
"}).catch(function(e) {\n"
|
|
||||||
" print('caught');\n"
|
|
||||||
" print(e);\n"
|
|
||||||
"});\n"
|
|
||||||
"var s3 = new Socket();\n"
|
|
||||||
"print('connecting s3');\n"
|
|
||||||
"print('before connect', s3.isConnected);\n"
|
|
||||||
"s3.onError(function(e) {\n"
|
|
||||||
" print('error');\n"
|
|
||||||
" print(e);\n"
|
|
||||||
"});\n"
|
|
||||||
"print('noDelay', s3.noDelay);\n"
|
|
||||||
"s3.noDelay = true;\n"
|
|
||||||
"s3.connect('0.0.0.0', 443).then(function() {\n"
|
|
||||||
" print('connected', '0.0.0.0', 443);\n"
|
|
||||||
" s3.read(function(data) {\n"
|
|
||||||
" print('read', data ? data.length : null);\n"
|
|
||||||
" });\n"
|
|
||||||
" return s3.startTls();\n"
|
|
||||||
"}).then(function() {\n"
|
|
||||||
" print('ready');\n"
|
|
||||||
" print(s3.peerName);\n"
|
|
||||||
" s3.write('GET / HTTP/1.0\\r\\nConnection: close\\r\\n\\r\\n').then(function() {\n"
|
|
||||||
" s3.shutdown();\n"
|
|
||||||
" });\n"
|
|
||||||
"}).catch(function(e) {\n"
|
|
||||||
" print('caught');\n"
|
|
||||||
" print(e);\n"
|
|
||||||
"});\n");
|
|
||||||
|
|
||||||
char command[256];
|
|
||||||
unlink("out/test_db0.sqlite");
|
|
||||||
snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
|
|
||||||
tf_printf("%s\n", command);
|
|
||||||
int result = system(command);
|
|
||||||
tf_printf("returned %d\n", WEXITSTATUS(result));
|
|
||||||
assert(WIFEXITED(result));
|
|
||||||
assert(WEXITSTATUS(result) == 0);
|
|
||||||
|
|
||||||
unlink("out/test.js");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _test_file(const tf_test_options_t* options)
|
static void _test_file(const tf_test_options_t* options)
|
||||||
{
|
{
|
||||||
_write_file("out/test.js",
|
_write_file("out/test.js",
|
||||||
@@ -1065,7 +978,6 @@ void tf_tests(const tf_test_options_t* options)
|
|||||||
_tf_test_run(options, "icu", _test_icu, false);
|
_tf_test_run(options, "icu", _test_icu, false);
|
||||||
_tf_test_run(options, "uint8array", _test_uint8array, false);
|
_tf_test_run(options, "uint8array", _test_uint8array, false);
|
||||||
_tf_test_run(options, "float", _test_float, false);
|
_tf_test_run(options, "float", _test_float, false);
|
||||||
_tf_test_run(options, "socket", _test_socket, false);
|
|
||||||
_tf_test_run(options, "file", _test_file, false);
|
_tf_test_run(options, "file", _test_file, false);
|
||||||
_tf_test_run(options, "b64", _test_b64, false);
|
_tf_test_run(options, "b64", _test_b64, false);
|
||||||
_tf_test_run(options, "rooms", tf_ssb_test_rooms, false);
|
_tf_test_run(options, "rooms", tf_ssb_test_rooms, false);
|
||||||
@@ -1076,6 +988,7 @@ void tf_tests(const tf_test_options_t* options)
|
|||||||
_tf_test_run(options, "peer_exchange", tf_ssb_test_peer_exchange, false);
|
_tf_test_run(options, "peer_exchange", tf_ssb_test_peer_exchange, false);
|
||||||
_tf_test_run(options, "publish", tf_ssb_test_publish, false);
|
_tf_test_run(options, "publish", tf_ssb_test_publish, false);
|
||||||
_tf_test_run(options, "replicate", tf_ssb_test_replicate, false);
|
_tf_test_run(options, "replicate", tf_ssb_test_replicate, false);
|
||||||
|
_tf_test_run(options, "replicate_blob", tf_ssb_test_replicate_blob, false);
|
||||||
_tf_test_run(options, "connect_str", tf_ssb_test_connect_str, false);
|
_tf_test_run(options, "connect_str", tf_ssb_test_connect_str, false);
|
||||||
_tf_test_run(options, "invite", tf_ssb_test_invite, false);
|
_tf_test_run(options, "invite", tf_ssb_test_invite, false);
|
||||||
_tf_test_run(options, "triggers", tf_ssb_test_triggers, false);
|
_tf_test_run(options, "triggers", tf_ssb_test_triggers, false);
|
||||||
|
|||||||
@@ -1,105 +0,0 @@
|
|||||||
#include "tlscontext.js.h"
|
|
||||||
|
|
||||||
#include "log.h"
|
|
||||||
#include "mem.h"
|
|
||||||
#include "task.h"
|
|
||||||
#include "tls.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
static JSClassID _classId;
|
|
||||||
static int _count;
|
|
||||||
|
|
||||||
typedef struct _tf_tls_context_t
|
|
||||||
{
|
|
||||||
tf_tls_context_t* context;
|
|
||||||
tf_task_t* task;
|
|
||||||
JSValue object;
|
|
||||||
} tf_tls_context_t;
|
|
||||||
|
|
||||||
static JSValue _tls_context_create(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
|
|
||||||
static void _tls_context_finalizer(JSRuntime* runtime, JSValue value);
|
|
||||||
|
|
||||||
static JSValue _tls_context_set_certificate(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
|
||||||
{
|
|
||||||
tf_tls_context_t* tls = JS_GetOpaque(this_val, _classId);
|
|
||||||
const char* value = JS_ToCString(context, argv[0]);
|
|
||||||
tf_tls_context_set_certificate(tls->context, value);
|
|
||||||
JS_FreeCString(context, value);
|
|
||||||
return JS_UNDEFINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static JSValue _tls_context_set_private_key(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
|
||||||
{
|
|
||||||
tf_tls_context_t* tls = JS_GetOpaque(this_val, _classId);
|
|
||||||
const char* value = JS_ToCString(context, argv[0]);
|
|
||||||
tf_tls_context_set_private_key(tls->context, value);
|
|
||||||
JS_FreeCString(context, value);
|
|
||||||
return JS_UNDEFINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static JSValue _tls_context_add_trusted_certificate(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
|
||||||
{
|
|
||||||
tf_tls_context_t* tls = JS_GetOpaque(this_val, _classId);
|
|
||||||
const char* value = JS_ToCString(context, argv[0]);
|
|
||||||
tf_tls_context_add_trusted_certificate(tls->context, value);
|
|
||||||
JS_FreeCString(context, value);
|
|
||||||
return JS_UNDEFINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue tf_tls_context_register(JSContext* context)
|
|
||||||
{
|
|
||||||
JS_NewClassID(&_classId);
|
|
||||||
JSClassDef def = {
|
|
||||||
.class_name = "TlsContext",
|
|
||||||
.finalizer = _tls_context_finalizer,
|
|
||||||
};
|
|
||||||
if (JS_NewClass(JS_GetRuntime(context), _classId, &def) != 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to register TlsContext.\n");
|
|
||||||
}
|
|
||||||
return JS_NewCFunction2(context, _tls_context_create, "TlsContext", 0, JS_CFUNC_constructor, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
tf_tls_context_t* tf_tls_context_get(JSValue value)
|
|
||||||
{
|
|
||||||
tf_tls_context_t* tls = JS_GetOpaque(value, _classId);
|
|
||||||
return tls ? tls->context : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int tf_tls_context_get_count()
|
|
||||||
{
|
|
||||||
return _count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static JSValue _tls_context_create(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
|
||||||
{
|
|
||||||
tf_tls_context_t* tls = tf_malloc(sizeof(tf_tls_context_t));
|
|
||||||
memset(tls, 0, sizeof(*tls));
|
|
||||||
|
|
||||||
++_count;
|
|
||||||
tls->object = JS_NewObjectClass(context, _classId);
|
|
||||||
JS_SetOpaque(tls->object, tls);
|
|
||||||
|
|
||||||
JS_SetPropertyStr(context, tls->object, "setCertificate", JS_NewCFunction(context, _tls_context_set_certificate, "setCertificate", 1));
|
|
||||||
JS_SetPropertyStr(context, tls->object, "setPrivateKey", JS_NewCFunction(context, _tls_context_set_private_key, "setPrivateKey", 1));
|
|
||||||
JS_SetPropertyStr(context, tls->object, "addTrustedCertificate", JS_NewCFunction(context, _tls_context_add_trusted_certificate, "addTrustedCertificate", 1));
|
|
||||||
|
|
||||||
tls->context = tf_tls_context_create();
|
|
||||||
tls->task = tf_task_get(context);
|
|
||||||
|
|
||||||
return tls->object;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _tls_context_finalizer(JSRuntime* runtime, JSValue value)
|
|
||||||
{
|
|
||||||
tf_tls_context_t* tls = JS_GetOpaque(value, _classId);
|
|
||||||
if (tls->context)
|
|
||||||
{
|
|
||||||
tf_tls_context_destroy(tls->context);
|
|
||||||
tls->context = NULL;
|
|
||||||
}
|
|
||||||
--_count;
|
|
||||||
tf_free(tls);
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
/**
|
|
||||||
** \defgroup tls_js TLS Interface
|
|
||||||
** Exposes \ref tls to JS.
|
|
||||||
** @{
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "quickjs.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
** A TLS context instance.
|
|
||||||
*/
|
|
||||||
typedef struct _tf_tls_context_t tf_tls_context_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
** Register TLS script interface.
|
|
||||||
** @param context The TLS context.
|
|
||||||
** @return the TlsContext constructor.
|
|
||||||
*/
|
|
||||||
JSValue tf_tls_context_register(JSContext* context);
|
|
||||||
|
|
||||||
/**
|
|
||||||
** Get a TLS context instance from its JS object.
|
|
||||||
** @param value A TlsContext JS object.
|
|
||||||
** @return The corresponding instance.
|
|
||||||
*/
|
|
||||||
tf_tls_context_t* tf_tls_context_get(JSValue value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
** Get the number of active TLS context instances.
|
|
||||||
** @return The number of TlsContext objects created that have not been
|
|
||||||
** finalized.
|
|
||||||
*/
|
|
||||||
int tf_tls_context_get_count();
|
|
||||||
|
|
||||||
/** @} */
|
|
||||||
@@ -253,66 +253,6 @@ bool tf_util_report_error(JSContext* context, JSValue value)
|
|||||||
return is_error;
|
return is_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSValue _util_parseHttpResponse(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
|
||||||
{
|
|
||||||
JSValue result = JS_UNDEFINED;
|
|
||||||
int status = 0;
|
|
||||||
int minor_version = 0;
|
|
||||||
const char* message = NULL;
|
|
||||||
size_t message_length = 0;
|
|
||||||
struct phr_header headers[100];
|
|
||||||
size_t header_count = sizeof(headers) / sizeof(*headers);
|
|
||||||
int previous_length = 0;
|
|
||||||
JS_ToInt32(context, &previous_length, argv[1]);
|
|
||||||
|
|
||||||
JSValue buffer = JS_UNDEFINED;
|
|
||||||
size_t length;
|
|
||||||
uint8_t* array = tf_util_try_get_array_buffer(context, &length, argv[0]);
|
|
||||||
if (!array)
|
|
||||||
{
|
|
||||||
size_t offset;
|
|
||||||
size_t element_size;
|
|
||||||
buffer = tf_util_try_get_typed_array_buffer(context, argv[0], &offset, &length, &element_size);
|
|
||||||
if (!JS_IsException(buffer))
|
|
||||||
{
|
|
||||||
array = tf_util_try_get_array_buffer(context, &length, buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array)
|
|
||||||
{
|
|
||||||
int parse_result = phr_parse_response((const char*)array, length, &minor_version, &status, &message, &message_length, headers, &header_count, previous_length);
|
|
||||||
if (parse_result > 0)
|
|
||||||
{
|
|
||||||
result = JS_NewObject(context);
|
|
||||||
JS_SetPropertyStr(context, result, "bytes_parsed", JS_NewInt32(context, parse_result));
|
|
||||||
JS_SetPropertyStr(context, result, "minor_version", JS_NewInt32(context, minor_version));
|
|
||||||
JS_SetPropertyStr(context, result, "status", JS_NewInt32(context, status));
|
|
||||||
JS_SetPropertyStr(context, result, "message", JS_NewStringLen(context, message, message_length));
|
|
||||||
JSValue header_object = JS_NewObject(context);
|
|
||||||
for (int i = 0; i < (int)header_count; i++)
|
|
||||||
{
|
|
||||||
char name[256];
|
|
||||||
snprintf(name, sizeof(name), "%.*s", (int)headers[i].name_len, headers[i].name);
|
|
||||||
JS_SetPropertyStr(context, header_object, name, JS_NewStringLen(context, headers[i].value, headers[i].value_len));
|
|
||||||
}
|
|
||||||
JS_SetPropertyStr(context, result, "headers", header_object);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = JS_NewInt32(context, parse_result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = JS_ThrowTypeError(context, "Could not convert argument to array.");
|
|
||||||
}
|
|
||||||
|
|
||||||
JS_FreeValue(context, buffer);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char* k_kind_name[] = {
|
static const char* k_kind_name[] = {
|
||||||
[k_kind_bool] = "bool",
|
[k_kind_bool] = "bool",
|
||||||
[k_kind_int] = "int",
|
[k_kind_int] = "int",
|
||||||
@@ -359,10 +299,6 @@ static const setting_t k_settings[] = {
|
|||||||
.type = "integer",
|
.type = "integer",
|
||||||
.description = "Blobs older than this will be automatically deleted.",
|
.description = "Blobs older than this will be automatically deleted.",
|
||||||
.default_value = { .kind = k_kind_int, .int_value = TF_IS_MOBILE ? (int)(1.0f * 365 * 24 * 60 * 60) : -1 } },
|
.default_value = { .kind = k_kind_int, .int_value = TF_IS_MOBILE ? (int)(1.0f * 365 * 24 * 60 * 60) : -1 } },
|
||||||
{ .name = "fetch_hosts",
|
|
||||||
.type = "string",
|
|
||||||
.description = "Comma-separated list of host names to which HTTP fetch requests are allowed. None if empty.",
|
|
||||||
.default_value = { .kind = k_kind_string, .string_value = NULL } },
|
|
||||||
{ .name = "http_redirect",
|
{ .name = "http_redirect",
|
||||||
.type = "string",
|
.type = "string",
|
||||||
.description = "If connecting by HTTP and HTTPS is configured, Location header prefix (ie, \"http://example.com\")",
|
.description = "If connecting by HTTP and HTTPS is configured, Location header prefix (ie, \"http://example.com\")",
|
||||||
@@ -523,7 +459,6 @@ void tf_util_register(JSContext* context)
|
|||||||
JS_SetPropertyStr(context, global, "bip39Words", JS_NewCFunction(context, _util_bip39_words, "bip39Words", 1));
|
JS_SetPropertyStr(context, global, "bip39Words", JS_NewCFunction(context, _util_bip39_words, "bip39Words", 1));
|
||||||
JS_SetPropertyStr(context, global, "bip39Bytes", JS_NewCFunction(context, _util_bip39_bytes, "bip39Bytes", 1));
|
JS_SetPropertyStr(context, global, "bip39Bytes", JS_NewCFunction(context, _util_bip39_bytes, "bip39Bytes", 1));
|
||||||
JS_SetPropertyStr(context, global, "print", JS_NewCFunction(context, _util_print, "print", 1));
|
JS_SetPropertyStr(context, global, "print", JS_NewCFunction(context, _util_print, "print", 1));
|
||||||
JS_SetPropertyStr(context, global, "parseHttpResponse", JS_NewCFunction(context, _util_parseHttpResponse, "parseHttpResponse", 2));
|
|
||||||
JS_SetPropertyStr(context, global, "defaultGlobalSettings", JS_NewCFunction(context, _util_defaultGlobalSettings, "defaultGlobalSettings", 2));
|
JS_SetPropertyStr(context, global, "defaultGlobalSettings", JS_NewCFunction(context, _util_defaultGlobalSettings, "defaultGlobalSettings", 2));
|
||||||
JS_FreeValue(context, global);
|
JS_FreeValue(context, global);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
#define VERSION_NUMBER "0.0.33"
|
#define VERSION_NUMBER "0.2025.10-wip"
|
||||||
#define VERSION_NAME "This program kills fascists."
|
#define VERSION_NAME "This program kills fascists."
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ try:
|
|||||||
select(driver, ['#document', 'frame', '#gs_room_name'], ('send_keys', 'test room'))
|
select(driver, ['#document', 'frame', '#gs_room_name'], ('send_keys', 'test room'))
|
||||||
select(driver, ['#document', 'frame', '//*[@id="gs_room_name"]/following-sibling::button'], ('click',))
|
select(driver, ['#document', 'frame', '//*[@id="gs_room_name"]/following-sibling::button'], ('click',))
|
||||||
select(driver, ['//button[text()="✅ Allow"]'], ('click',))
|
select(driver, ['//button[text()="✅ Allow"]'], ('click',))
|
||||||
driver.switch_to.alert.accept()
|
wait.until(expected_conditions.alert_is_present()).accept()
|
||||||
|
|
||||||
select(driver, ['tf-navigation', 'shadow_root', '#identity'], ('click',))
|
select(driver, ['tf-navigation', 'shadow_root', '#identity'], ('click',))
|
||||||
select(driver, ['tf-navigation', 'shadow_root', '#logout'], ('click',))
|
select(driver, ['tf-navigation', 'shadow_root', '#logout'], ('click',))
|
||||||
|
|||||||
Reference in New Issue
Block a user