Comment: | steganography related software |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
0be45629487f0047c96bb372e521e1f3 |
User & Date: | martin_vahi on 2018-03-21 17:38:58 |
Other Links: | manifest | tags |
2018-03-21 18:18 | banking & unclassified_references check-in: 3d7c129ddd user: martin_vahi tags: trunk | |
2018-03-21 17:38 | steganography related software check-in: 0be4562948 user: martin_vahi tags: trunk | |
2018-03-21 16:27 | omnetpp.org Network Simulator check-in: d8576ecc35 user: martin_vahi tags: trunk | |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/COMMENTS.txt version [c14e34d3b2].
> > > > > > > > > |
1 2 3 4 5 6 7 8 9 |
The karasiq-nanoboard is a Scala based steganography software that uses PNG images as containers. Home page: https://github.com/Karasiq/nanoboard |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/pull_new_version_from_git_repository.bash version [1f47fec18c].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
#!/usr/bin/env bash #========================================================================== # Initial author: Martin.Vahi@softf1.com # This file is in public domain. #========================================================================== S_FP_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #-------------------------------------------------------------------------- # For copy-pasting to the ~/.bashrc # # alias mmmv_cre_git_clone="cp $PATH_TO_THE<$S_FP_DIR>/pull_new_version_from_git_repository ./; mkdir -p ./the_repository_clones;" # #-------------------------------------------------------------------------- fun_assert_exists_on_path_t1 () { local S_NAME_OF_THE_EXECUTABLE=$1 # first function argument local S_TMP_0="\`which $S_NAME_OF_THE_EXECUTABLE 2>/dev/null\`" local S_TMP_1="" local S_TMP_2="S_TMP_1=$S_TMP_0" eval ${S_TMP_2} if [ "$S_TMP_1" == "" ] ; then echo "" echo "This bash script requires the \"$S_NAME_OF_THE_EXECUTABLE\" to be on the PATH." echo "GUID=='5b33d722-4ad8-491b-a330-43136020a1e7'" echo "" exit 1 # exit with error fi } # fun_assert_exists_on_path_t1 fun_assert_exists_on_path_t1 "ruby" fun_assert_exists_on_path_t1 "grep" fun_assert_exists_on_path_t1 "date" fun_assert_exists_on_path_t1 "git" #-------------------------------------------------------------------------- S_TMP_0="`uname -a | grep -E [Ll]inux`" if [ "$S_TMP_0" == "" ]; then S_TMP_0="`uname -a | grep -E [Bb][Ss][Dd]`" if [ "$S_TMP_0" == "" ]; then echo "" echo " The classical command line utilities at " echo " different operating systems, for example, Linux and BSD," echo " differ. This script is designed to run only on Linux and BSD." echo " If You are willing to risk that some of Your data " echo " is deleted and/or Your operating system instance" echo " becomes permanently flawed, to the point that " echo " it will not even boot, then You may edit the Bash script that " echo " displays this error message by modifying the test that " echo " checks for the operating system type." echo "" echo " If You do decide to edit this Bash script, then " echo " a recommendation is to test Your modifications " echo " within a virtual machine or, if virtual machines are not" echo " an option, as some new operating system user that does not have " echo " any access to the vital data/files." echo " GUID=='1ee02b82-4b99-47f3-8430-43136020a1e7'" echo "" echo " Aborting script without doing anything." echo "" echo "GUID=='7f1be944-f283-4c07-b530-43136020a1e7'" echo "" exit 1 # exit with error fi fi #-------------------------------------------------------------------------- S_TIMESTAMP="`date +%Y`_`date +%m`_`date +%d`_T_`date +%H`h_`date +%M`min_`date +%S`s" S_FP_ARCHIVE="$S_FP_DIR/archives/$S_TIMESTAMP" S_FP_THE_REPOSITORY_CLONES="$S_FP_DIR/the_repository_clones" mkdir -p $S_FP_THE_REPOSITORY_CLONES #-------------------------------------------------------------------------- S_ARGV_0="$1" SB_SKIP_ARCHIVING="f" fun_init_sb_archive_and_archives_folder(){ #-------- if [ "$S_ARGV_0" == "skip_archiving" ]; then SB_SKIP_ARCHIVING="t" fi if [ "$S_ARGV_0" == "ska" ]; then # abbreviation of "skip archiving" SB_SKIP_ARCHIVING="t" fi #-------- if [ "$SB_SKIP_ARCHIVING" != "t" ]; then mkdir -p $S_FP_ARCHIVE fi #-------- } # fun_init_sb_archive_and_archives_folder fun_init_sb_archive_and_archives_folder #-------------------------------------------------------------------------- AR_REPO_FOLDER_NAMES=() fun_assemble_array_of_repository_clone_folder_names () { cd $S_FP_THE_REPOSITORY_CLONES local S_TMP_0="`ruby -e \"ar=Array.new; Dir.glob('*').each{|x| if File.directory? x then ar<<x end}; puts(ar.to_s.gsub('[','(').gsub(']',')').gsub(',',' '))\"`" cd $S_FP_DIR local S_TMP_1="AR_REPO_FOLDER_NAMES=$S_TMP_0" eval ${S_TMP_1} } # fun_assemble_array_of_repository_clone_folder_names fun_assemble_array_of_repository_clone_folder_names fun_update () { #-------- local S_FP_FUNC_UPDATE_ORIG="`pwd`" #-------- for s_iter in ${AR_REPO_FOLDER_NAMES[@]}; do S_FOLDER_NAME_OF_THE_LOCAL_COPY="$s_iter" echo "" #---- if [ "$SB_SKIP_ARCHIVING" != "t" ]; then echo " Archiving a copy of $S_FOLDER_NAME_OF_THE_LOCAL_COPY" cp -f -R $S_FP_THE_REPOSITORY_CLONES/$S_FOLDER_NAME_OF_THE_LOCAL_COPY $S_FP_ARCHIVE/ else echo " Skipping the archiving a copy of $S_FOLDER_NAME_OF_THE_LOCAL_COPY" fi #---- cd $S_FP_THE_REPOSITORY_CLONES/$S_FOLDER_NAME_OF_THE_LOCAL_COPY echo "Checking out a newer version of $S_FOLDER_NAME_OF_THE_LOCAL_COPY" #-------- # Downloads the newest version of the software to that folder. git checkout --force # overwrites local changes, like the "svn co" git pull --all --recurse-submodules --force # gets the submodules #---- # http://stackoverflow.com/questions/1030169/easy-way-pull-latest-of-all-submodules git submodule update --init --recursive --force #-------- cd $S_FP_DIR done cd $S_FP_FUNC_UPDATE_ORIG } # fun_update fun_update # is a call to the function echo "" #========================================================================== |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/FETCH_HEAD version [7d32254f07].
> > > > > > |
1 2 3 4 5 6 |
020ae3c469c33e2d33ddab6411cc1b83d3d58166 branch 'master' of https://github.com/Karasiq/nanoboard 2bd19ceac4d7c10db8ab121e27eea7eb32f28e45 not-for-merge branch 'captcha' of https://github.com/Karasiq/nanoboard 0732b898d1d18c1a892a97377c0775adb6d01b12 not-for-merge branch 'new-format' of https://github.com/Karasiq/nanoboard 020ae3c469c33e2d33ddab6411cc1b83d3d58166 not-for-merge tag 'v1.3.2' of https://github.com/Karasiq/nanoboard b423e5a6ed750f2f5131edb7eb0bbe66617b8c30 not-for-merge tag 'v1.3.0' of https://github.com/Karasiq/nanoboard efaedd598892e3118401c5e7c6258bb563651962 not-for-merge tag 'v1.3.1' of https://github.com/Karasiq/nanoboard |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/HEAD version [acbaef275e].
> |
1 |
ref: refs/heads/master
|
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/ORIG_HEAD version [02cc7799b7].
> |
1 |
eaa9bd05c684736620ba0aa89de29816ee118628
|
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/config version [d6a676db81].
> > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 |
[core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [remote "origin"] url = https://github.com/Karasiq/nanoboard.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "master"] remote = origin merge = refs/heads/master |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/description version [9635f1b7e1].
> |
1 |
Unnamed repository; edit this file 'description' to name the repository.
|
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/hooks/applypatch-msg.sample version [86b9655a9e].
> > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#!/bin/sh # # An example hook script to check the commit log message taken by # applypatch from an e-mail message. # # The hook should exit with non-zero status after issuing an # appropriate message if it wants to stop the commit. The hook is # allowed to edit the commit message file. # # To enable this hook, rename this file to "applypatch-msg". . git-sh-setup test -x "$GIT_DIR/hooks/commit-msg" && exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} : |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/hooks/commit-msg.sample version [ee1ed5aad9].
> > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#!/bin/sh # # An example hook script to check the commit log message. # Called by "git commit" with one argument, the name of the file # that has the commit message. The hook should exit with non-zero # status after issuing an appropriate message if it wants to stop the # commit. The hook is allowed to edit the commit message file. # # To enable this hook, rename this file to "commit-msg". # Uncomment the below to add a Signed-off-by line to the message. # Doing this in a hook is a bad idea in general, but the prepare-commit-msg # hook is more suited to it. # # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" # This example catches duplicate Signed-off-by lines. test "" = "$(grep '^Signed-off-by: ' "$1" | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { echo >&2 Duplicate Signed-off-by lines. exit 1 } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/hooks/post-update.sample version [b614c2f63d].
> > > > > > > > |
1 2 3 4 5 6 7 8 |
#!/bin/sh # # An example hook script to prepare a packed repository for use over # dumb transports. # # To enable this hook, rename this file to "post-update". exec git update-server-info |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/hooks/pre-applypatch.sample version [42fa415649].
> > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#!/bin/sh # # An example hook script to verify what is about to be committed # by applypatch from an e-mail message. # # The hook should exit with non-zero status after issuing an # appropriate message if it wants to stop the commit. # # To enable this hook, rename this file to "pre-applypatch". . git-sh-setup test -x "$GIT_DIR/hooks/pre-commit" && exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} : |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/hooks/pre-commit.sample version [36aed8976d].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
#!/bin/sh # # An example hook script to verify what is about to be committed. # Called by "git commit" with no arguments. The hook should # exit with non-zero status after issuing an appropriate message if # it wants to stop the commit. # # To enable this hook, rename this file to "pre-commit". if git rev-parse --verify HEAD >/dev/null 2>&1 then against=HEAD else # Initial commit: diff against an empty tree object against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi # If you want to allow non-ASCII filenames set this variable to true. allownonascii=$(git config --bool hooks.allownonascii) # Redirect output to stderr. exec 1>&2 # Cross platform projects tend to avoid non-ASCII filenames; prevent # them from being added to the repository. We exploit the fact that the # printable range starts at the space character and ends with tilde. if [ "$allownonascii" != "true" ] && # Note that the use of brackets around a tr range is ok here, (it's # even required, for portability to Solaris 10's /usr/bin/tr), since # the square bracket bytes happen to fall in the designated range. test $(git diff --cached --name-only --diff-filter=A -z $against | LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 then cat <<\EOF Error: Attempt to add a non-ASCII file name. This can cause problems if you want to work with people on other platforms. To be portable it is advisable to rename the file. If you know what you are doing you can disable this check using: git config hooks.allownonascii true EOF exit 1 fi # If there are whitespace errors, print the offending file names and fail. exec git diff-index --check --cached $against -- |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/hooks/pre-push.sample version [b4ad74c989].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
#!/bin/sh # An example hook script to verify what is about to be pushed. Called by "git # push" after it has checked the remote status, but before anything has been # pushed. If this script exits with a non-zero status nothing will be pushed. # # This hook is called with the following parameters: # # $1 -- Name of the remote to which the push is being done # $2 -- URL to which the push is being done # # If pushing without using a named remote those arguments will be equal. # # Information about the commits which are being pushed is supplied as lines to # the standard input in the form: # # <local ref> <local sha1> <remote ref> <remote sha1> # # This sample shows how to prevent push of commits where the log message starts # with "WIP" (work in progress). remote="$1" url="$2" z40=0000000000000000000000000000000000000000 IFS=' ' while read local_ref local_sha remote_ref remote_sha do if [ "$local_sha" = $z40 ] then # Handle delete : else if [ "$remote_sha" = $z40 ] then # New branch, examine all commits range="$local_sha" else # Update to existing branch, examine new commits range="$remote_sha..$local_sha" fi # Check for WIP commit commit=`git rev-list -n 1 --grep '^WIP' "$range"` if [ -n "$commit" ] then echo "Found WIP commit in $local_ref, not pushing" exit 1 fi fi done exit 0 |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/hooks/pre-rebase.sample version [5885a56ab4].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
#!/bin/sh # # Copyright (c) 2006, 2008 Junio C Hamano # # The "pre-rebase" hook is run just before "git rebase" starts doing # its job, and can prevent the command from running by exiting with # non-zero status. # # The hook is called with the following parameters: # # $1 -- the upstream the series was forked from. # $2 -- the branch being rebased (or empty when rebasing the current branch). # # This sample shows how to prevent topic branches that are already # merged to 'next' branch from getting rebased, because allowing it # would result in rebasing already published history. publish=next basebranch="$1" if test "$#" = 2 then topic="refs/heads/$2" else topic=`git symbolic-ref HEAD` || exit 0 ;# we do not interrupt rebasing detached HEAD fi case "$topic" in refs/heads/??/*) ;; *) exit 0 ;# we do not interrupt others. ;; esac # Now we are dealing with a topic branch being rebased # on top of master. Is it OK to rebase it? # Does the topic really exist? git show-ref -q "$topic" || { echo >&2 "No such branch $topic" exit 1 } # Is topic fully merged to master? not_in_master=`git rev-list --pretty=oneline ^master "$topic"` if test -z "$not_in_master" then echo >&2 "$topic is fully merged to master; better remove it." exit 1 ;# we could allow it, but there is no point. fi # Is topic ever merged to next? If so you should not be rebasing it. only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` only_next_2=`git rev-list ^master ${publish} | sort` if test "$only_next_1" = "$only_next_2" then not_in_topic=`git rev-list "^$topic" master` if test -z "$not_in_topic" then echo >&2 "$topic is already up-to-date with master" exit 1 ;# we could allow it, but there is no point. else exit 0 fi else not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` /usr/bin/perl -e ' my $topic = $ARGV[0]; my $msg = "* $topic has commits already merged to public branch:\n"; my (%not_in_next) = map { /^([0-9a-f]+) /; ($1 => 1); } split(/\n/, $ARGV[1]); for my $elem (map { /^([0-9a-f]+) (.*)$/; [$1 => $2]; } split(/\n/, $ARGV[2])) { if (!exists $not_in_next{$elem->[0]}) { if ($msg) { print STDERR $msg; undef $msg; } print STDERR " $elem->[1]\n"; } } ' "$topic" "$not_in_next" "$not_in_master" exit 1 fi exit 0 ################################################################ This sample hook safeguards topic branches that have been published from being rewound. The workflow assumed here is: * Once a topic branch forks from "master", "master" is never merged into it again (either directly or indirectly). * Once a topic branch is fully cooked and merged into "master", it is deleted. If you need to build on top of it to correct earlier mistakes, a new topic branch is created by forking at the tip of the "master". This is not strictly necessary, but it makes it easier to keep your history simple. * Whenever you need to test or publish your changes to topic branches, merge them into "next" branch. The script, being an example, hardcodes the publish branch name to be "next", but it is trivial to make it configurable via $GIT_DIR/config mechanism. With this workflow, you would want to know: (1) ... if a topic branch has ever been merged to "next". Young topic branches can have stupid mistakes you would rather clean up before publishing, and things that have not been merged into other branches can be easily rebased without affecting other people. But once it is published, you would not want to rewind it. (2) ... if a topic branch has been fully merged to "master". Then you can delete it. More importantly, you should not build on top of it -- other people may already want to change things related to the topic as patches against your "master", so if you need further changes, it is better to fork the topic (perhaps with the same name) afresh from the tip of "master". Let's look at this example: o---o---o---o---o---o---o---o---o---o "next" / / / / / a---a---b A / / / / / / / / c---c---c---c B / / / / \ / / / / b---b C \ / / / / / \ / ---o---o---o---o---o---o---o---o---o---o---o "master" A, B and C are topic branches. * A has one fix since it was merged up to "next". * B has finished. It has been fully merged up to "master" and "next", and is ready to be deleted. * C has not merged to "next" at all. We would want to allow C to be rebased, refuse A, and encourage B to be deleted. To compute (1): git rev-list ^master ^topic next git rev-list ^master next if these match, topic has not merged in next at all. To compute (2): git rev-list master..topic if this is empty, it is fully merged to "master". |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/hooks/prepare-commit-msg.sample version [2b6275eda3].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#!/bin/sh # # An example hook script to prepare the commit log message. # Called by "git commit" with the name of the file that has the # commit message, followed by the description of the commit # message's source. The hook's purpose is to edit the commit # message file. If the hook fails with a non-zero status, # the commit is aborted. # # To enable this hook, rename this file to "prepare-commit-msg". # This hook includes three examples. The first comments out the # "Conflicts:" part of a merge commit. # # The second includes the output of "git diff --name-status -r" # into the message, just before the "git status" output. It is # commented because it doesn't cope with --amend or with squashed # commits. # # The third example adds a Signed-off-by line to the message, that can # still be edited. This is rarely a good idea. case "$2,$3" in merge,) /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; # ,|template,) # /usr/bin/perl -i.bak -pe ' # print "\n" . `git diff --cached --name-status -r` # if /^#/ && $first++ == 0' "$1" ;; *) ;; esac # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/hooks/update.sample version [39355a0759].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
#!/bin/sh # # An example hook script to blocks unannotated tags from entering. # Called by "git receive-pack" with arguments: refname sha1-old sha1-new # # To enable this hook, rename this file to "update". # # Config # ------ # hooks.allowunannotated # This boolean sets whether unannotated tags will be allowed into the # repository. By default they won't be. # hooks.allowdeletetag # This boolean sets whether deleting tags will be allowed in the # repository. By default they won't be. # hooks.allowmodifytag # This boolean sets whether a tag may be modified after creation. By default # it won't be. # hooks.allowdeletebranch # This boolean sets whether deleting branches will be allowed in the # repository. By default they won't be. # hooks.denycreatebranch # This boolean sets whether remotely creating branches will be denied # in the repository. By default this is allowed. # # --- Command line refname="$1" oldrev="$2" newrev="$3" # --- Safety check if [ -z "$GIT_DIR" ]; then echo "Don't run this script from the command line." >&2 echo " (if you want, you could supply GIT_DIR then run" >&2 echo " $0 <ref> <oldrev> <newrev>)" >&2 exit 1 fi if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then echo "usage: $0 <ref> <oldrev> <newrev>" >&2 exit 1 fi # --- Config allowunannotated=$(git config --bool hooks.allowunannotated) allowdeletebranch=$(git config --bool hooks.allowdeletebranch) denycreatebranch=$(git config --bool hooks.denycreatebranch) allowdeletetag=$(git config --bool hooks.allowdeletetag) allowmodifytag=$(git config --bool hooks.allowmodifytag) # check for no description projectdesc=$(sed -e '1q' "$GIT_DIR/description") case "$projectdesc" in "Unnamed repository"* | "") echo "*** Project description file hasn't been set" >&2 exit 1 ;; esac # --- Check types # if $newrev is 0000...0000, it's a commit to delete a ref. zero="0000000000000000000000000000000000000000" if [ "$newrev" = "$zero" ]; then newrev_type=delete else newrev_type=$(git cat-file -t $newrev) fi case "$refname","$newrev_type" in refs/tags/*,commit) # un-annotated tag short_refname=${refname##refs/tags/} if [ "$allowunannotated" != "true" ]; then echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 exit 1 fi ;; refs/tags/*,delete) # delete tag if [ "$allowdeletetag" != "true" ]; then echo "*** Deleting a tag is not allowed in this repository" >&2 exit 1 fi ;; refs/tags/*,tag) # annotated tag if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 then echo "*** Tag '$refname' already exists." >&2 echo "*** Modifying a tag is not allowed in this repository." >&2 exit 1 fi ;; refs/heads/*,commit) # branch if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then echo "*** Creating a branch is not allowed in this repository" >&2 exit 1 fi ;; refs/heads/*,delete) # delete branch if [ "$allowdeletebranch" != "true" ]; then echo "*** Deleting a branch is not allowed in this repository" >&2 exit 1 fi ;; refs/remotes/*,commit) # tracking branch ;; refs/remotes/*,delete) # delete tracking branch if [ "$allowdeletebranch" != "true" ]; then echo "*** Deleting a tracking branch is not allowed in this repository" >&2 exit 1 fi ;; *) # Anything else (is there anything else?) echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 exit 1 ;; esac # --- Finished exit 0 |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/index version [e8756a1baf].
cannot compute difference between binary files
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/info/exclude version [c879df015d].
> > > > > > |
1 2 3 4 5 6 |
# git ls-files --others --exclude-from=.git/info/exclude # Lines that start with '#' are comments. # For a project mostly in C, the following would be a good set of # exclude patterns (uncomment them if you want to use them): # *.[oa] # *~ |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/logs/HEAD version [b88dae22e6].
> > |
1 2 |
0000000000000000000000000000000000000000 eaa9bd05c684736620ba0aa89de29816ee118628 Martin Vahi <martin.vahi@softf1.com> 1512461959 +0200 clone: from https://github.com/Karasiq/nanoboard.git eaa9bd05c684736620ba0aa89de29816ee118628 020ae3c469c33e2d33ddab6411cc1b83d3d58166 Martin Vahi <martin.vahi@softf1.com> 1521653750 +0200 pull --all --recurse-submodules --force: Fast-forward |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/logs/refs/heads/master version [b88dae22e6].
> > |
1 2 |
0000000000000000000000000000000000000000 eaa9bd05c684736620ba0aa89de29816ee118628 Martin Vahi <martin.vahi@softf1.com> 1512461959 +0200 clone: from https://github.com/Karasiq/nanoboard.git eaa9bd05c684736620ba0aa89de29816ee118628 020ae3c469c33e2d33ddab6411cc1b83d3d58166 Martin Vahi <martin.vahi@softf1.com> 1521653750 +0200 pull --all --recurse-submodules --force: Fast-forward |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/logs/refs/remotes/origin/HEAD version [149bc70ffc].
> |
1 |
0000000000000000000000000000000000000000 eaa9bd05c684736620ba0aa89de29816ee118628 Martin Vahi <martin.vahi@softf1.com> 1512461959 +0200 clone: from https://github.com/Karasiq/nanoboard.git
|
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/logs/refs/remotes/origin/master version [9bbab0b7da].
> |
1 |
eaa9bd05c684736620ba0aa89de29816ee118628 020ae3c469c33e2d33ddab6411cc1b83d3d58166 ts2 <ts2@linux-0fiz.(none)> 1521653750 +0200 pull --all --recurse-submodules --force: fast-forward
|
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/objects/pack/pack-37adcc4d0ff940d37a85783a4e51a6409f1a11ac.idx version [41ce56438d].
cannot compute difference between binary files
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/objects/pack/pack-37adcc4d0ff940d37a85783a4e51a6409f1a11ac.pack version [f0a3e29251].
cannot compute difference between binary files
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/objects/pack/pack-fe6b0ba5600084ffbdda6101cc2162648e45780d.idx version [63ab72ef98].
cannot compute difference between binary files
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/objects/pack/pack-fe6b0ba5600084ffbdda6101cc2162648e45780d.pack version [57433aca69].
cannot compute difference between binary files
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/packed-refs version [daf0e0878b].
> > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# pack-refs with: peeled fully-peeled 2bd19ceac4d7c10db8ab121e27eea7eb32f28e45 refs/remotes/origin/captcha eaa9bd05c684736620ba0aa89de29816ee118628 refs/remotes/origin/master 0732b898d1d18c1a892a97377c0775adb6d01b12 refs/remotes/origin/new-format a72caa8b4e9a4ac02160f0ae6ceae0376034a12b refs/tags/v1.0.0 ^9e6a3dc4d9f66ebbd7f0c2fbb33465c2f2a51321 75d4548b3f9504b076a9fb562936090b00709569 refs/tags/v1.0.1 ^0a7bde79e47ccbad208d8a0032f8508bf0929147 cc5cf1f0f80a85fee0b21419a44153d6712aad1a refs/tags/v1.0.2 3dc3b7070817c2c04108255fab024ea1cdb8c163 refs/tags/v1.0.3 ^3aa25556e4ea515088459bbded3b527c33fd4826 b270d7cbd7b52c46a6569af5100e5d43f649401b refs/tags/v1.0.4 68e3436e4d9d49e674a2c3c8856f71338fa2988d refs/tags/v1.0.5 ^eb335d9f02187d4cb6b4ea67aa2250f54a2eb270 d9298ac824b4edec2e9e774d7ab6ccfb08f27e7b refs/tags/v1.0.5-M1 4bbd201d8c345fd44e0d578d7e25da5677a35d03 refs/tags/v1.0.6 ^3534d9e90d85858f09c8847794330d1dc78cbb9e 5c6df7234e7d71547aac26443b5fcc45a29a5543 refs/tags/v1.0.7 ^f3149f8605c5f20bfaa29991f4603cdc77813d9c 4f9421c300d520b52461f3c880bfcd99020edec8 refs/tags/v1.1.0 ^9628f0687a2c860adbdce42bc0e7f1f1ede0606b 0dd6978e8aaa4216351f20d5493d13561d16570c refs/tags/v1.2.0 ^7016f4bf24e08bd3729e9ca65add5b150d4ece9d |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/refs/heads/master version [291d62e8ac].
> |
1 |
020ae3c469c33e2d33ddab6411cc1b83d3d58166
|
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/refs/remotes/origin/HEAD version [d9427cda09].
> |
1 |
ref: refs/remotes/origin/master
|
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/refs/remotes/origin/master version [291d62e8ac].
> |
1 |
020ae3c469c33e2d33ddab6411cc1b83d3d58166
|
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/refs/tags/v1.3.0 version [5978efc2ec].
> |
1 |
b423e5a6ed750f2f5131edb7eb0bbe66617b8c30
|
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/refs/tags/v1.3.1 version [3581810989].
> |
1 |
efaedd598892e3118401c5e7c6258bb563651962
|
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.git/refs/tags/v1.3.2 version [291d62e8ac].
> |
1 |
020ae3c469c33e2d33ddab6411cc1b83d3d58166
|
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.gitignore version [4daa174306].
> > > |
1 2 3 |
.idea target *.bat |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/.travis.yml version [86e84b4e5a].
> > > > > > > > |
1 2 3 4 5 6 7 8 |
language: scala scala: - 2.11.8 jdk: - oraclejdk8 script: - sbt ++$TRAVIS_SCALA_VERSION compile nanoboard/test nanoboard-server/test sudo: false |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/LICENSE version [211af68d19].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/README.MD version [4376a475b9].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# karasiq-nanoboard [](https://travis-ci.org/Karasiq/nanoboard) [](https://maven-badges.herokuapp.com/maven-central/com.github.karasiq/nanoboard_2.12) Scala [nanoboard](https://github.com/nanoboard/nanoboard) implementation.  # [Original project description](https://github.com/nanoboard/nanoboard/releases) >What is Nanoboard: it is steganographic imageboard without centralized server or p2p: users share nanoposts by posting png-containers (with nanoposts hidden inside) on real imageboards (users negotiate which imageboard threads to use for posting containers in special Nanoboard thread). > >Nanoboard's goals are: speech of freedom, immortality and ownership of the imageboard. Nanoboard is able to use transport different from png-containers. One such alternative transport is: [BitMessage transport](https://github.com/nanoboard/nanoboard-bittransport). See also: * https://wiki.1chan.ca/Наноборда * https://github.com/RosinSmoke/nanoboard/blob/1a0d72cb155f8bee0e59cd16feb4c6a24d101436/README.md * http://blog.andersen.im/2014/11/hiding-your-bits-in-the-bytes/ # Features * Accepted containers management * Automatic containers download * Bootstrap typography BB-codes * Code highlighting * External images expansion * File sharing * Fractal music * Highly optimized POW algorithm * HTML5 video player * Linked posts preview * Live update (WebSockets) * Localized interface * Markdown formatting support * Message live preview * Option to verify existing posts * Random container generation * SVG images support * Six themes # How to use * [Download latest release](https://github.com/Karasiq/nanoboard/releases) * Install and launch application using Windows installer * Or extract zip package and enter `./bin/nanoboard` in console * Open <http://localhost:7347/> in your browser |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/build.sbt version [63560b8ce5].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
import sbtassembly.Plugin.AssemblyKeys._ lazy val commonSettings = Seq( organization := "com.github.karasiq", version := "1.3.2", isSnapshot := version.value.endsWith("SNAPSHOT"), scalaVersion := "2.12.4" ) lazy val librarySettings = Seq( name := "nanoboard", libraryDependencies ++= { val akkaV = "2.5.10" Seq( "com.typesafe.akka" %% "akka-actor" % akkaV, "org.scalatest" %% "scalatest" % "3.0.5" % "test", "com.typesafe.akka" %% "akka-stream" % akkaV, "com.typesafe.akka" %% "akka-http" % "10.0.11", "commons-codec" % "commons-codec" % "1.11", "commons-io" % "commons-io" % "2.6", "org.bouncycastle" % "bcprov-jdk15on" % "1.59", "net.i2p.crypto" % "eddsa" % "0.2.0", "org.jsoup" % "jsoup" % "1.11.2", "com.typesafe.play" %% "play-json" % "2.6.7", "com.lihaoyi" %% "scalatags" % "0.6.7", "com.upokecenter" % "cbor" % "3.0.3" ) }, publishMavenStyle := true, publishTo := { val nexus = "https://oss.sonatype.org/" if (isSnapshot.value) Some("snapshots" at nexus + "content/repositories/snapshots") else Some("releases" at nexus + "service/local/staging/deploy/maven2") }, publishArtifact in Test := false, pomIncludeRepository := { _ ⇒ false }, licenses := Seq("Apache License, Version 2.0" → url("http://opensource.org/licenses/Apache-2.0")), homepage := Some(url(s"https://github.com/Karasiq/${name.value}")), pomExtra := <scm> <url>git@github.com:Karasiq/{name.value}.git</url> <connection>scm:git:git@github.com:Karasiq/{name.value}.git</connection> </scm> <developers> <developer> <id>karasiq</id> <name>Piston Karasiq</name> <url>https://github.com/Karasiq</url> </developer> </developers> ) lazy val backendSettings = Seq( name := "nanoboard-server", libraryDependencies ++= Seq( "com.typesafe.slick" %% "slick" % "3.2.1", "com.h2database" % "h2" % "1.4.196", "org.slf4j" % "slf4j-nop" % "1.7.25", "org.scalatest" %% "scalatest" % "3.0.5" % "test" ), mainClass in Compile := Some("com.karasiq.nanoboard.server.Main"), mainClass in assembly := (mainClass in Compile).value, jarName in assembly := "nanoboard-server.jar", test in assembly := {}, mappings in Universal := { val universalMappings = (mappings in Universal).value val fatJar = (assembly in Compile).value val filtered = universalMappings.filterNot(_._2.endsWith(".jar")) filtered :+ (fatJar → ("lib/" + fatJar.getName)) }, scriptClasspath := Seq((jarName in assembly).value), scalaJsBundlerCompile in Compile <<= (scalaJsBundlerCompile in Compile).dependsOn(fullOptJS in Compile in frontend), scalaJsBundlerAssets in Compile += { import com.karasiq.scalajsbundler.dsl._ val bootstrap = github("twbs", "bootstrap", "v3.3.6") / "dist" val fontAwesome = github("FortAwesome", "Font-Awesome", "v4.5.0") val videoJs = github("videojs", "video.js", "v5.8.0") / "dist" val highlightJs = "org.webjars" % "highlightjs" % "9.2.0" val jsDeps = Seq( // jQuery Script from url("https://code.jquery.com/jquery-2.1.4.min.js"), // Bootstrap Style from bootstrap / "css" / "bootstrap.css", Script from bootstrap / "js" / "bootstrap.js", // Font Awesome Style from fontAwesome / "css" / "font-awesome.css", // Video.js Script from videoJs / "video.min.js", Style from videoJs / "video-js.min.css", Static("video-js.swf") from videoJs / "video-js.swf", // Plugins Script from github("eXon", "videojs-youtube", "v2.0.8") / "dist" / "Youtube.min.js", // Noty.js Script from github("needim", "noty", "v2.3.8") / "js" / "noty" / "packaged" / "jquery.noty.packaged.min.js", // Moment.js Script from github("moment", "moment", "2.12.0") / "min" / "moment-with-locales.min.js", // Marked Script from "org.webjars.bower" % "marked" % "0.3.5" / "marked.min.js", // Tab Override Script from github("wjbryant", "taboverride", "4.0.3") / "build" / "output" / "taboverride.min.js", // Highlight.js Script from highlightJs / "highlight.min.js", Style from highlightJs / s"styles/${NanoboardAssets.highlightJsStyle}.css" ) val highlightJsLanguages = for (lang ← NanoboardAssets.highlightJsLanguages) yield Script from highlightJs / s"languages/$lang.min.js" val staticDir = (baseDirectory in frontend)(_ / "files").value val staticFiles = Seq( Html from NanoboardAssets.index, Style from NanoboardAssets.style, Script from staticDir / "img2base64.js", Script from staticDir / "modernizr.js", Image("favicon.ico") from staticDir / "favicon.ico", Image("img/muon_bg.jpg") from staticDir / "muon_bg.jpg", Image("img/muon_posts.jpg") from staticDir / "muon_posts.jpg", Image("img/muon_inputs.jpg") from staticDir / "muon_inputs.jpg" ) val fonts = (fontAwesome / "fonts" / "fontawesome-webfont").fonts() ++ (videoJs / "font" / "VideoJS").fonts(dir = "font", extensions = Seq("eot", "svg", "ttf", "woff")) Bundle("index", jsDeps, highlightJsLanguages, staticFiles, fonts, scalaJsApplication(frontend, launcher = false).value) } ) lazy val frontendSettings = Seq( scalaJSUseMainModuleInitializer := true, name := "nanoboard-frontend", resolvers += Resolver.sonatypeRepo("snapshots"), libraryDependencies ++= Seq( "com.chuusai" %%% "shapeless" % "2.3.3", "org.parboiled" %%% "parboiled" % "2.1.4", "com.github.karasiq" %%% "scalajs-bootstrap" % "2.3.1", "com.github.karasiq" %%% "scalajs-videojs" % "1.0.5", "com.github.karasiq" %%% "scalajs-marked" % "1.0.2", "ru.pavkin" %%% "scala-js-momentjs" % "0.9.1", "com.lihaoyi" %%% "scalatags" % "0.6.7" ) ) lazy val shared = crossProject.in(file("shared")) .settings(commonSettings:_*) .settings( name := "nanoboard-shared", libraryDependencies += "io.suzaku" %%% "boopickle" % "1.2.6" ) lazy val sharedJVM = shared.jvm lazy val sharedJS = shared.js lazy val library = Project("nanoboard", file("library")) .settings(commonSettings, librarySettings) lazy val backend = Project("nanoboard-server", file(".")) .dependsOn(library, sharedJVM) .settings(assemblySettings, commonSettings, backendSettings) .enablePlugins(ScalaJSBundlerPlugin, JavaAppPackaging) lazy val frontend = Project("nanoboard-frontend", file("frontend")) .dependsOn(sharedJS) .settings(commonSettings, frontendSettings) .enablePlugins(ScalaJSPlugin) |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/files/favicon.ico version [bdfad40b11].
cannot compute difference between binary files
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/files/img2base64.js version [21efdb6bee].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
var img2base64 = { sharpen: function (ctx, w, h, mix) { var weights = [0, -1, 0, -1, 5, -1, 0, -1, 0], katet = Math.round(Math.sqrt(weights.length)), half = (katet * 0.5) | 0, dstData = ctx.createImageData(w, h), dstBuff = dstData.data, srcBuff = ctx.getImageData(0, 0, w, h).data, y = h; while (y--) { x = w; while (x--) { var sy = y, sx = x, dstOff = (y * w + x) * 4, r = 0, g = 0, b = 0, a = 0; for (var cy = 0; cy < katet; cy++) { for (var cx = 0; cx < katet; cx++) { var scy = sy + cy - half; var scx = sx + cx - half; if (scy >= 0 && scy < h && scx >= 0 && scx < w) { var srcOff = (scy * w + scx) * 4; var wt = weights[cy * katet + cx]; r += srcBuff[srcOff] * wt; g += srcBuff[srcOff + 1] * wt; b += srcBuff[srcOff + 2] * wt; a += srcBuff[srcOff + 3] * wt; } } } dstBuff[dstOff] = r * mix + srcBuff[dstOff] * (1 - mix); dstBuff[dstOff + 1] = g * mix + srcBuff[dstOff + 1] * (1 - mix); dstBuff[dstOff + 2] = b * mix + srcBuff[dstOff + 2] * (1 - mix) dstBuff[dstOff + 3] = srcBuff[dstOff + 3]; } } ctx.putImageData(dstData, 0, 0); }, drawImage: function (file, compress, imgType, imageScale, quality, sharpness, success, error) { var reader = new FileReader(); reader.onerror = error; reader.onloadend = function () { var res = reader.result; if (!compress) { success(res); return; } var canvas = document.createElement("canvas"); var ctx = canvas.getContext("2d"); img = new Image(); img.onerror = error; img.onload = function () { canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, img.width, img.height); img2base64.sharpen(ctx, img.width, img.height, sharpness / 100.0); var scale = 1.0 / (imageScale / 100.0); var shr = new Image(); shr.onerror = error; shr.onload = function () { canvas.width = img.width / scale; canvas.height = img.height / scale; ctx.drawImage(shr, 0, 0, img.width, img.height, 0, 0, img.width / scale, img.height / scale); success(canvas.toDataURL(imgType, quality / 100.0)); }; shr.src = canvas.toDataURL(); }; img.src = res; }; reader.readAsDataURL(file); } }; |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/files/modernizr.js version [6a3b0d2dd6].
> > > |
1 2 3 |
/*! modernizr 3.3.1 (Custom Build) | MIT * * http://modernizr.com/download/?-webp-setclasses !*/ !function(e,n,A){function o(e,n){return typeof e===n}function t(){var e,n,A,t,a,i,l;for(var f in r)if(r.hasOwnProperty(f)){if(e=[],n=r[f],n.name&&(e.push(n.name.toLowerCase()),n.options&&n.options.aliases&&n.options.aliases.length))for(A=0;A<n.options.aliases.length;A++)e.push(n.options.aliases[A].toLowerCase());for(t=o(n.fn,"function")?n.fn():n.fn,a=0;a<e.length;a++)i=e[a],l=i.split("."),1===l.length?Modernizr[l[0]]=t:(!Modernizr[l[0]]||Modernizr[l[0]]instanceof Boolean||(Modernizr[l[0]]=new Boolean(Modernizr[l[0]])),Modernizr[l[0]][l[1]]=t),s.push((t?"":"no-")+l.join("-"))}}function a(e){var n=u.className,A=Modernizr._config.classPrefix||"";if(c&&(n=n.baseVal),Modernizr._config.enableJSClass){var o=new RegExp("(^|\\s)"+A+"no-js(\\s|$)");n=n.replace(o,"$1"+A+"js$2")}Modernizr._config.enableClasses&&(n+=" "+A+e.join(" "+A),c?u.className.baseVal=n:u.className=n)}function i(e,n){if("object"==typeof e)for(var A in e)f(e,A)&&i(A,e[A]);else{e=e.toLowerCase();var o=e.split("."),t=Modernizr[o[0]];if(2==o.length&&(t=t[o[1]]),"undefined"!=typeof t)return Modernizr;n="function"==typeof n?n():n,1==o.length?Modernizr[o[0]]=n:(!Modernizr[o[0]]||Modernizr[o[0]]instanceof Boolean||(Modernizr[o[0]]=new Boolean(Modernizr[o[0]])),Modernizr[o[0]][o[1]]=n),a([(n&&0!=n?"":"no-")+o.join("-")]),Modernizr._trigger(e,n)}return Modernizr}var s=[],r=[],l={_version:"3.3.1",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,n){var A=this;setTimeout(function(){n(A[e])},0)},addTest:function(e,n,A){r.push({name:e,fn:n,options:A})},addAsyncTest:function(e){r.push({name:null,fn:e})}},Modernizr=function(){};Modernizr.prototype=l,Modernizr=new Modernizr;var f,u=n.documentElement,c="svg"===u.nodeName.toLowerCase();!function(){var e={}.hasOwnProperty;f=o(e,"undefined")||o(e.call,"undefined")?function(e,n){return n in e&&o(e.constructor.prototype[n],"undefined")}:function(n,A){return e.call(n,A)}}(),l._l={},l.on=function(e,n){this._l[e]||(this._l[e]=[]),this._l[e].push(n),Modernizr.hasOwnProperty(e)&&setTimeout(function(){Modernizr._trigger(e,Modernizr[e])},0)},l._trigger=function(e,n){if(this._l[e]){var A=this._l[e];setTimeout(function(){var e,o;for(e=0;e<A.length;e++)(o=A[e])(n)},0),delete this._l[e]}},Modernizr._q.push(function(){l.addTest=i}),Modernizr.addAsyncTest(function(){function e(e,n,A){function o(n){var o=n&&"load"===n.type?1==t.width:!1,a="webp"===e;i(e,a?new Boolean(o):o),A&&A(n)}var t=new Image;t.onerror=o,t.onload=o,t.src=n}var n=[{uri:"data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAwA0JaQAA3AA/vuUAAA=",name:"webp"},{uri:"data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAABBxAR/Q9ERP8DAABWUDggGAAAADABAJ0BKgEAAQADADQlpAADcAD++/1QAA==",name:"webp.alpha"},{uri:"data:image/webp;base64,UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA",name:"webp.animation"},{uri:"data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA=",name:"webp.lossless"}],A=n.shift();e(A.name,A.uri,function(A){if(A&&"load"===A.type)for(var o=0;o<n.length;o++)e(n[o].name,n[o].uri)})}),t(),a(s),delete l.addTest,delete l.addAsyncTest;for(var p=0;p<Modernizr._q.length;p++)Modernizr._q[p]();e.Modernizr=Modernizr}(window,document); |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/files/muon_bg.jpg version [6feb7231e4].
cannot compute difference between binary files
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/files/muon_inputs.jpg version [1807ecef2c].
cannot compute difference between binary files
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/files/muon_posts.jpg version [abe1959912].
cannot compute difference between binary files
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/Icons.scala version [54c2b45b13].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
package com.karasiq.nanoboard.frontend import com.karasiq.bootstrap.Bootstrap.default._ /** * Nanoboard application icons */ object Icons { @inline private[this] def fa(name: String): IconModifier = name.fontAwesome(FontAwesome.fixedWidth) lazy val thread = fa("server") lazy val settings = fa("cogs") lazy val container = fa("globe") lazy val previous = fa("angle-double-left") lazy val next = fa("angle-double-right") lazy val removeContainer = fa("ban") lazy val batchDelete = fa("eraser") lazy val clearDeleted = fa("eye-slash") lazy val preferences = fa("wrench") lazy val control = fa("warning") lazy val containers = fa("archive") lazy val answers = fa("envelope-o") lazy val recent = fa("newspaper-o") lazy val categories = fa("sitemap") lazy val link = fa("link") lazy val parent = fa("level-up") lazy val delete = fa("trash-o") lazy val enqueue = fa("sign-in") lazy val dequeue = fa("sign-out") lazy val image = fa("file-image-o") lazy val video = fa("play-circle") lazy val file = fa("file-archive-o") lazy val submit = fa("mail-forward") lazy val reply = fa("reply") lazy val source = fa("file-text-o") lazy val music = fa("music") lazy val verify = fa("key") } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/NanoboardContext.scala version [cf63fbfde0].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
package com.karasiq.nanoboard.frontend import rx._ import com.karasiq.nanoboard.frontend.utils.RxLocation sealed trait NanoboardContext sealed trait NanoboardContextWithOffset extends NanoboardContext { def offset: Int def withOffset(newOffset: Int): NanoboardContextWithOffset } object NanoboardContext { case object Categories extends NanoboardContext case class Thread(hash: String, offset: Int = 0) extends NanoboardContextWithOffset { def withOffset(newOffset: Int) = copy(offset = newOffset) } case class Recent(offset: Int = 0) extends NanoboardContextWithOffset { def withOffset(newOffset: Int) = copy(offset = newOffset) } case class Pending(offset: Int = 0) extends NanoboardContextWithOffset { def withOffset(newOffset: Int) = copy(offset = newOffset) } //noinspection VariablePatternShadow // Simple single page app router def fromLocation()(implicit ctx: Ctx.Owner): Var[NanoboardContext] = { val location = RxLocation() val sha256 = "([a-fA-F0-9]{32})".r val sha256WithOffset = "([a-fA-F0-9]{32})/(\\d+)".r val onlyOffset = "(\\d+)".r val pendingOffset = "pending/(\\d+)".r val result = Var[NanoboardContext](NanoboardContext.Categories) location.hash.foreach { hash ⇒ result() = hash match { case Some(sha256(hash)) ⇒ NanoboardContext.Thread(hash) case Some(sha256WithOffset(hash, offset)) ⇒ NanoboardContext.Thread(hash, offset.toInt) case Some(onlyOffset(offset)) ⇒ NanoboardContext.Recent(offset.toInt) case Some("pending") ⇒ NanoboardContext.Pending() case Some(pendingOffset(offset)) ⇒ NanoboardContext.Pending(offset.toInt) case _ ⇒ NanoboardContext.Categories } } result.triggerLater { location.hash() = result.now match { case NanoboardContext.Categories ⇒ None case NanoboardContext.Thread(hash, 0) ⇒ Some(s"$hash") case NanoboardContext.Thread(hash, offset) ⇒ Some(s"$hash/$offset") case NanoboardContext.Recent(offset) ⇒ Some(offset.toString) case NanoboardContext.Pending(0) ⇒ Some("pending") case NanoboardContext.Pending(offset) ⇒ Some(s"pending/$offset") } } result } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/NanoboardController.scala version [5ce3154fbd].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
package com.karasiq.nanoboard.frontend import scala.language.postfixOps import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue import org.scalajs.dom._ import rx._ import com.karasiq.bootstrap.Bootstrap.default._ import scalaTags.all._ import com.karasiq.bootstrap.Bootstrap.default._ import com.karasiq.nanoboard.api.NanoboardMessageData import com.karasiq.nanoboard.frontend.api.streaming.NanoboardMessageStream import com.karasiq.nanoboard.frontend.components._ import com.karasiq.nanoboard.frontend.locales.BoardLocale import com.karasiq.nanoboard.frontend.styles.BoardStyle import com.karasiq.nanoboard.frontend.utils.Mouse import com.karasiq.nanoboard.streaming.{NanoboardEvent, NanoboardSubscription} import com.karasiq.nanoboard.streaming.NanoboardSubscription.{PostHashes, Unfiltered} object NanoboardController { def apply(): NanoboardController = { new NanoboardController() } } final class NanoboardController { private implicit def controller: NanoboardController = this private val styleSelector = BoardStyle.selector val style: BoardStyle = styleSelector.style.now val locale = BoardLocale.fromBrowserLanguage() private val thread = ThreadContainer(NanoboardContext.fromLocation(), postsPerPage = 20) private val settingsPanel = SettingsPanel() private val pngGenerationPanel = PngGenerationPanel() private val title = ThreadPageTitle(thread.model) private val styleField = FormInput.simpleSelect(locale.style, BoardStyle.styles.map(_.toString):_*) styleField.selected() = Seq(style.toString) styleField.selected.map(_.head).foreach { style ⇒ styleSelector.style() = BoardStyle.fromString(style) } private val navigationBar = NavigationBar() .withBrand("Nanoboard", onclick := Callback.onClick { _ ⇒ setContext(NanoboardContext.Categories) }) .withTabs( NavigationTab(locale.nanoboard, "thread", Icons.thread, GridSystem.containerFluid(thread)), NavigationTab(locale.settings, "server-settings", Icons.settings, GridSystem.container( GridSystem.mkRow(styleField.renderTag(style.input)), GridSystem.mkRow(settingsPanel) )), NavigationTab(locale.containerGeneration, "png-gen", Icons.container, GridSystem.container(GridSystem.mkRow(pngGenerationPanel))) ) .withStyles(NavigationBarStyle.staticTop, NavigationBarStyle.inverse) .withContentContainer(md ⇒ div(md)) .build() private val messageChannel = NanoboardMessageStream { case NanoboardEvent.PostAdded(message) ⇒ // Notifications.info(s"New message: ${message.text}", Layout.topRight) addPost(message) case NanoboardEvent.PostDeleted(hash) ⇒ // Notifications.warning(s"Post was deleted: $hash", Layout.topRight) deleteSingle(NanoboardMessageData(None, None, hash, "", 0)) case NanoboardEvent.PostVerified(message) ⇒ addPending(message) } def initialize(): Unit = { document.head.appendChild(controller.title.renderTag().render) Seq[Modifier](navigationBar, style.body, controller.styleSelector.renderTag(id := "nanoboard-style")) .foreach(_.applyTo(document.body)) } def updateCategories(): Unit = { thread.model.updateCategories() } def updatePosts(): Unit = { Seq(thread.model, pngGenerationPanel.model).foreach(_.updatePosts()) } def showPost(hash: String): Unit = { if (!Mouse.scroll(s"#post-$hash")) { setContext(NanoboardContext.Thread(hash)) } } def setContext(context: NanoboardContext): Unit = { thread.context() = context navigationBar.selectTab("thread") } def isPending(hash: String): Rx[Boolean] = { pngGenerationPanel.model.posts.map(_.exists(_.hash == hash)) } def addPending(post: NanoboardMessageData): Unit = { pngGenerationPanel.model.addPost(post) } def deletePending(post: NanoboardMessageData): Unit = { pngGenerationPanel.model.deleteSingle(post) } def addPost(post: NanoboardMessageData): Unit = { thread.model.addPost(post) } def deleteSingle(post: NanoboardMessageData): Unit = { Seq(thread.model, pngGenerationPanel.model).foreach(_.deleteSingle(post)) } private val messageChannelContext = Rx[NanoboardSubscription] { thread.model.context() match { case NanoboardContext.Recent(0) | NanoboardContext.Pending(0) ⇒ // Accept all posts Unfiltered case context ⇒ PostHashes(thread.model.posts().map(_.hash).toSet ++ thread.model.categories().map(_.hash).toSet ++ Some(context).collect { case NanoboardContext.Thread(hash, _) ⇒ hash }) } } messageChannelContext.foreach { context ⇒ messageChannel.setContext(context) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/NanoboardFrontend.scala version [024f582129].
> > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package com.karasiq.nanoboard.frontend import scala.language.postfixOps import moment.Moment import org.scalajs.jquery.jQuery import com.karasiq.nanoboard.frontend.locales.BoardLocale import com.karasiq.taboverridejs.TabOverride object NanoboardFrontend { def main(args: Array[String]): Unit = { jQuery(() ⇒ { TabOverride.tabSize(2) Moment.locale(BoardLocale.browserLanguage) NanoboardController().initialize() }) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/api/BinaryMarshaller.scala version [dcc11c90de].
> > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package com.karasiq.nanoboard.frontend.api import java.nio.ByteBuffer import scala.scalajs.js.typedarray.{ArrayBuffer, TypedArrayBuffer} import boopickle.Default._ private[api] object BinaryMarshaller { def responseType: String = "arraybuffer" def write[T: Pickler](value: T): ByteBuffer = { Pickle.intoBytes(value) } def read[T: Pickler](value: Any): T = { Unpickle[T].fromBytes(TypedArrayBuffer.wrap(value.asInstanceOf[ArrayBuffer])) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/api/NanoboardApi.scala version [79a4fedb0b].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
package com.karasiq.nanoboard.frontend.api import scala.concurrent.Future import scala.language.postfixOps import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue import boopickle.Default._ import org.scalajs.dom.{console, Blob} import org.scalajs.dom.ext.Ajax import org.scalajs.dom.raw.XMLHttpRequest import com.karasiq.nanoboard.api._ /** * Nanoboard REST API */ object NanoboardApi { private val marshaller = BinaryMarshaller private def readResponse[T: Pickler](response: XMLHttpRequest): T = { if (response.status == 200) { marshaller.read[T](response.response) } else { val message = s"Nanoboard API error: ${response.status} ${response.statusText}" console.error(message) throw new IllegalArgumentException(message) } } def categories(): Future[Vector[NanoboardMessageData]] = { Ajax.get("/categories", responseType = marshaller.responseType) .map(readResponse[Vector[NanoboardMessageData]]) } def post(hash: String): Future[Option[NanoboardMessageData]] = { Ajax.get(s"/post/$hash", responseType = marshaller.responseType) .map(readResponse[Option[NanoboardMessageData]]) } def thread(hash: String, offset: Long, count: Long): Future[Vector[NanoboardMessageData]] = { Ajax.get(s"/posts/$hash?offset=$offset&count=$count", responseType = marshaller.responseType) .map(readResponse[Vector[NanoboardMessageData]]) } def addReply(hash: String, message: String): Future[NanoboardMessageData] = { Ajax.post(s"/post", marshaller.write(NanoboardReply(hash, message)), responseType = marshaller.responseType) .map(readResponse[NanoboardMessageData]) } def markAsPending(hash: String): Future[Unit] = { Ajax.put(s"/pending/$hash").map(_ ⇒ ()) } def markAsNotPending(hash: String): Future[Unit] = { Ajax.delete(s"/pending/$hash").map(_ ⇒ ()) } def delete(hash: String): Future[Seq[String]] = { Ajax.delete(s"/post/$hash", responseType = marshaller.responseType) .map(readResponse[Seq[String]]) } def clearDeleted(): Future[Int] = { Ajax.delete("/deleted", responseType = marshaller.responseType) .map(readResponse[Int]) } def delete(offset: Int, count: Int): Future[Seq[String]] = { Ajax.delete(s"/posts?offset=$offset&count=$count", responseType = marshaller.responseType) .map(readResponse[Seq[String]]) } def places(): Future[Seq[String]] = { Ajax.get("/places", responseType = marshaller.responseType) .map(readResponse[Seq[String]]) } def setPlaces(newList: Seq[String]): Future[Unit] = { Ajax.put("/places", marshaller.write(newList)) .map(_ ⇒ ()) } def setCategories(newList: Seq[NanoboardCategory]): Future[Unit] = { Ajax.put("/categories", marshaller.write(newList)) .map(_ ⇒ ()) } def pending(offset: Long, count: Long): Future[Vector[NanoboardMessageData]] = { Ajax.get(s"/pending?offset=$offset&count=$count", responseType = marshaller.responseType) .map(readResponse[Vector[NanoboardMessageData]]) } def recent(offset: Long, count: Long): Future[Vector[NanoboardMessageData]] = { Ajax.get(s"/posts?offset=$offset&count=$count", responseType = marshaller.responseType) .map(readResponse[Vector[NanoboardMessageData]]) } def generateContainer(pending: Int, random: Int, format: String, container: Ajax.InputData): Future[Blob] = { Ajax.post(s"/container?pending=$pending&random=$random&format=$format", container, responseType = "blob") .map { r ⇒ if (r.status == 200) { r.response.asInstanceOf[Blob] } else { throw new IllegalArgumentException(s"${r.status} ${r.statusText}") } } } def generateAttachment(format: String, size: Int, quality: Int, container: Ajax.InputData): Future[String] = { Ajax.post(s"/attachment?format=$format&size=$size&quality=$quality", container) .map(_.responseText) } def containers(offset: Long, count: Long): Future[Vector[NanoboardContainer]] = { Ajax.get(s"/containers?offset=$offset&count=$count", responseType = marshaller.responseType) .map(readResponse[Vector[NanoboardContainer]]) } def clearContainer(id: Long): Future[Vector[String]] = { Ajax.delete(s"/posts?container=$id", responseType = marshaller.responseType) .map(readResponse[Vector[String]]) } def requestVerification(hash: String): Future[NanoboardCaptchaRequest] = { Ajax.get(s"/verify/$hash", responseType = marshaller.responseType) .map(readResponse[NanoboardCaptchaRequest]) } def verifyPost(request: NanoboardCaptchaRequest, answer: String): Future[NanoboardMessageData] = { Ajax.post(s"/verify", marshaller.write(NanoboardCaptchaAnswer(request, answer)), responseType = marshaller.responseType) .map(readResponse[NanoboardMessageData]) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/api/streaming/NanoboardMessageStream.scala version [78920887fb].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
package com.karasiq.nanoboard.frontend.api.streaming import java.nio.ByteBuffer import scala.scalajs.js.typedarray.{ArrayBuffer, TypedArrayBuffer} import scala.scalajs.js.typedarray.TypedArrayBufferOps._ import boopickle.Default._ import org.scalajs.dom.window import org.scalajs.dom.raw._ import com.karasiq.nanoboard.frontend.NanoboardController import com.karasiq.nanoboard.frontend.api.BinaryMarshaller import com.karasiq.nanoboard.frontend.utils.Notifications import com.karasiq.nanoboard.frontend.utils.Notifications.Layout import com.karasiq.nanoboard.streaming.{NanoboardEvent, NanoboardEventSeq, NanoboardSubscription} import com.karasiq.nanoboard.streaming.NanoboardSubscription.Unfiltered object NanoboardMessageStream { def apply(f: PartialFunction[NanoboardEvent, Unit])(implicit controller: NanoboardController): NanoboardMessageStream = { new NanoboardMessageStream(f) } private[api] def asArrayBuffer(data: ByteBuffer): ArrayBuffer = { if (data.hasTypedArray()) { data.typedArray().subarray(data.position, data.limit).asInstanceOf[ArrayBuffer] } else { val tempBuffer = ByteBuffer.allocateDirect(data.remaining) val origPosition = data.position tempBuffer.put(data) data.position(origPosition) tempBuffer.typedArray().asInstanceOf[ArrayBuffer] } } private[api] def asEventSeq(response: Any): NanoboardEventSeq = { Unpickle[NanoboardEventSeq].fromBytes(TypedArrayBuffer.wrap(response.asInstanceOf[ArrayBuffer])) } } // WebSocket wrapper final class NanoboardMessageStream(f: PartialFunction[NanoboardEvent, Unit])(implicit controller: NanoboardController) { private var webSocket: Option[WebSocket] = None private var last: NanoboardSubscription = Unfiltered private var lastSet = false def setContext(context: NanoboardSubscription): Unit = { if (!lastSet || context != last) { webSocket.foreach { webSocket ⇒ val buffer = NanoboardMessageStream.asArrayBuffer(BinaryMarshaller.write(context)) webSocket.send(buffer) } } last = context lastSet = webSocket.isDefined } private def initWebSocket(): Unit = { val webSocket = new WebSocket(s"ws://${window.location.host}/live") webSocket.binaryType = "arraybuffer" webSocket.onmessage = { (m: MessageEvent) ⇒ val eventSeq = NanoboardMessageStream.asEventSeq(m.data) eventSeq.events .filter(f.isDefinedAt) .foreach(f(_)) } webSocket.onclose = { (e: CloseEvent) ⇒ this.webSocket = None Notifications.warning(s"${controller.locale.webSocketError}: ${e.code} ${e.reason}", Layout.topRight) window.setTimeout(() ⇒ initWebSocket(), 3000) } webSocket.onopen = { (e: Event) ⇒ this.lastSet = false this.webSocket = Some(webSocket) setContext(last) } } initWebSocket() } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/components/ContainersPanel.scala version [ea8801237f].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
package com.karasiq.nanoboard.frontend.components import scala.language.postfixOps import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue import scala.util.{Failure, Success} import moment.Moment import rx._ import com.karasiq.bootstrap.Bootstrap.default._ import scalaTags.all._ import com.karasiq.nanoboard.api.NanoboardContainer import com.karasiq.nanoboard.frontend.{Icons, NanoboardController} import com.karasiq.nanoboard.frontend.api.NanoboardApi import com.karasiq.nanoboard.frontend.utils.Notifications import com.karasiq.nanoboard.frontend.utils.Notifications.Layout private[components] object ContainersPanel { def apply(perPage: Int)(implicit controller: NanoboardController): ContainersPanel = { new ContainersPanel(perPage) } } private[components] final class ContainersPanel(perPage: Int)(implicit controller: NanoboardController) extends BootstrapHtmlComponent { import controller.locale val currentOffset = Var(0) val containers = Var(Vector.empty[NanoboardContainer]) def update(): Unit = { NanoboardApi.containers(currentOffset.now, perPage).foreach { cs ⇒ containers() = cs } } currentOffset.foreach(_ ⇒ update()) def renderTag(md: Modifier*) = { def isEmpty(c: NanoboardContainer): Modifier = if (c.posts == 0) Seq(textDecoration.`line-through`, color.gray) else () def dateTime(c: NanoboardContainer): Modifier = Moment(c.time.toDouble).format("YYYY, MMMM Do, HH:mm") var loading = false div(containers.map { cs ⇒ div( GridSystem.mkRow( ButtonGroup(ButtonGroupSize.extraSmall, Button(ButtonStyle.danger)( Icons.previous, locale.fromTo(math.max(0, currentOffset.now - perPage), currentOffset.now), onclick := Callback.onClick { _ ⇒ currentOffset() = math.max(0, currentOffset.now - perPage) } ), Button(ButtonStyle.success)( locale.fromTo(currentOffset.now + containers.now.length, currentOffset.now + containers.now.length + perPage), Icons.next, onclick := Callback.onClick { _ ⇒ currentOffset() = currentOffset.now + containers.now.length } ) ) ), for (c ← cs) yield GridSystem.mkRow(isEmpty(c), a(href := "#", Icons.removeContainer, onclick := Callback.onClick { _ ⇒ if (!loading) { Notifications.confirmation(locale.batchDeleteConfirmation(c.posts), Layout.topLeft) { loading = true NanoboardApi.clearContainer(c.id).onComplete { case Success(_) ⇒ loading = false this.update() controller.updatePosts() controller.updateCategories() Notifications.success(locale.batchDeleteSuccess(c.posts), Layout.topRight) case Failure(exc) ⇒ loading = false Notifications.error(exc)(locale.batchDeleteError, Layout.topRight) } } } }), dateTime(c), " ", s"${locale.container(c)} (", a(isEmpty(c), href := c.url, c.url, target := "_blank"), ")") ) }) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/components/PngGenerationPanel.scala version [06a3bdaa78].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
package com.karasiq.nanoboard.frontend.components import scala.concurrent.ExecutionContext import scala.scalajs.js import scala.util.{Failure, Success} import org.scalajs.dom.Blob import org.scalajs.dom.html.Input import rx._ import scalatags.JsDom.all._ import com.karasiq.bootstrap.Bootstrap.default._ import com.karasiq.nanoboard.frontend.{NanoboardContext, NanoboardController} import com.karasiq.nanoboard.frontend.api.NanoboardApi import com.karasiq.nanoboard.frontend.components.post.NanoboardPost import com.karasiq.nanoboard.frontend.model.ThreadModel import com.karasiq.nanoboard.frontend.utils.{Blobs, Notifications} import com.karasiq.nanoboard.frontend.utils.Notifications.Layout object PngGenerationPanel { def apply()(implicit ec: ExecutionContext, controller: NanoboardController): PngGenerationPanel = { new PngGenerationPanel } } final class PngGenerationPanel(implicit ec: ExecutionContext, controller: NanoboardController) extends BootstrapHtmlComponent { import controller.{locale, style} val model = ThreadModel(Var(NanoboardContext.Pending()), 100) private val loading = Var(false) private val pendingContainer = Rx[Frag] { val posts = model.posts() if (posts.nonEmpty) div( marginTop := 20.px, h3(locale.pendingPosts), for (p ← posts) yield GridSystem.mkRow(NanoboardPost(showParent = true, showAnswers = false, p)) ) else () } private val form = Form( FormInput.number(locale.pendingPosts, style.input, name := "pending", value := 3, min := 0), FormInput.number(locale.randomPosts, style.input, name := "random", value := 30, min := 0), // FormInput.text(locale.imageFormat, style.input, name := "format", value := "png"), FormInput.file(locale.dataContainer, style.input, name := "container"), Form.submit(locale.generateContainer)(style.submit, "disabled".classIf(loading), "btn-block".addClass), onsubmit := Callback.onSubmit { frm ⇒ if (!loading.now) { loading() = true def input(name: String) = frm(name).asInstanceOf[Input] val file: Blob = input("container").files.headOption.getOrElse(Blobs.fromBytes(Array.emptyByteArray)) val pending = input("pending").valueAsNumber val random = input("random").valueAsNumber val format = "png" // input("format").value NanoboardApi.generateContainer(pending, random, format, file).onComplete { case Success(blob) ⇒ loading() = false Blobs.saveBlob(blob, s"${js.Date.now()}.$format") model.updatePosts() case Failure(exc) ⇒ loading() = false Notifications.error(exc)(locale.containerGenerationError, Layout.topRight, 1500) } } } ) override def renderTag(md: Modifier*) = { div(form, pendingContainer, marginBottom := 50.px) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/components/SettingsPanel.scala version [acf1a59288].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
package com.karasiq.nanoboard.frontend.components import scala.concurrent.Future import scala.language.postfixOps import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue import scala.util.{Failure, Success, Try} import rx._ import com.karasiq.bootstrap.Bootstrap.default._ import scalaTags.all._ import com.karasiq.nanoboard.api.NanoboardCategory import com.karasiq.nanoboard.frontend.{Icons, NanoboardController} import com.karasiq.nanoboard.frontend.api.NanoboardApi import com.karasiq.nanoboard.frontend.utils.Notifications import com.karasiq.nanoboard.frontend.utils.Notifications.Layout object SettingsPanel { def apply()(implicit controller: NanoboardController): SettingsPanel = { new SettingsPanel } } final class SettingsPanel(implicit controller: NanoboardController) extends BootstrapHtmlComponent { import controller.{locale, style} private val placesText = Var("") private val categoriesText = Var("") val places = Rx { val urls = placesText().lines.toVector if (urls.forall(_.matches("""\b(https?|ftp)://([-a-zA-Z0-9.]+)(/[-a-zA-Z0-9+&@#/%=~_|!:,.;]*)?(\?[a-zA-Z0-9+&@#/%=~_|!:,.;]*)?"""))) { urls } else { Vector.empty } } val categories = Rx { Try { val lines = categoriesText().lines.toVector require(lines.length % 2 == 0) val categories = lines.grouped(2).map(seq ⇒ NanoboardCategory(seq.head, seq.last)).toVector require(categories.forall(c ⇒ c.hash.matches("[a-fA-F0-9]{32}") && c.name.nonEmpty)) categories }.getOrElse(Vector.empty) } private val loading = Var(false) private val buttonDisabled = Rx { loading() || categories().isEmpty || places().isEmpty } override def renderTag(md: Modifier*) = { val batchDelete = { val offset, count = Var("0") val loading = Var(false) val disabled = Rx { loading() || Try(offset().toInt).filter(_ >= 0).isFailure || Try(count().toInt).filter(_ > 0).isFailure } Form( FormInput.number(locale.offset, style.input, min := 0, offset.reactiveInput), FormInput.number(locale.count, style.input, min := 0, count.reactiveInput), Button(ButtonStyle.danger, block = true)(Icons.batchDelete, locale.batchDelete, "disabled".classIf(disabled), onclick := Callback.onClick { _ ⇒ if (!disabled.now) { Notifications.confirmation(locale.batchDeleteConfirmation(count.now.toInt), Layout.topLeft) { loading() = true NanoboardApi.delete(offset.now.toInt, count.now.toInt).onComplete { case Success(hashes) ⇒ controller.updatePosts() controller.updateCategories() count() = "" loading() = false Notifications.success(locale.batchDeleteSuccess(hashes.length), Layout.topRight) case Failure(exc) ⇒ loading() = false Notifications.error(exc)(locale.batchDeleteError, Layout.topRight) } } } }) ) } val clearDeleted = { val loading = Var(false) Button(ButtonStyle.warning, block = true)(Icons.clearDeleted, locale.clearDeleted, "disabled".classIf(loading), onclick := Callback.onClick { _ ⇒ if (!loading.now) { Notifications.confirmation(locale.clearDeletedConfirmation, Layout.topLeft) { loading() = true NanoboardApi.clearDeleted().onComplete { case Success(count) ⇒ loading() = false Notifications.success(locale.clearDeletedSuccess(count), Layout.topRight) case Failure(exc) ⇒ loading() = false Notifications.error(exc)(locale.clearDeletedError, Layout.topRight) } } } }) } val navigation = Navigation.pills( NavigationTab(locale.preferences, "server", Icons.preferences, div( GridSystem.mkRow(Form( FormInput.textArea(locale.places, style.input, rows := 15, placesText.reactiveInput)("has-error".classIf(places.map(_.isEmpty))), FormInput.textArea(locale.categories, style.input, rows := 15, categoriesText.reactiveInput)("has-error".classIf(categories.map(_.isEmpty))) )), GridSystem.mkRow(Button(block = true)(locale.submit, style.submit, "disabled".classIf(buttonDisabled), onclick := Callback.onClick { _ ⇒ if (!buttonDisabled.now) { loading() = true Future.sequence(Seq(NanoboardApi.setCategories(categories.now), NanoboardApi.setPlaces(places.now))).onComplete { case Success(_) ⇒ loading() = false controller.updateCategories() case Failure(exc) ⇒ Notifications.error(exc)(locale.settingsUpdateError, Layout.topRight) loading() = false } } })) )), NavigationTab(locale.control, "control", Icons.control, div( GridSystem.mkRow(h3(locale.batchDelete)), GridSystem.mkRow(batchDelete), GridSystem.mkRow(h3(locale.clearDeleted)), GridSystem.mkRow(clearDeleted) )), NavigationTab(locale.containers, "containers", Icons.containers, div( ContainersPanel(30) )) ) div( navigation, marginBottom := 50.px ) } def update(): Unit = { NanoboardApi.places().foreach { places ⇒ placesText() = places.mkString("\n") } NanoboardApi.categories().foreach { categories ⇒ categoriesText() = categories.map(c ⇒ s"${c.hash}\n${c.text}").mkString("\n") } } update() } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/components/ThreadContainer.scala version [e4afb76145].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
package com.karasiq.nanoboard.frontend.components import scala.language.postfixOps import rx._ import com.karasiq.bootstrap.Bootstrap.default._ import scalaTags.all._ import com.karasiq.nanoboard.api.NanoboardMessageData import com.karasiq.nanoboard.frontend.{Icons, NanoboardContext, NanoboardContextWithOffset, NanoboardController} import com.karasiq.nanoboard.frontend.components.post.{NanoboardPost, PostRenderer} import com.karasiq.nanoboard.frontend.model.ThreadModel import com.karasiq.nanoboard.frontend.utils.PostParser object ThreadContainer { def apply(context: Var[NanoboardContext], postsPerPage: Int) (implicit controller: NanoboardController): ThreadContainer = { new ThreadContainer(context, postsPerPage) } } final class ThreadContainer(val context: Var[NanoboardContext], postsPerPage: Int) (implicit controller: NanoboardController) extends BootstrapHtmlComponent { import controller.locale val model = ThreadModel(context, postsPerPage) // View private val threadPosts = Rx[Frag] { val thread = model.posts() val rendered = context.now match { case NanoboardContext.Thread(hash, _) ⇒ val (opPost, answers) = thread.partition(_.hash == hash) opPost.map(NanoboardPost(true, false, _, scrollable = true)) ++ answers.map(NanoboardPost(false, true, _, scrollable = true)) case NanoboardContext.Recent(_) | NanoboardContext.Pending(_) ⇒ thread.map(NanoboardPost(true, true, _, scrollable = true)) case NanoboardContext.Categories ⇒ thread.map(NanoboardPost(false, true, _, scrollable = true)) } div(for (p ← rendered) yield GridSystem.mkRow(p)) } private val pagination = Rx[Frag] { val posts = model.posts() val deleted = model.deletedPosts().size def previousButton(ofs: NanoboardContextWithOffset, prevOffset: Int): Tag = { Button(ButtonStyle.danger)( Icons.previous, locale.fromTo(prevOffset, prevOffset + postsPerPage), onclick := Callback.onClick { _ ⇒ context() = ofs.withOffset(prevOffset) }) } def nextButton(ofs: NanoboardContextWithOffset, newOffset: Int): Tag = { Button(ButtonStyle.success)( locale.fromTo(newOffset, newOffset + postsPerPage), Icons.next, onclick := Callback.onClick { _ ⇒ context() = ofs.withOffset(math.max(0, newOffset)) }) } context.now match { case NanoboardContext.Categories ⇒ "" case th @ NanoboardContext.Thread(hash, offset) ⇒ ButtonGroup(ButtonGroupSize.default, if (offset > 0) previousButton(th, math.max(0, offset - postsPerPage)) else (), if ((posts.length + deleted - 1) >= postsPerPage) nextButton(th, offset + math.max(0, posts.length - 1)) else (), margin := 5.px ) case ofs: NanoboardContextWithOffset ⇒ ButtonGroup(ButtonGroupSize.default, if (ofs.offset > 0) previousButton(ofs, math.max(0, ofs.offset - postsPerPage)) else (), if ((posts.length + deleted) >= postsPerPage) nextButton(ofs, ofs.offset + posts.length) else (), margin := 5.px ) } } override def renderTag(md: Modifier*): TagT = { val categories = Rx[Frag] { span( model.categories().map[Frag, Seq[Frag]] { case m @ NanoboardMessageData(_, _, hash, _, answers, _, _) ⇒ val plainText = PostRenderer.strip(PostParser.parse(m.text)) val answersSpan = span(marginLeft := 0.25.em, Icons.answers, answers) a(href := s"#$hash", margin := 0.25.em, "[", span(fontWeight.bold, plainText), answersSpan, "]", onclick := Callback.onClick { _ ⇒ controller.setContext(NanoboardContext.Thread(hash)) }) } ) } val navigation = Seq( a(href := "#0", margin := 0.25.em, Icons.recent, locale.recentPosts, onclick := Callback.onClick { _ ⇒ controller.setContext(NanoboardContext.Recent()) }), a(href := "#", margin := 0.25.em, Icons.categories, locale.categories, onclick := Callback.onClick { _ ⇒ controller.setContext(NanoboardContext.Categories) }) ) div(GridSystem.mkRow(categories), GridSystem.mkRow(navigation), GridSystem.mkRow(pagination), GridSystem.mkRow(threadPosts), GridSystem.mkRow(pagination), marginBottom := 400.px, md) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/components/ThreadPageTitle.scala version [cc9f1732a3].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
package com.karasiq.nanoboard.frontend.components import rx._ import scalatags.JsDom.tags2 import scalatags.JsDom.all._ import com.karasiq.bootstrap.Bootstrap.default._ import com.karasiq.nanoboard.frontend.{NanoboardContext, NanoboardController} import com.karasiq.nanoboard.frontend.components.post.PostRenderer import com.karasiq.nanoboard.frontend.model.ThreadModel import com.karasiq.nanoboard.frontend.utils.PostParser object ThreadPageTitle { def apply(thread: ThreadModel)(implicit controller: NanoboardController): ThreadPageTitle = { new ThreadPageTitle(thread) } } private[components] final class ThreadPageTitle(thread: ThreadModel)(implicit controller: NanoboardController) extends BootstrapHtmlComponent { import controller.locale val title = Rx[String] { thread.context() match { case NanoboardContext.Thread(_, _) ⇒ thread.posts().headOption.fold(locale.nanoboard) { post ⇒ val text = Some(PostParser.parse(post.text)) .map(PostRenderer.strip(_).trim.split("\\s+").take(10).mkString(" ")) .filter(_.nonEmpty) text.fold(locale.nanoboard)(locale.nanoboard + " - " + _) } case NanoboardContext.Recent(0) ⇒ s"${locale.nanoboard} - ${locale.recentPosts}" case NanoboardContext.Recent(offset) ⇒ s"${locale.nanoboard} - ${locale.recentPostsFrom(offset)}" case NanoboardContext.Categories ⇒ s"${locale.nanoboard} - ${locale.categories}" case _ ⇒ locale.nanoboard } } override def renderTag(md: Modifier*) = { tags2.title(title, md) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/components/post/Linkifier.scala version [ac97ade349].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
package com.karasiq.nanoboard.frontend.components.post import scala.language.postfixOps import scala.util.matching.Regex import org.scalajs.dom.Element import com.karasiq.bootstrap.Bootstrap.default._ import scalaTags.all._ import com.karasiq.nanoboard.frontend.{Icons, NanoboardController} import com.karasiq.videojs.VideoSource sealed trait LinkifierNode extends Frag case class InlineDom(frag: Frag) extends LinkifierNode { override def render = frag.render override def applyTo(t: Element) = frag.applyTo(t) } case class InlineText(str: String) extends LinkifierNode { private val frag: Frag = str override def render = frag.render override def applyTo(t: Element) = frag.applyTo(t) } object Linkifier { private val videoRegex = """\b(https?|ftp)://([-a-zA-Z0-9.]+)(/[-a-zA-Z0-9+&@#/%=~_|!:,.;]*\.(webm|mp4|ogv|3gp|avi|mov))(\?[a-zA-Z0-9+&@#/%=~_|!:,.;]*)?""".r private val youtubeRegex = """https?://(?:www\.)?youtu(?:be\.com/watch\?v=|\.be/)([\w\-]+)(&(amp;)?[\w\?=]*)?""".r private val urlRegex = """\b(?:(?:https?|ftp|file)://|www\.|ftp\.)[-а-яА-Яa-zA-Z0-9+&@#/%=~_|$?!:,.]*[а-яА-ЯA-Za-z0-9+&@#/%=~_|$]""".r private val postLinkRegex = """(?:>>|/expand/)([A-Za-z0-9]{32})""".r private val quoteRegex = """(^|\n)>[^\r\n]+""".r private def processText(text: String, regex: Regex, f: String ⇒ Frag): Seq[LinkifierNode] = { regex.findFirstMatchIn(text) match { case Some(rm) ⇒ val matched = text.slice(rm.start, rm.end) val texts = Seq(text.take(rm.start), text.drop(rm.end)) .flatMap(processText(_, regex, f)) texts.take(1) ++ Seq(InlineDom(f(matched))) ++ texts.drop(1) case None ⇒ Seq(InlineText(text)) } } def inlineYoutube(text: String): Seq[LinkifierNode] = { processText(text, youtubeRegex, url ⇒ PostExternalVideo.youtube(url)) } def inlineVideos(text: String): Seq[LinkifierNode] = { processText(text, videoRegex, { case url @ videoRegex(protocol, domain, file, ext, query) ⇒ PostExternalVideo(url, VideoSource(s"video/$ext", url)) }) } def linkify(text: String): Seq[LinkifierNode] = { processText(text, urlRegex, url ⇒ a(href := url, url)) } def postLinks(text: String)(implicit controller: NanoboardController): Seq[LinkifierNode] = { processText(text, postLinkRegex, { case postLinkRegex(hash) ⇒ PostLink(hash).renderTag(Icons.link, hash) }) } def quotes(text: String)(implicit controller: NanoboardController): Seq[LinkifierNode] = { processText(text, quoteRegex, quote ⇒ span(controller.style.greenText, quote)) } def apply(text: String)(implicit controller: NanoboardController): Seq[LinkifierNode] = { Seq(inlineYoutube _, inlineVideos _, linkify _, postLinks _, quotes _).foldLeft(Seq[LinkifierNode](InlineText(text))) { case (nodes, f) ⇒ nodes.flatMap { case dom @ InlineDom(_) ⇒ Some(dom) case InlineText(data) ⇒ f(data) } } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/components/post/NanoboardPost.scala version [38865b2329].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
package com.karasiq.nanoboard.frontend.components.post import scala.language.postfixOps import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue import rx._ import com.karasiq.bootstrap.Bootstrap.default._ import scalaTags.all._ import com.karasiq.nanoboard.api.NanoboardMessageData import com.karasiq.nanoboard.frontend.{Icons, NanoboardContext, NanoboardController} import com.karasiq.nanoboard.frontend.api.NanoboardApi import com.karasiq.nanoboard.frontend.components.post.actions.{PendingButton, ReplyField, VerificationButton} import com.karasiq.nanoboard.frontend.styles.CommonStyles import com.karasiq.nanoboard.frontend.utils.{Notifications, PostParser} import com.karasiq.nanoboard.frontend.utils.Notifications.Layout private[components] object NanoboardPost { def render(text: String)(implicit controller: NanoboardController): Frag = { PostRenderer().render(PostParser.parse(text)) } def apply(showParent: Boolean, showAnswers: Boolean, data: NanoboardMessageData, scrollable: Boolean = false) (implicit controller: NanoboardController): NanoboardPost = { new NanoboardPost(showParent, showAnswers, data, scrollable) } } private[components] final class NanoboardPost(showParent: Boolean, showAnswers: Boolean, postData: NanoboardMessageData, scrollable: Boolean) (implicit controller: NanoboardController) extends BootstrapHtmlComponent { import controller.{locale, style} val expanded = Var(false) val showSource = Var(false) override def renderTag(md: Modifier*): TagT = { val heightMod = Rx[Modifier] { if (expanded()) maxHeight := 100.pct else maxHeight := 48.em } div( if (scrollable) id := s"post-${postData.hash}" else (), style.post, div( heightMod.auto, style.postInner, CommonStyles.flatScroll, span( style.postId, if (showParent && postData.parent.isDefined) PostLink(postData.parent.get).renderTag(Icons.parent) else (), sup(cursor.pointer, postData.containerId.fold(postData.hash)(cid ⇒ s"${postData.hash}/$cid"), onclick := Callback.onClick(_ ⇒ expanded() = !expanded.now)) ), Rx[Frag](if (showSource()) postData.text else span(NanoboardPost.render(postData.text))) ), div( if (showAnswers && postData.answers > 0) a(style.postLink, href := s"#${postData.hash}", Icons.answers, s"${postData.answers}", onclick := Callback.onClick { _ ⇒ this.openAsThread() }) else (), a(style.postLink, href := "#", Icons.delete, locale.delete, onclick := Callback.onClick { _ ⇒ this.delete() }), PendingButton(postData), a(style.postLink, href := "#", Icons.source, locale.source, onclick := Callback.onClick(_ ⇒ showSource() = !showSource.now)), VerificationButton(postData), ReplyField(postData) ), md ) } def openAsThread(): Unit = { controller.setContext(NanoboardContext.Thread(postData.hash, 0)) } def delete(): Unit = { Notifications.confirmation(locale.deleteConfirmation(postData.hash), Layout.topLeft) { NanoboardApi.delete(postData.hash).foreach { hashes ⇒ controller.deleteSingle(postData) hashes.foreach { hash ⇒ controller.deleteSingle(NanoboardMessageData(None, None, hash, "", 0)) } } } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/components/post/PostExternalImage.scala version [35f715aa7f].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
package com.karasiq.nanoboard.frontend.components.post import scala.language.postfixOps import rx._ import com.karasiq.bootstrap.Bootstrap.default._ import scalaTags.all._ import com.karasiq.nanoboard.frontend.Icons private[components] object PostExternalImage { def apply(url: String): PostExternalImage = { new PostExternalImage(url) } } private[components] final class PostExternalImage(url: String) extends BootstrapHtmlComponent { val opened = Var(false) val expanded = Var(false) private val imageStyleMod = Rx { val modifier: Modifier = if (expanded()) { Seq[Modifier](maxWidth := 100.pct, maxHeight := 100.pct) } else { Seq[Modifier](maxWidth := 200.px, maxHeight := 200.px) } modifier } private val image = Rx[Frag] { if (opened()) { img(display.block, src := url, imageStyleMod.auto, onclick := Callback.onClick(_ ⇒ expanded() = !expanded.now)) } else { "" } } override def renderTag(md: Modifier*) = { span( a(href := url, fontWeight.bold, Icons.image, url, onclick := Callback.onClick(_ ⇒ opened() = !opened.now)), image, md ) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/components/post/PostExternalVideo.scala version [c2e3801b12].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
package com.karasiq.nanoboard.frontend.components.post import rx._ import scalatags.JsDom.all._ import com.karasiq.bootstrap.Bootstrap.default._ import com.karasiq.nanoboard.frontend.Icons import com.karasiq.videojs.{VideoJSBuilder, VideoSource} private[components] object PostExternalVideo { def defaultType = "webm" def apply(url: String, sources: VideoSource*): PostExternalVideo = { new PostExternalVideo(url, sources) } def youtube(url: String): PostExternalVideo = { new PostExternalVideo(url, Seq(VideoSource("video/youtube", url)), Seq("youtube")) } } private[components] final class PostExternalVideo(url: String, sources: Seq[VideoSource], techOrder: Seq[String] = Nil) extends BootstrapHtmlComponent { val expanded = Var(false) private val videoPlayer = Rx[Frag] { if (expanded()) { VideoJSBuilder() .techOrder(techOrder:_*) .sources(sources:_*) .dimensions(640, 360) .fluid(true) .autoplay(true) .loop(true) .controls(true) .options("iv_load_policy" → 1) .build() } else { "" } } override def renderTag(md: Modifier*) = { span( a(href := url, fontWeight.bold, Icons.video, url, onclick := Callback.onClick(_ ⇒ expanded() = !expanded.now)), videoPlayer, md ) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/components/post/PostFractalMusic.scala version [dcb7db3f15].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
package com.karasiq.nanoboard.frontend.components.post import scala.scalajs.js.URIUtils import rx._ import scalatags.JsDom.all._ import com.karasiq.bootstrap.Bootstrap.default._ import com.karasiq.nanoboard.frontend.Icons private[components] object PostFractalMusic { def apply(formula: String): PostFractalMusic = { new PostFractalMusic(formula) } } private[components] final class PostFractalMusic(formula: String) extends BootstrapHtmlComponent { val opened = Var(false) val url = s"/fractal_music/${URIUtils.encodeURIComponent(formula)}" private val player = Rx[Frag] { if (opened()) { audio(attr("controls") := "controls", attr("autoplay") := true, attr("loop") := true, display.block, `type` := "audio/wav", src := url) } else { "" } } override def renderTag(md: Modifier*) = { span( a(href := url, fontWeight.bold, Icons.music, formula, onclick := Callback.onClick(_ ⇒ opened() = !opened.now)), player, md ) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/components/post/PostInlineFile.scala version [b57359b780].
> > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package com.karasiq.nanoboard.frontend.components.post import scalatags.JsDom.all._ import com.karasiq.bootstrap.Bootstrap.default._ import com.karasiq.nanoboard.frontend.{Icons, NanoboardController} import com.karasiq.nanoboard.frontend.utils.Blobs private[components] object PostInlineFile { def apply(fileName: String, base64: String, fileType: String)(implicit controller: NanoboardController): PostInlineFile = { new PostInlineFile(fileName, base64, fileType) } } private[components] final class PostInlineFile(val fileName: String, val base64: String, val fileType: String) (implicit controller: NanoboardController) extends BootstrapHtmlComponent { val file = Blobs.fromBase64(base64, fileType) override def renderTag(md: Modifier*): TagT = { a(fontWeight.bold, href := "#", Icons.file, fileName, onclick := Callback.onClick { _ ⇒ Blobs.saveBlob(file, fileName) }) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/components/post/PostInlineImage.scala version [b09bca03df].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
package com.karasiq.nanoboard.frontend.components.post import rx._ import scalatags.JsDom.all._ import com.karasiq.bootstrap.Bootstrap.default._ import com.karasiq.nanoboard.frontend.NanoboardController import com.karasiq.nanoboard.frontend.utils.Blobs private[components] object PostInlineImage { def defaultType = "jpeg" def apply(base64: String, imageType: String = defaultType)(implicit controller: NanoboardController): PostInlineImage = { new PostInlineImage(base64, imageType) } } private[components] final class PostInlineImage(val base64: String, val imageType: String)(implicit controller: NanoboardController) extends BootstrapHtmlComponent { val expanded = Var(false) private val styleMod = Rx { val modifier: Modifier = if (expanded()) { Seq[Modifier](maxWidth := 100.pct, maxHeight := 100.pct) } else { Seq[Modifier](maxWidth := 200.px, maxHeight := 200.px) } modifier } override def renderTag(md: Modifier*) = { val blobUrl = Blobs.asUrl(Blobs.fromBase64(base64, s"image/$imageType")) // s"data:image/jpeg;base64,$base64" img(alt := controller.locale.embeddedImage, src := blobUrl, styleMod.auto, onclick := Callback.onClick(_ ⇒ expanded() = !expanded.now), md) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/components/post/PostLink.scala version [ff385b2e65].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
package com.karasiq.nanoboard.frontend.components.post import scala.language.postfixOps import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue import rx._ import rx.async._ import scalatags.JsDom.all._ import com.karasiq.bootstrap.Bootstrap.default._ import com.karasiq.nanoboard.frontend.NanoboardController import com.karasiq.nanoboard.frontend.api.NanoboardApi import com.karasiq.nanoboard.frontend.utils.Mouse private[components] object PostLink { def apply(hash: String)(implicit controller: NanoboardController): PostLink = { new PostLink(hash) } } private[components] final class PostLink(hash: String)(implicit controller: NanoboardController) extends BootstrapHtmlComponent { lazy val post = NanoboardApi.post(hash).toRx(None) .map(_.map(data ⇒ div(Mouse.relative(xOffset = 12), zIndex := 1, NanoboardPost(showParent = false, showAnswers = true, data)).render)) private val hover = Var(false) override def renderTag(md: Modifier*): TagT = { val updateHover: Modifier = Seq( onmouseover := { () ⇒ hover() = true }, onmouseout := { () ⇒ hover() = false } ) span( position.relative, a(updateHover, href := s"#$hash", onclick := Callback.onClick(_ ⇒ controller.showPost(hash)), md), Rx[Frag](if (hover() && post().nonEmpty) post().get else "") ) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/components/post/PostRenderer.scala version [bf563f5909].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
package com.karasiq.nanoboard.frontend.components.post import scala.scalajs.js import scalatags.JsDom.all._ import com.karasiq.bootstrap.Bootstrap.default._ import com.karasiq.highlightjs.HighlightJS import com.karasiq.markedjs.{Marked, MarkedOptions, MarkedRenderer} import com.karasiq.nanoboard.frontend.NanoboardController import com.karasiq.nanoboard.frontend.utils._ import com.karasiq.nanoboard.frontend.utils.PostDomValue._ import com.karasiq.videojs.VideoSource private[components] object PostRenderer { def apply()(implicit controller: NanoboardController): PostRenderer = { new PostRenderer } // Returns HTML string def formatCode(source: String, language: Option[String]): String = { import scalatags.Text.all.{source ⇒ _, _} val result = language.fold(HighlightJS.highlightAuto(source))(HighlightJS.highlight(_, source)) span(whiteSpace.`pre-wrap`, code(`class` := s"hljs ${result.language}", raw(result.value))).render } def renderMarkdown(source: String): Frag = { val renderer = MarkedRenderer( image = { (url: String, imageTitle: String, text: String) ⇒ import scalatags.Text.all._ a(href := url, title := text, target := "_blank", imageTitle).render }, code = { (source: String, language: String) ⇒ formatCode(source, if (js.isUndefined(language)) None else Some(language)) }, table = { (header: String, body: String) ⇒ import scalatags.Text.all.{body ⇒ _, header ⇒ _, _} div(`class` := "table-responsive", table(`class` := "table", thead(raw(header)), tbody(raw(body)))).render } ) val options = MarkedOptions( renderer = renderer, gfm = true, tables = true, breaks = true, pedantic = false, sanitize = true, smartLists = true, smartypants = true ) span(whiteSpace.normal, raw(Marked(source, options))) } def asText(parsed: PostDomValue): String = parsed match { case PlainText(value) ⇒ value case PostDomValues(values) ⇒ values.map(asText).mkString case BBCode("plain", _, value) ⇒ asText(value) case BBCode(name, parameters, value) ⇒ s"[$name${if (parameters.isEmpty) "" else parameters.map(p ⇒ p._1 + "=\"" + p._2 + "\"").mkString(" ", " ", "")}]" + asText(value) + s"[/$name]" case ShortBBCode(name, value) ⇒ s"[$name=$value]" } def strip(parsed: PostDomValue): String = parsed match { case PlainText(value) ⇒ value case PostDomValues(values) ⇒ values.map(strip).mkString case BBCode("md" | "img" | "xmg" | "file" | "g" | "sp" | "spoiler", _, _) ⇒ "" case BBCode(_, _, value) ⇒ strip(value) case ShortBBCode("svid" | "simg", url) ⇒ url case _ ⇒ "" } } private[components] final class PostRenderer(implicit controller: NanoboardController) { def render(parsed: PostDomValue): Frag = parsed match { case PlainText(value) ⇒ Linkifier(value) case PostDomValues(values) ⇒ values.map(render) case BBCode("md", _, value) ⇒ PostRenderer.renderMarkdown(PostRenderer.asText(value)) case BBCode("b", _, value) ⇒ strong(render(value)) case BBCode("i", _, value) ⇒ em(render(value)) case BBCode("u", _, value) ⇒ u(render(value)) case BBCode("s", _, value) ⇒ s(render(value)) case BBCode("g", _, value) ⇒ span(controller.style.greenText, render(value)) case BBCode("sp" | "spoiler", _, value) ⇒ span(controller.style.spoiler, render(value)) case ShortBBCode("img" | "xmg", base64) ⇒ PostInlineImage(base64) case BBCode("img", parameters, value) ⇒ PostInlineImage(PostRenderer.asText(value), parameters.getOrElse("type", PostInlineImage.defaultType)) case ShortBBCode("simg", url) ⇒ PostExternalImage(url) case BBCode("video", parameters, value) ⇒ val url = PostRenderer.asText(value) PostExternalVideo(url, VideoSource(s"video/${parameters.getOrElse("type", PostExternalVideo.defaultType)}", url)) case ShortBBCode("svid", url) ⇒ PostExternalVideo(url, VideoSource(s"video/${PostExternalVideo.defaultType}", url)) case ShortBBCode("fm", music) ⇒ PostFractalMusic(music) case BBCode("code", parameters, source) ⇒ span(raw(PostRenderer.formatCode(PostRenderer.asText(source), parameters.get("lang")))) case BBCode("file", parameters, value) ⇒ PostInlineFile(parameters.getOrElse("name", controller.locale.file), PostRenderer.asText(value), parameters.getOrElse("type", "")) case BBCode("link", parameters, value) ⇒ a(target := "_blank", href := parameters.getOrElse("url", PostRenderer.asText(value)), render(value)) case BBCode("small", _, value) ⇒ small(render(value)) case BBCode("abbr", parameters, value) ⇒ val abbr = tag("abbr") abbr(title := parameters.getOrElse("title", ""), render(value)) case BBCode("mark", _, value) ⇒ val mark = tag("mark") mark(render(value)) case BBCode("quote", parameters, value) ⇒ blockquote( if (parameters.contains("reverse")) "blockquote-reverse".addClass else (), p(render(value)), parameters.get("title").map(footer(_)) ) // Unknown case value ⇒ PostRenderer.asText(value) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/components/post/actions/CaptchaDialog.scala version [0faa598008].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
package com.karasiq.nanoboard.frontend.components.post.actions import scala.concurrent.{Future, Promise} import scala.language.postfixOps import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue import scala.util.{Failure, Success} import rx._ import com.karasiq.bootstrap.Bootstrap.default._ import scalaTags.all._ import com.karasiq.bootstrap.Bootstrap.default._ import com.karasiq.nanoboard.api.{NanoboardCaptchaRequest, NanoboardMessageData} import com.karasiq.nanoboard.frontend.NanoboardController import com.karasiq.nanoboard.frontend.api.NanoboardApi import com.karasiq.nanoboard.frontend.utils.{Blobs, CancelledException, Notifications} import com.karasiq.nanoboard.frontend.utils.Notifications.Layout private[components] object CaptchaDialog { def apply()(implicit controller: NanoboardController): CaptchaDialog = { new CaptchaDialog() } } /** * Captcha dialog */ private[components] final class CaptchaDialog(implicit controller: NanoboardController) { import controller.locale val answer = Var("") val ready = Rx { answer().nonEmpty } def verify(hash: String): Future[NanoboardMessageData] = { for { request ← NanoboardApi.requestVerification(hash) result ← solveCaptcha(request) } yield result } def solveCaptcha(request: NanoboardCaptchaRequest): Future[NanoboardMessageData] = { val promise = Promise[NanoboardMessageData] val modal = Modal(locale.verify) .withBody(Form( img(display.block, height := 60.px, src := Blobs.asUrl(Blobs.fromBytes(request.captcha.image, "image/png"))), FormInput.text((), answer.reactiveInput), onsubmit := Callback.onSubmit(_ ⇒ ()) )) .withButtons( Modal.closeButton(locale.cancel)(onclick := Callback.onClick { _ ⇒ promise.failure(CancelledException) }), Button(ButtonStyle.success)(locale.submit, Modal.dismiss, ready.reactiveShow, onclick := Callback.onClick { _ ⇒ NanoboardApi.verifyPost(request, answer.now) onComplete { case Success(data) ⇒ promise.success(data) case Failure(exc) ⇒ Notifications.error(exc)(locale.verificationError, Layout.topRight) promise.completeWith(solveCaptcha(request)) // Retry } }) ) modal.show(backdrop = false) promise.future } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/components/post/actions/ImageAttachDialog.scala version [e7dc3e1e38].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
package com.karasiq.nanoboard.frontend.components.post.actions import scala.concurrent.{Future, Promise} import scala.language.postfixOps import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue import scala.util.{Failure, Success, Try} import org.scalajs.dom.raw.File import rx._ import com.karasiq.bootstrap.Bootstrap.default._ import scalaTags.all._ import scalatags.JsDom.all._ import com.karasiq.nanoboard.frontend.NanoboardController import com.karasiq.nanoboard.frontend.api.NanoboardApi import com.karasiq.nanoboard.frontend.components.post.PostInlineImage import com.karasiq.nanoboard.frontend.utils.{Blobs, CancelledException, Images, Notifications} import com.karasiq.nanoboard.frontend.utils.Notifications.Layout case class ImageData(base64: String, format: String) private[components] object ImageAttachDialog { def apply()(implicit controller: NanoboardController): ImageAttachDialog = { new ImageAttachDialog() } } private[components] final class ImageAttachDialog(implicit controller: NanoboardController) { import controller.locale val scale = Var("50") val size = Var("500") val quality = Var("50") val sharpness = Var("50") val files = Var[Seq[File]](Nil) val useServer = Var(false) val formatSelect = FormInput.select(locale.imageFormat, Rx { val options = if (!useServer() && Images.isWebpSupported) { Seq("jpeg", "webp", "png") } else { Seq("jpeg", "png") } options.map(str ⇒ FormSelectOption(str, str)) }) lazy val format = formatSelect.selected.map(_.head) lazy val ready = Rx { def isValidPct(value: Rx[String]): Boolean = Try(value().toInt).filter((1 to 100).contains).isSuccess files().nonEmpty && ((useServer() && Try(size().toInt).filter(_ > 0).isSuccess) || isValidPct(scale)) && isValidPct(quality) && (useServer() || isValidPct(sharpness)) } def generate(): Future[ImageData] = { val promise = Promise[ImageData] val preview = Var[Option[PostInlineImage]](None) val modal = Modal(locale.insertImage) .withBody(Form( formatSelect, Rx { if (useServer()) FormInput.number(locale.imageSize, name := "size", min := 1, size.reactiveInput, placeholder := 500) else FormInput.number(locale.imageScale, name := "scale", min := 1, max := 100, scale.reactiveInput, placeholder := 50) }, FormInput.number(locale.imageQuality, name := "quality", min := 1, max := 100, quality.reactiveInput, placeholder := 50), Rx[Frag] { if (useServer()) "" else FormInput.number(locale.imageSharpness, name := "sharpness", min := 1, max := 100, sharpness.reactiveInput, placeholder := 50) }, FormInput.file(locale.dataContainer, name := "image", files.reactiveInputRead), FormInput.checkbox(locale.useServerRendering, useServer.reactiveInput), div(preview.map(_.map(_.base64.length).fold[Frag]("")(length ⇒ s"$length ${locale.bytes}"))), div(preview.map(_.fold[Frag]("")(img ⇒ img))), onsubmit := Callback.onSubmit(_ ⇒ ()) )) .withButtons( Modal.closeButton(locale.cancel)(onclick := Callback.onClick { _ ⇒ promise.failure(CancelledException) }), Button(ButtonStyle.info)(locale.preview, ready.reactiveShow)(onclick := Callback.onClick { _ ⇒ createBase64Image().onComplete { case Success(ImageData(base64, format)) ⇒ preview() = Some(PostInlineImage(base64, format)) case Failure(exc) ⇒ Notifications.error(exc)(locale.attachmentGenerationError, Layout.topRight) preview() = None } }), Button(ButtonStyle.success)(locale.submit, Modal.dismiss, ready.reactiveShow, onclick := Callback.onClick { _ ⇒ promise.completeWith(createBase64Image()) }) ) modal.show(backdrop = false) promise.future } private def createBase64Image(): Future[ImageData] = files.now.headOption match { case Some(file) if file.`type` == "image/svg+xml" ⇒ Blobs.asBase64(file).map(ImageData(_, "svg+xml")) case Some(file) if useServer.now ⇒ NanoboardApi.generateAttachment(format.now, size.now.toInt, quality.now.toInt, file).map(ImageData(_, format.now)) case Some(file) ⇒ Images.compress(file, s"image/${format.now}", scale.now.toInt, quality.now.toInt, sharpness.now.toInt).map(ImageData(_, format.now)) case None ⇒ Future.failed(new NoSuchElementException("No file selected")) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/components/post/actions/PendingButton.scala version [509ca5f560].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
package com.karasiq.nanoboard.frontend.components.post.actions import scala.language.postfixOps import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue import rx._ import com.karasiq.bootstrap.Bootstrap.default._ import scalaTags.all._ import com.karasiq.nanoboard.api.NanoboardMessageData import com.karasiq.nanoboard.frontend.{Icons, NanoboardController} import com.karasiq.nanoboard.frontend.api.NanoboardApi private[post] object PendingButton { def apply(post: NanoboardMessageData)(implicit controller: NanoboardController) = { new PendingButton(post) } } private[post] final class PendingButton(post: NanoboardMessageData)(implicit controller: NanoboardController) extends BootstrapComponent { import controller.{locale, style} override def render(md: Modifier*): Modifier = { controller.isPending(post.hash).map { pending ⇒ if (!pending) a(style.postLink, href := "#", Icons.enqueue, locale.enqueue, onclick := Callback.onClick { a ⇒ NanoboardApi.markAsPending(post.hash).foreach { _ ⇒ controller.addPending(post) } }) else a(style.postLink, href := "#", Icons.dequeue, locale.dequeue, onclick := Callback.onClick { a ⇒ NanoboardApi.markAsNotPending(post.hash).foreach { _ ⇒ controller.deletePending(post) } }) } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/components/post/actions/ReplyField.scala version [c27464fc4b].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
package com.karasiq.nanoboard.frontend.components.post.actions import scala.language.postfixOps import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue import scala.util.{Failure, Success} import org.scalajs.dom.Element import org.scalajs.dom.html.TextArea import rx._ import com.karasiq.bootstrap.Bootstrap.default._ import scalaTags.all._ import com.karasiq.nanoboard.api.NanoboardMessageData import com.karasiq.nanoboard.frontend.{Icons, NanoboardController} import com.karasiq.nanoboard.frontend.api.NanoboardApi import com.karasiq.nanoboard.frontend.components.post.NanoboardPost import com.karasiq.nanoboard.frontend.styles.CommonStyles import com.karasiq.nanoboard.frontend.utils.{Blobs, CancelledException, Notifications} import com.karasiq.nanoboard.frontend.utils.Notifications.Layout import com.karasiq.taboverridejs.TabOverride private[post] object ReplyField { def apply(post: NanoboardMessageData)(implicit controller: NanoboardController): ReplyField = { new ReplyField(post) } def tabOverride: Modifier = new Modifier { override def applyTo(t: Element): Unit = { TabOverride.set(t.asInstanceOf[TextArea]) } } } private[post] final class ReplyField(post: NanoboardMessageData)(implicit controller: NanoboardController) extends BootstrapHtmlComponent { import controller.{locale, style} val expanded = Var(false) val replyText = Var("") val lengthIsValid = replyText.map(text ⇒ (1 to 65535).contains(text.length)) override def renderTag(md: Modifier*) = { val field = Form( FormInput.textArea((), style.input, placeholder := locale.writeYourMessage, rows := 5, replyText.reactiveInput, "has-errors".classIf(lengthIsValid.map(!_)), ReplyField.tabOverride) ) val imageLink = Button(ButtonStyle.primary)(Icons.image, locale.insertImage, onclick := Callback.onClick { _ ⇒ ImageAttachDialog().generate().onComplete { case Success(ImageData(base64, format)) ⇒ val data = if (format == "svg+xml") "[img type=\"svg+xml\"]" + base64 + "[/img]" else s"[img=$base64]" replyText() = s"${replyText.now}$data" case Failure(CancelledException) ⇒ // Pass case Failure(exc) ⇒ Notifications.error(exc)(locale.attachmentGenerationError, Layout.topRight) } }) val fileLink = Button(ButtonStyle.info)(Icons.file, locale.file, onclick := Callback.onClick { _ ⇒ val field = input(`type` := "file", onchange := Callback.onInput { field ⇒ val file = field.files.head Blobs.asBase64(file).foreach { base64 ⇒ replyText() = s"${replyText.now}${if (replyText.now.nonEmpty) "\n" else ""}[file name=${'"' + file.name + '"'} type=${'"' + file.`type` + '"'}]$base64[/file]" } }).render field.click() }) val submitButton = Button(ButtonStyle.success)(/* "disabled".classIf(lengthIsValid.map(!_)),*/ Icons.submit, locale.submit, onclick := Callback.onClick { _ ⇒ if (/* lengthIsValid.now */ replyText.now.nonEmpty) { NanoboardApi.addReply(post.hash, replyText.now).onComplete { case Success(newPost) ⇒ expanded() = false replyText() = "" controller.addPost(newPost) case Failure(exc) ⇒ Notifications.error(exc)(locale.postingError, Layout.topRight) } } }) val postLength = Rx { span(float.right, fontStyle.italic, if (!lengthIsValid()) color.red else (), s"${replyText().length} ${locale.bytes}") } span( // Reply link a(style.postLink, href := "#", Icons.reply, locale.reply, onclick := Callback.onClick { _ ⇒ expanded() = !expanded.now }), // Input field div(field, ButtonGroup(ButtonGroupSize.small, imageLink, fileLink, submitButton), postLength, expanded.reactiveShow ), // Preview div(marginTop := 20.px, style.post, Rx(span(style.postInner, CommonStyles.flatScroll, NanoboardPost.render(replyText()))), Rx(expanded() && replyText().nonEmpty).reactiveShow), md ) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/components/post/actions/VerificationButton.scala version [083a68319e].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
package com.karasiq.nanoboard.frontend.components.post.actions import scala.language.postfixOps import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue import scala.util.{Failure, Success} import rx._ import com.karasiq.bootstrap.Bootstrap.default._ import scalaTags.all._ import scalatags.JsDom.all._ import com.karasiq.nanoboard.api.NanoboardMessageData import com.karasiq.nanoboard.frontend.{Icons, NanoboardController} import com.karasiq.nanoboard.frontend.utils.{CancelledException, Notifications} import com.karasiq.nanoboard.frontend.utils.Notifications.Layout private[post] object VerificationButton { def apply(post: NanoboardMessageData)(implicit controller: NanoboardController) = { new VerificationButton(post) } } private[post] final class VerificationButton(post: NanoboardMessageData)(implicit controller: NanoboardController) extends BootstrapHtmlComponent { import controller.{locale, style} val hidden = Var(post.isSigned || post.isCategory) override def renderTag(md: Modifier*): TagT = { a(style.postLink, href := "#", Icons.verify, locale.verify, Rx(if (hidden()) display.none else display.inline).auto, onclick := Callback.onClick { _ ⇒ hidden() = true CaptchaDialog().verify(post.hash).onComplete { case Success(verified) ⇒ Notifications.success(locale.verificationSuccess(verified.hash), Layout.topRight) controller.addPending(verified) case Failure(CancelledException) ⇒ hidden() = false case Failure(exc) ⇒ hidden() = false Notifications.error(exc)(locale.verificationError, Layout.topRight) } }) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/locales/BoardLocale.scala version [7216e6386f].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
package com.karasiq.nanoboard.frontend.locales import com.karasiq.nanoboard.api.NanoboardContainer trait BoardLocale { def nanoboard: String def settings: String def containerGeneration: String def recentPosts: String def recentPostsFrom(post: Int): String def categories: String def places: String def delete: String def deleteConfirmation(hash: String): String def enqueue: String def dequeue: String def reply: String def insertImage: String def submit: String def cancel: String def pendingPosts: String def randomPosts: String def imageScale: String def imageSize: String def imageQuality: String def imageFormat: String def imageSharpness: String def useServerRendering: String def preview: String def dataContainer: String def generateContainer: String def fromTo(from: Int, to: Int): String def embeddedImage: String def writeYourMessage: String def bytes: String def style: String def preferences: String def control: String def offset: String def count: String def batchDelete: String def batchDeleteConfirmation(count: Int): String def batchDeleteSuccess(count: Int): String def clearDeleted: String def clearDeletedConfirmation: String def clearDeletedSuccess(count: Int): String def containers: String def container(c: NanoboardContainer): String def file: String def source: String def verify: String def verificationError: String def verificationSuccess(hash: String): String def clearDeletedError: String def postingError: String def updateError: String def containerGenerationError: String def attachmentGenerationError: String def settingsUpdateError: String def batchDeleteError: String def webSocketError: String } object BoardLocale { def browserLanguage: String = { import org.scalajs.dom.window.navigator navigator.language } def fromBrowserLanguage(): BoardLocale = { browserLanguage.toLowerCase match { case "ru-ru" | "ru" ⇒ Russian case _ ⇒ English } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/locales/English.scala version [e675ee496c].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
package com.karasiq.nanoboard.frontend.locales import com.karasiq.nanoboard.api.NanoboardContainer object English extends BoardLocale { def nanoboard = "Nanoboard" def generateContainer = "Generate container" def cancel = "Cancel" def dataContainer = "Source file" def dequeue = "Dequeue" def insertImage = "Picture" def recentPostsFrom(post: Int) = s"Recent posts (from $post)" def categories = "Categories" def pendingPosts = "Pending posts" def containerGeneration = "Container generation" def delete = "Delete" def imageFormat = "Image format" def imageSharpness = "Image sharpness" def enqueue = "Enqueue" def recentPosts = "Recent posts" def reply = "Reply" def settings = "Settings" def places = "Places" def imageScale = "Image scale (%)" def imageSize = "Image size (pixels)" def useServerRendering = "Use server rendering" def preview = "Preview" def submit = "Submit" def randomPosts = "Random posts" def imageQuality = "Image quality" def deleteConfirmation(hash: String) = s"Are you sure you want to permanently delete post #$hash?" def writeYourMessage = "Write your message" def bytes = "bytes" def style = "Style" def fromTo(from: Int, to: Int) = s"From $from to $to" def embeddedImage = "Embedded image" def preferences = "Preferences" def control = "Control" def offset = "Offset" def count = "Count" def batchDelete = "Batch delete" def batchDeleteConfirmation(count: Int) = s"Are you sure you want to permanently delete $count posts?" def batchDeleteSuccess(count: Int) = s"$count posts successfully removed" def clearDeleted = "Clear deleted posts" def clearDeletedConfirmation = "Clear deleted posts cache?" def clearDeletedSuccess(count: Int) = s"$count deleted posts evicted" def containers = "Containers" def container(c: NanoboardContainer) = s"№${c.id}, ${c.posts} posts" def file = "File" def source = "Source" def verify = "Verify" def verificationError = "Verification error" def verificationSuccess(hash: String) = s"Post verified: #$hash" def webSocketError = "WebSocket error" def clearDeletedError = "Clearing deleted posts failure" def containerGenerationError = "Container generation failure" def attachmentGenerationError = "Attachment generation error" def batchDeleteError = "Batch deletion error" def settingsUpdateError = "Settings update error" def postingError = "Posting error" def updateError = "Update error" } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/locales/Russian.scala version [397350a577].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
package com.karasiq.nanoboard.frontend.locales import com.karasiq.nanoboard.api.NanoboardContainer object Russian extends BoardLocale { def nanoboard = "Наноборда" def generateContainer = "Создать контейнер" def cancel = "Отмена" def dataContainer = "Исходный файл" def dequeue = "Из очереди" def insertImage = "Изображение" def recentPostsFrom(post: Int) = s"Недавние сообщения, начиная с $post" def categories = "Категории" def pendingPosts = "Сообщения, ожидающие отправки" def containerGeneration = "Генерация контейнера" def delete = "Удалить" def imageFormat = "Формат изображения" def enqueue = "В очередь" def recentPosts = "Недавние сообщения" def reply = "Ответить" def settings = "Настройки" def places = "Треды с контейнерами" def imageScale = "Размер изображения в процентах" def imageSize = "Размер изображения в пикселях" def submit = "Отправить" def randomPosts = "Случайные сообщения" def imageQuality = "Качество изображения" def imageSharpness = "Резкость изображения" def useServerRendering = "Использовать серверный рендеринг" def preview = "Предпросмотр" def deleteConfirmation(hash: String) = s"Вы уверены, что хотите навсегда удалить сообщение #$hash?" def writeYourMessage = "Введите сообщение" def bytes = "байт" def style = "Стиль оформления" def fromTo(from: Int, to: Int) = s"С $from по $to" def embeddedImage = "Встроенное изображение" def preferences = "Опции" def control = "Управление" def offset = "Начиная с" def count = "Количество" def source = "Текст" def batchDelete = "Массовое удаление" def batchDeleteConfirmation(count: Int) = s"Вы уверены, что хотите навсегда удалить $count сообщений?" def batchDeleteSuccess(count: Int) = s"$count сообщений успешно удалено" def clearDeleted = "Очистка удалённых сообщений" def clearDeletedConfirmation = "Очистить кэш удалённых сообщений?" def clearDeletedSuccess(count: Int) = s"$count удалённых сообщений очищенно" def containers = "Принятые контейнеры" def container(c: NanoboardContainer) = s"№${c.id}, ${c.posts} сообщений" def file = "Файл" def verify = "Подтвердить" def verificationError = "Ошибка подтверждения" def verificationSuccess(hash: String) = s"Сообщение подтверждено: #$hash" def webSocketError = "Ошибка WebSocket" def clearDeletedError = "Ошибка очистки кэша удалённых сообщений" def postingError = "Ошибка отправки сообщения" def updateError = "Ошибка обновления" def containerGenerationError = "Ошибка создания контейнера" def attachmentGenerationError = "Ошибка вставки изображения" def batchDeleteError = "Ошибка массового удаления" def settingsUpdateError = "Ошибка применения настроек" } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/model/ThreadModel.scala version [e604b30224].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
package com.karasiq.nanoboard.frontend.model import scala.concurrent.Future import scala.language.postfixOps import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue import scala.util.{Failure, Success} import rx._ import com.karasiq.bootstrap.Bootstrap.default._ import scalaTags.all._ import com.karasiq.nanoboard.api.NanoboardMessageData import com.karasiq.nanoboard.frontend.{NanoboardContext, NanoboardController} import com.karasiq.nanoboard.frontend.api.NanoboardApi import com.karasiq.nanoboard.frontend.utils.Notifications import com.karasiq.nanoboard.frontend.utils.Notifications.Layout private[frontend] object ThreadModel { def apply(context: Var[NanoboardContext], postsPerPage: Int)(implicit controller: NanoboardController) = { new ThreadModel(context, postsPerPage) } } private[frontend] final class ThreadModel(val context: Var[NanoboardContext], postsPerPage: Int)(implicit controller: NanoboardController) { import controller.locale val addedPosts = Var(Set.empty[String]) val deletedPosts = Var(Set.empty[String]) val categories = Var(Vector.empty[NanoboardMessageData]) val posts = Var(Vector.empty[NanoboardMessageData]) def addPost(post: NanoboardMessageData): Unit = { if (!addedPosts.now.contains(post.hash) && !posts.now.exists(_.hash == post.hash)) { posts() = context.now match { case NanoboardContext.Recent(0) ⇒ if (posts.now.length == postsPerPage) { post +: posts.now.dropRight(1) } else { post +: posts.now } case NanoboardContext.Pending(_) if posts.now.length < postsPerPage ⇒ posts.now :+ post case NanoboardContext.Thread(hash, 0) if post.parent.contains(hash) ⇒ val (opPost, answers) = posts.now.partition(_.hash == hash) if (answers.length >= postsPerPage) { opPost ++ Some(post) ++ answers.dropRight(1) } else { opPost ++ Some(post) ++ answers } case NanoboardContext.Thread(post.hash, _) ⇒ val (_, answers) = posts.now.partition(_.hash == post.hash) post +: answers case _ ⇒ posts.now } updateAnswersCount(+1, post, posts) addedPosts() = addedPosts.now + post.hash deletedPosts() = deletedPosts.now - post.hash updateAnswersCount(+1, post, categories) } } def deleteTree(post: NanoboardMessageData): Unit = { deleteBy(post, p ⇒ p.hash == post.hash || p.parent.contains(post.hash)) } def deleteSingle(post: NanoboardMessageData): Unit = { deleteBy(post, _.hash == post.hash) } def updatePosts(): Unit = { val future = context.now match { case NanoboardContext.Categories ⇒ Future.successful(categories.now) case NanoboardContext.Thread(hash, offset) ⇒ NanoboardApi.thread(hash, offset, postsPerPage) case NanoboardContext.Recent(offset) ⇒ NanoboardApi.recent(offset, postsPerPage) case NanoboardContext.Pending(offset) ⇒ NanoboardApi.pending(offset, postsPerPage) } future.onComplete { case Success(posts) ⇒ this.addedPosts() = Set.empty this.deletedPosts() = Set.empty this.posts() = posts case Failure(exc) ⇒ Notifications.error(exc)(locale.updateError, Layout.topRight) } } def updateCategories(): Unit = { NanoboardApi.categories().onComplete { case Success(categories) ⇒ this.categories() = categories case Failure(exc) ⇒ Notifications.error(exc)(locale.updateError, Layout.topRight) } } private[this] def updateAnswersCount(i: Int, post: NanoboardMessageData, posts: Var[Vector[NanoboardMessageData]]): Unit = { posts() = posts.now.collect { case msg @ NanoboardMessageData(_, _, hash, _, answers, _, _) if post.parent.contains(hash) ⇒ msg.copy(answers = answers + i) case msg ⇒ msg } } private[this] def deleteBy(post: NanoboardMessageData, f: (NanoboardMessageData) ⇒ Boolean): Unit = { if (!deletedPosts.now.contains(post.hash)) { context.now match { case NanoboardContext.Thread(post.hash, _) ⇒ context() = post.parent.fold[NanoboardContext](NanoboardContext.Categories)(NanoboardContext.Thread(_)) deletedPosts() = deletedPosts.now + post.hash case NanoboardContext.Thread(hash, _) ⇒ val (opPost, answers) = posts.now.partition(_.hash == hash) val filtered = answers.filterNot(f) posts() = opPost.map(p ⇒ p.copy(answers = p.answers - 1)) ++ filtered deletedPosts() = deletedPosts.now ++ answers.diff(filtered).map(_.hash) case _ ⇒ val current = posts.now val filtered = current.filterNot(f) posts() = filtered deletedPosts() = deletedPosts.now ++ current.diff(filtered).map(_.hash) } updateAnswersCount(-1, post, posts) categories() = categories.now.filterNot(f) updateAnswersCount(-1, post, categories) addedPosts() = addedPosts.now - post.hash } } // Initialization private[this] def initialize(): Unit = { context.foreach(_ ⇒ updatePosts()) categories.foreach { categories ⇒ if (context.now == NanoboardContext.Categories && posts.now != categories) { addedPosts() = Set.empty deletedPosts() = Set.empty posts() = categories } } updateCategories() } initialize() } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/styles/BoardStyle.scala version [9407a3a16d].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
package com.karasiq.nanoboard.frontend.styles import scala.scalajs.js.UndefOr import org.scalajs.dom._ import rx._ import scalatags.JsDom.tags2 import scalatags.JsDom.all._ import scalatags.stylesheet.{StyleSheet, _} import com.karasiq.bootstrap.Bootstrap.default._ trait BoardStyle extends StyleSheet { override final def customSheetName: Option[String] = Some("nanoboard") def body: Cls def post: Cls def postInner: Cls def postId: Cls def postLink: Cls def input: Cls def submit: Cls def spoiler: Cls def greenText: Cls } object BoardStyle { lazy val styles: Seq[BoardStyle] = { Vector(Makaba, Futaba, Burichan, Muon, Neutron, Gurochan) } def fromString(style: String): BoardStyle = { styles.find(_.toString == style).getOrElse(Makaba) } def selector: Selector = { new Selector() } final class Selector extends BootstrapHtmlComponent { val style: Var[BoardStyle] = Var { val styleName: UndefOr[String] = window.localStorage.getItem("nanoboard-style") styleName.map(fromString).getOrElse(Makaba) } style.foreach { style ⇒ window.localStorage.setItem("nanoboard-style", style.toString) } override def renderTag(md: Modifier*) = { tags2.style(style.map(_.styleSheetText), CommonStyles.styleSheetText, md) } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/styles/Burichan.scala version [2feaf19197].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
package com.karasiq.nanoboard.frontend.styles import scalatags.Text.all._ object Burichan extends BoardStyle { initStyleSheet() val body = cls( color := "#000000", backgroundColor := "#EEF2FF" ) val post = cls( minWidth := 40.pct, maxWidth := 100.pct, border := "solid 1px #CCCCCC", borderRadius := 2.px, display.`inline-block`, background := "#D6DAF0", margin := 0.25.em, clear.both, padding := "0.5em 1.5em" ) val postInner = cls( marginBottom := 0.5.em, fontSize := 0.9.em, fontFamily := "Verdana,sans-serif" ) val postId = cls( color := "#789922", marginRight := 0.5.em ) val postLink = cls( color := "#34345C", &.hover( color.red ), cursor.pointer, marginRight := 0.5.em ) val input = cls() val submit = cls() val greenText = cls( color.green, fontSize := 90.pct, lineHeight := 2.em ) val spoiler = cls( textDecoration.none, color := "#9988EE", background := "#9988EE", &.hover( color := "#34345C" ) ) override def toString: String = { "Burichan" } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/styles/CommonStyles.scala version [3e1bb9106d].
> > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.karasiq.nanoboard.frontend.styles import scalatags.Text.all._ import scalatags.stylesheet._ object CommonStyles extends StyleSheet { initStyleSheet() val flatScroll = cls( overflowX.hidden, overflowY.auto, whiteSpace.`pre-wrap`, wordWrap.`break-word`, new Selector(Seq("::-webkit-scrollbar")).apply(display.none) ) } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/styles/Futaba.scala version [2ceb8b2523].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
package com.karasiq.nanoboard.frontend.styles import scalatags.Text.all._ object Futaba extends BoardStyle { initStyleSheet() val body = cls( color := "#800000", backgroundColor := "#FFFFEE" ) val post = cls( minWidth := 40.pct, maxWidth := 100.pct, border := "solid 1px #F0D0B6", borderRadius := 2.px, display.`inline-block`, background := "#F0E0D6", color := "#800000", margin := 0.25.em, clear.both, padding := "0.5em 1.5em" ) val postInner = cls( marginBottom := 0.5.em, fontSize := 0.9.em, fontFamily := "Verdana,sans-serif" ) val postId = cls( color := "#789922", marginRight := 0.5.em ) val postLink = cls( color := "#0000EE", &.hover( color.red ), cursor.pointer, marginRight := 0.5.em ) val input = cls() val submit = cls() val greenText = cls( color.green, fontSize := 90.pct, lineHeight := 2.em ) val spoiler = cls( textDecoration.none, color := "#F0D0B6", background := "#F0D0B6", &.hover( color := "#0000EE" ) ) override def toString: String = { "Futaba" } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/styles/Gurochan.scala version [ec6d10bb4c].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
package com.karasiq.nanoboard.frontend.styles import scalatags.Text.all._ object Gurochan extends BoardStyle { initStyleSheet() val body = cls( color := "#000000", backgroundColor := "#EDDAD2" ) val post = cls( minWidth := 40.pct, maxWidth := 100.pct, border := "1px solid #CA927B", borderRadius := 2.px, display.`inline-block`, background := "#D9AF9E", margin := 0.25.em, clear.both, padding := "0.5em 1.5em" ) val postInner = cls( marginBottom := 0.5.em, fontSize := 0.9.em, fontFamily := "Trebuchet MS, Verdana, sans-serif" ) val postId = cls( color := "#789922", marginRight := 0.5.em ) val postLink = cls( color := "#34345C", &.hover( color := "#DD0000" ), cursor.pointer, marginRight := 0.5.em ) val input = cls() val submit = cls() val greenText = cls( color := "#AF0A0F", fontSize := 90.pct, lineHeight := 2.em ) val spoiler = cls( textDecoration.none, color := "#CA927B", background := "#CA927B", &.hover( color := "#34345C" ) ) override def toString: String = { "Gurochan" } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/styles/Makaba.scala version [669afb7882].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
package com.karasiq.nanoboard.frontend.styles import scalatags.Text.all._ object Makaba extends BoardStyle { initStyleSheet() val body = cls( color := "#333333", backgroundColor := "#EEEEEE" ) val post = cls( minWidth := 40.pct, maxWidth := 100.pct, border := "solid 1px #CCCCCC", borderRadius := 2.px, display.`inline-block`, background := "#DDDDDD", color := "#333333", margin := 0.25.em, clear.both, padding := "0.5em 1.5em" ) val postInner = cls( marginBottom := 0.5.em, fontSize := 0.9.em, fontFamily := "Verdana,sans-serif" ) val postId = cls( color := "#789922", marginRight := 0.5.em ) val postLink = cls( color := "#FF6600", &.hover( color := "#0066FF" ), cursor.pointer, marginRight := 0.5.em ) val input = cls() val submit = cls() val greenText = cls( color.green, fontSize := 90.pct, lineHeight := 2.em ) val spoiler = cls( textDecoration.none, color := "#BBBBBB", background := "#BBBBBB", &.hover( color := "#333333" ) ) override def toString: String = { "Makaba" } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/styles/Muon.scala version [9d5ad83957].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
package com.karasiq.nanoboard.frontend.styles import scalatags.Text.all._ import scalatags.generic.Style object Muon extends BoardStyle { initStyleSheet() val body = cls( color := "#9B8165", background := "scroll #211F1A url('/img/muon_bg.jpg') repeat" ) val post = cls( minWidth := 40.pct, maxWidth := 100.pct, border := "solid 1px #34352D", borderRadius := 2.px, display.`inline-block`, background := "url('/img/muon_posts.jpg') #292825", color := "#9B8165", margin := 0.25.em, clear.both, padding := "0.5em 1.5em" ) val postInner = cls( fontFamily := "Verdana,sans-serif", marginBottom := 0.5.em, fontSize := 1.em ) val postId = cls( color := "#789922", marginRight := 0.5.em ) val postLink = cls( color := "#FFD97A", &.hover( color := "#FCE236" ), cursor.pointer, marginRight := 0.5.em ) val input = cls( background := "#3E3C38 url('/img/muon_inputs.jpg') !important", color := "#FEC77D!important", border := "1px solid #44453D" ) val submit = cls( Style("webkitAppearance", "-webkit-appearance") := "none!important", backgroundImage := "-webkit-gradient(linear,center top,center bottom,from(#4A4A4A),color-stop(25%,#313131),color-stop(50%,#292929),color-stop(75%,#313131),to(#4A4A4A))!important", backgroundColor := "#333333!important", borderRadius := "5px!important", borderBottom := "1px solid #151515!important", borderTop := "1px solid #151515!important", borderLeft := "1px solid #000000!important", borderRight := "1px solid #000000!important", color := "#AAAAAA!important" ) val greenText = cls( color.green, fontSize := 90.pct, lineHeight := 2.em ) val spoiler = cls( textDecoration.none, color := "#454545", background := "#454545", opacity := 0.5, &.hover( color := "#FFD97A" ) ) override def toString: String = { "Muon" } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/styles/Neutron.scala version [7cfde88325].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
package com.karasiq.nanoboard.frontend.styles import scalatags.Text.all._ import scalatags.generic.Style object Neutron extends BoardStyle { initStyleSheet() val body = cls( color := "#698CC0", backgroundColor := "#212121" ) val post = cls( minWidth := 40.pct, maxWidth := 100.pct, border := "solid 1px #575757", borderRadius := 2.px, display.`inline-block`, background := "#212121", color := "#698CC0", margin := 0.25.em, clear.both, padding := "0.5em 1.5em" ) val postInner = cls( fontFamily := "Trebuchet MS,Trebuchet,tahoma,serif", marginBottom := 0.5.em, fontSize := 1.em ) val postId = cls( color := "#789922", marginRight := 0.5.em ) val postLink = cls( color := "#C9BE89", &.hover( color := "#EEFEBB" ), cursor.pointer, marginRight := 0.5.em ) val input = cls( backgroundColor := "#111111 !important", color := "#CCCCCC", border := "2px solid #545454!important" ) val submit = cls( Style("webkitAppearance", "-webkit-appearance") := "none!important", backgroundColor := "#333333!important", backgroundImage := "-webkit-gradient(linear,center top,center bottom,from(#4A4A4A),color-stop(25%,#313131),color-stop(50%,#292929),color-stop(75%,#313131),to(#4A4A4A))!important", borderRadius := "5px!important", borderBottom := "1px solid #151515!important", borderTop := "1px solid #151515!important", borderLeft := "1px solid #000000!important", borderRight := "1px solid #000000!important", color := "#AAAAAA!important", fontWeight := "bold!important" ) val greenText = cls( color.green, fontSize := 90.pct, lineHeight := 2.em ) val spoiler = cls( textDecoration.none, color := "#575757", background := "#575757", &.hover( color := "#C9BE89" ) ) override def toString: String = { "Neutron" } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/utils/Blobs.scala version [5d2de217eb].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
package com.karasiq.nanoboard.frontend.utils import scala.concurrent.{ExecutionContext, Future, Promise} import scala.scalajs.js import scala.scalajs.js.typedarray.Uint8Array import org.scalajs.dom import org.scalajs.dom.{Blob, Event} import org.scalajs.dom.raw._ import scalatags.JsDom.all._ /** * Blob/file utility */ object Blobs { def fromBytes(data: Array[Byte], contentType: String = ""): Blob = { import scala.scalajs.js.JSConverters._ val array = new Uint8Array(data.toJSArray) new Blob(js.Array(array), BlobPropertyBag(contentType)) } def fromChars(data: Array[Char], contentType: String = ""): Blob = { fromBytes(data.map(_.toByte), contentType) } def fromString(data: String, contentType: String = ""): Blob = { fromChars(data.toCharArray, contentType) } def fromBase64(base64: String, contentType: String = ""): Blob = { fromString(dom.window.atob(base64), contentType) } def saveBlob(blob: Blob, fileName: String): Unit = { val url = URL.createObjectURL(blob) val anchor = a(href := url, attr("download") := fileName, target := "_blank", display.none).render dom.document.body.appendChild(anchor) dom.window.setTimeout(() ⇒ { dom.document.body.removeChild(anchor) URL.revokeObjectURL(url) }, 500) anchor.click() } def asUrl(blob: Blob): String = { URL.createObjectURL(blob) } def asDataURL(blob: Blob): Future[String] = { val promise = Promise[String] val reader = new FileReader reader.readAsDataURL(blob) reader.onloadend = (_: ProgressEvent) ⇒ { promise.success(reader.result.asInstanceOf[String]) } reader.onerror = (e: Event) ⇒ { promise.failure(new IllegalArgumentException(e.toString)) } promise.future } def asBase64(blob: Blob)(implicit ec: ExecutionContext): Future[String] = { asDataURL(blob).map(_.split(",", 2).last) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/utils/CancelledException.scala version [dd91e190e1].
> > > |
1 2 3 |
package com.karasiq.nanoboard.frontend.utils case object CancelledException extends Exception("Operation is cancelled") |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/utils/Images.scala version [557b5cc06e].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
package com.karasiq.nanoboard.frontend.utils import scala.concurrent.{Future, Promise} import scala.scalajs.js import scala.scalajs.js.annotation.JSGlobal import org.scalajs.dom.{Blob, ErrorEvent} @js.native private[utils] trait ImageUtil extends js.Object { def sharpen(ctx: js.Dynamic, width: Int, height: Int, sharpness: Double): Unit = js.native def drawImage(file: Blob, compress: Boolean = true, format: String = "image/jpeg", scale: Double, quality: Double, sharpness: Double, success: js.Function, error: js.Function): Unit = js.native } object Images { @js.native @JSGlobal("img2base64") private object ImageUtil extends ImageUtil def compress(data: Blob, format: String = "image/jpeg", scale: Int = 100, quality: Int = 100, sharpness: Int = 100): Future[String] = { assert(sharpness > 0 && scale > 0 && quality > 0 && quality <= 100) val promise = Promise[String] ImageUtil.drawImage(data, compress = true, format, scale, quality, sharpness, { (url: String) ⇒ promise.success(url.split(",", 2).last) }, { (e: ErrorEvent) ⇒ promise.failure(new IllegalArgumentException(e.message)) }) promise.future } def isWebpSupported: Boolean = { js.Dynamic.global.Modernizr.webp.toString == "true" // Boolean bug } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/utils/Mouse.scala version [e5ede7f7ac].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package com.karasiq.nanoboard.frontend.utils import scala.scalajs.js import org.scalajs.dom import org.scalajs.dom.{Element, MouseEvent} import scalatags.JsDom.all._ object Mouse { def scroll(selector: String): Boolean = { import org.scalajs.jquery.jQuery val e = jQuery(selector) if (e.length > 0) { jQuery("html, body").animate(js.Dynamic.literal( scrollTop = e.offset().asInstanceOf[js.Dynamic].top ), 800) true } else { false } } def relative(xOffset: Double = 0, yOffset: Double = 0): Modifier = { Seq[Modifier]( position.fixed, overflow.hidden, new Modifier { override def applyTo(t: Element): Unit = { dom.window.addEventListener("mousemove", (e: MouseEvent) ⇒ { val style = t.asInstanceOf[dom.html.Element].style style.left = (e.clientX + xOffset) + "px" style.top = (e.clientY + yOffset) + "px" }) } } ) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/utils/Notifications.scala version [730817346b].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
package com.karasiq.nanoboard.frontend.utils import java.io.{PrintWriter, StringWriter} import org.scalajs.dom.console // From proxychecker frontend object Notifications { import scala.scalajs.js.{Array ⇒ JsArray} import scala.scalajs.js.Dynamic.{global ⇒ js, literal ⇒ lt} private def defaultTimeout: Int = { 1800 } sealed trait Layout { def apply(): String } object Layout { private final class LayoutImpl(string: String) extends Layout { override def apply(): String = string } def top: Layout = new LayoutImpl("top") def topLeft: Layout = new LayoutImpl("topLeft") def topRight: Layout = new LayoutImpl("topRight") def topCenter: Layout = new LayoutImpl("topCenter") def center: Layout = new LayoutImpl("center") def centerLeft: Layout = new LayoutImpl("centerLeft") def centerRight: Layout = new LayoutImpl("centerRight") def bottom: Layout = new LayoutImpl("bottom") def bottomLeft: Layout = new LayoutImpl("bottomLeft") def bottomRight: Layout = new LayoutImpl("bottomRight") def bottomCenter: Layout = new LayoutImpl("bottomCenter") } sealed class InfoMessage(`type`: String) { def apply(text: String, layout: Layout = Layout.top, timeout: Int = defaultTimeout): Unit = { Notifications.notify(text, `type`=`type`, layout=layout, timeout = Some(timeout)) } } sealed class ErrorMessage(cause: Option[Throwable]) extends InfoMessage("error") { private def formatException(title: String, exc: Option[Throwable]): String = { val stringWriter = new StringWriter(256) val printWriter = new PrintWriter(stringWriter) try { printWriter.println(title) exc.foreach(_.printStackTrace(printWriter)) printWriter.flush() stringWriter.toString } finally printWriter.close() } override def apply(text: String, layout: Layout = Layout.top, timeout: Int = defaultTimeout): Unit = { val formatted = formatException(text, cause) console.error(formatted) super.apply(formatted, layout, timeout) } } def alert: InfoMessage = new InfoMessage("alert") def success: InfoMessage = new InfoMessage("success") def error(cause: Throwable): InfoMessage = new ErrorMessage(Some(cause)) def warning: InfoMessage = new InfoMessage("warning") def info: InfoMessage = new InfoMessage("information") sealed class ConfirmationMessage { def apply(text: String, layout: Layout = Layout.top)(onSuccess: ⇒ Unit): Unit = { Notifications.notify(text, `type`="confirmation", layout=layout, buttons=JsArray( button("Ok", "btn btn-primary") { msg ⇒ msg.close() onSuccess }, button("Cancel", "btn btn-danger") { msg ⇒ msg.close() } )) } } def confirmation = new ConfirmationMessage private def button(text: String, addClass: String = "")(onClick: scalajs.js.Dynamic ⇒ Unit): scalajs.js.Dynamic = { lt(addClass=addClass, text=text, onClick=onClick) } private def notify(text: String, `type`: String = "alert", theme: String = "defaultTheme", layout: Layout = Layout.top, timeout: Option[Int] = None, buttons: JsArray[scalajs.js.Dynamic] = JsArray()) = { js.noty(lt( `type`=`type`, text = text, layout = layout(), theme = theme, timeout = if (timeout.nonEmpty) timeout.get else false, animation = lt( open = lt(height="toggle"), close = lt(height="toggle"), easing = "swing", // easing speed = 500 // opening & closing animation speed ), buttons = if (buttons.nonEmpty) buttons else false )) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/utils/PostParser.scala version [3aefb0d862].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
package com.karasiq.nanoboard.frontend.utils import org.parboiled2._ import com.karasiq.nanoboard.frontend.utils.PostDomValue._ sealed trait PostDomValue object PostDomValue { case class PostDomValues(seq: Seq[PostDomValue]) extends PostDomValue case class PlainText(underlying: String) extends PostDomValue case class BBCode(name: String, parameters: Map[String, String], inner: PostDomValue) extends PostDomValue case class ShortBBCode(name: String, value: String) extends PostDomValue } object PostParser { def parse(text: String, plainCodes: Set[String] = Set("md", "plain", "code", "file", "img", "video")): PostDomValue = { new PostParser(text, plainCodes).Message.run().getOrElse(PlainText(text)) } } class PostParser(val input: ParserInput, plainCodes: Set[String]) extends Parser { private def BBCodeParameter: Rule1[(String, String)] = rule { capture(oneOrMore(CharPredicate.Alpha)) ~ (('=' ~ '"' ~ capture(zeroOrMore(!'"' ~ ANY)) ~ '"') | push("")) ~> { (s1: String, s2: String) ⇒ s1 → s2 } } private def BBCodeParameters: Rule1[Map[String, String]] = rule { zeroOrMore(' ' ~ BBCodeParameter) ~> { (parameters: Seq[(String, String)]) ⇒ parameters.toMap } } private def BBCodeAnyTag: Rule0 = rule { '[' ~ optional('/') ~ oneOrMore(CharPredicate.Alpha) ~ (('=' ~ oneOrMore(!']' ~ ANY)) | BBCodeParameters) ~ ']' } private def BBCodeOpenTag: Rule2[String, Map[String, String]] = rule { '[' ~ capture(oneOrMore(CharPredicate.Alpha)) ~ BBCodeParameters ~ ']' } private def BBCodeCloseTag(tag: String): Rule0 = rule { "[/" ~ tag ~ "]" } def BBCode: Rule1[PostDomValue.BBCode] = rule { BBCodeOpenTag ~> { (tag: String, parameters: Map[String, String]) ⇒ push(tag) ~ push(parameters) ~ ((test(plainCodes.contains(tag.toLowerCase) || parameters.contains("plain")) ~ capture(oneOrMore(!BBCodeCloseTag(tag) ~ ANY)) ~> PostDomValue.PlainText) | FormattedText) ~ BBCodeCloseTag(tag) } ~> PostDomValue.BBCode } def ShortBBCode: Rule1[PostDomValue.ShortBBCode] = rule { '[' ~ capture(oneOrMore(CharPredicate.Alpha)) ~ '=' ~ capture(oneOrMore(!']' ~ ANY)) ~ ']' ~> PostDomValue.ShortBBCode } def PlainText: Rule1[PostDomValue.PlainText] = rule { capture(oneOrMore(!BBCodeAnyTag ~ ANY)) ~> PostDomValue.PlainText } def FormattedText: Rule1[PostDomValues] = rule { zeroOrMore(BBCode | ShortBBCode | PlainText) ~> PostDomValues } def Message = rule { FormattedText ~ EOI } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/frontend/src/main/scala/com/karasiq/nanoboard/frontend/utils/RxLocation.scala version [c2182edec3].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
package com.karasiq.nanoboard.frontend.utils import scala.scalajs.js.UndefOr import org.scalajs.dom import org.scalajs.dom.window import org.scalajs.jquery.jQuery import rx._ final class RxLocation(implicit ctx: Ctx.Owner) { private def readHash(hash: UndefOr[String]): Option[String] = { hash .filter(_.nonEmpty) .map(_.tail) .toOption } val hash: Var[Option[String]] = Var(readHash(window.location.hash)) hash.triggerLater { window.location.hash = hash.now.fold("")("#" + _) } jQuery(dom.window).on("hashchange", () ⇒ { hash() = readHash(window.location.hash) }) } object RxLocation { def apply()(implicit ctx: Ctx.Owner): RxLocation = { new RxLocation() } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/images/screenshot.png version [21aa3fb705].
cannot compute difference between binary files
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/resources/reference.conf version [479a20a76f].
> > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
nanoboard { version = 1.3.2 client-version = karasiq-nanoboard v${nanoboard.version} encryption-key = "nano3" bitmessage { chan-address = "BM-2cWzzuoiF7pxchwiF5obVmXaQKFywETu7k" } message-pack-format = text pow { version = v1 offset = 3 length = 3 threshold = 1 } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/NanoboardCategory.scala version [a5613fd097].
> > > > > > > > |
1 2 3 4 5 6 7 8 |
package com.karasiq.nanoboard /** * Nanoboard category data * @param hash Category hash * @param name Category alias */ case class NanoboardCategory(hash: String, name: String) |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/NanoboardLegacy.scala version [f5f5d1ea33].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
package com.karasiq.nanoboard import java.io.File /** * Official implementation compatibility util * @see [[https://github.com/nanoboard/nanoboard]] */ object NanoboardLegacy { /** * Reads places.txt file in `nanoboard/1.*` client format * @param file File path */ def placesFromTxt(file: String): Vector[String] = { if (new File(file).isFile) { val source = io.Source.fromFile(file, "UTF-8") try { source.getLines() .filter(_.nonEmpty) .toVector } finally source.close() } else { Vector.empty } } /** * Reads categories.txt file in `nanoboard/1.*` client format * @param file File path */ def categoriesFromTxt(file: String): Vector[NanoboardCategory] = { if (new File(file).isFile) { val source = io.Source.fromFile(file, "UTF-8") try { source .getLines() .filter(_.nonEmpty) .grouped(2) .collect { case Seq(hash, name) ⇒ NanoboardCategory(hash, name) } .toVector } finally source.close() } else { Vector.empty } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/NanoboardMessage.scala version [55666e7c7f].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
package com.karasiq.nanoboard import scala.util.Try import akka.util.ByteString import com.karasiq.nanoboard.encoding.NanoboardCrypto.{sha256, BCDigestOps} import com.karasiq.nanoboard.encoding.formats.{CBORMessagePackFormat, MessagePackFormat, TextMessagePackFormat} import com.karasiq.nanoboard.utils.{ByteStringOps, _} case class NanoboardMessage(parent: String, text: String, pow: ByteString = NanoboardMessage.NoPOW, signature: ByteString = NanoboardMessage.NoSignature) { val hash: String = sha256.digest(ByteString(parent + NanoboardMessage.textWithSignatureTags(this))).take(16).toHexString() } object NanoboardMessage extends MessagePackFormat { // Constants val HashLength = 32 val HashFormat = "(?i)[a-f0-9]{32}".r val POWLength = 128 val SignatureLength = 64 val NoPOW = ByteString(Array.fill[Byte](POWLength)(0)) val NoSignature = ByteString(Array.fill[Byte](SignatureLength)(0)) //noinspection ScalaDeprecation override def parseMessages(payload: ByteString): Vector[NanoboardMessage] = { Try(CBORMessagePackFormat.parseMessages(payload)) .orElse(Try(TextMessagePackFormat.parseMessages(payload))) .get } override def writeMessages(messages: Seq[NanoboardMessage]): ByteString = { TextMessagePackFormat.writeMessages(messages) // CBORMessagePackFormat.writeMessages(messages) } private[nanoboard] def getPOWTag(pow: ByteString) = { if (pow.isEmpty || pow == NoPOW) "" else s"[pow=${pow.toHexString()}]" } private[nanoboard] def getSignatureTag(signature: ByteString) = { if (signature.isEmpty || signature == NoSignature) "" else s"[sign=${signature.toHexString()}]" } private[nanoboard] def stripSignatureTags(message: String): (String, Option[ByteString], Option[ByteString]) = { val regex = "(?i)\\[pow=([0-9a-f]+)\\]\\[sign=([0-9a-f]{128})\\]".r regex.findFirstMatchIn(message) match { case Some(m @ regex(pow, sign)) ⇒ (message.take(m.start), Some(ByteString.fromHexString(pow)), Some(ByteString.fromHexString(sign))) case _ ⇒ (message, None, None) } } private[nanoboard] def textWithSignatureTags(m: NanoboardMessage): String = { m.text + getPOWTag(m.pow) + getSignatureTag(m.signature) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/NanoboardMessageGenerator.scala version [d5ee3872f0].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
package com.karasiq.nanoboard import java.time.format.{DateTimeFormatter, DateTimeFormatterBuilder, TextStyle} import java.time.temporal.ChronoField import java.time.{ZoneId, ZonedDateTime} import java.util.Locale import com.typesafe.config.{Config, ConfigFactory} import scala.util.Try object NanoboardMessageGenerator { def fromConfig(config: Config) = { new NanoboardMessageGenerator(config.getString("client-version"), Try(ZoneId.of(config.getString("default-time-zone"))).getOrElse(ZoneId.systemDefault())) } def apply(config: Config = ConfigFactory.load()) = { fromConfig(config.getConfig("nanoboard")) } } /** * Nanoboard message generator * @param clientVersion Client version string * @param timeZone Timestamp time zone */ class NanoboardMessageGenerator(clientVersion: String, timeZone: ZoneId) { /** * Message timestamp format */ protected val timestampFormat = new DateTimeFormatterBuilder() .appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT) .appendLiteral(", ") .appendValue(ChronoField.DAY_OF_MONTH) .appendLiteral('/') .appendText(ChronoField.MONTH_OF_YEAR, TextStyle.SHORT) .appendLiteral('/') .appendValue(ChronoField.YEAR) .appendLiteral(", ") .append(DateTimeFormatter.ISO_LOCAL_TIME) .appendLiteral(" (") .appendZoneOrOffsetId() .appendLiteral(")") .toFormatter(Locale.ENGLISH) /** * Creates new message with timestamp and client header * @param parent Parent message hash * @param text Message text * @return Created message */ def newMessage(parent: String, text: String): NanoboardMessage = { val header = s"[g]${timestampFormat.format(ZonedDateTime.now(timeZone))}, client: $clientVersion[/g]" NanoboardMessage(parent, s"$header\n$text") } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/captcha/NanoboardCaptcha.scala version [bd706c6666].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
package com.karasiq.nanoboard.captcha import java.awt.Color import java.awt.image.BufferedImage import scala.concurrent.{ExecutionContext, Future} import akka.util.ByteString import com.karasiq.nanoboard.NanoboardMessage import com.karasiq.nanoboard.captcha.internal.{Constants, Ed25519} import com.karasiq.nanoboard.captcha.storage.NanoboardCaptchaSource import com.karasiq.nanoboard.encoding.NanoboardCrypto._ import com.karasiq.nanoboard.utils._ /** * Nanoboard captcha block * @param publicKey EdDSA public key * @param seed XORed seed * @param image Encoded captcha image * @see [[https://github.com/nanoboard/nanoboard/commit/ef747596802919c270d0de61bd9bcdf319c787f0]] * @note {{{ * UnsignedMessage = ReplyTo + Text + PowValue * Signature = Sign(UnsignedMessage, PrivateKeyFromSeed(DecryptedSeed)) * }}} */ case class NanoboardCaptcha(publicKey: ByteString, seed: ByteString, image: ByteString) { require(publicKey.length == Constants.PUBLIC_KEY_LENGTH && seed.length == Constants.SEED_LENGTH && image.length == Constants.IMAGE_LENGTH, "Invalid data") def toBytes: ByteString = { publicKey ++ seed ++ image } private def decryptSeed(answer: String): ByteString = { val result = new Array[Byte](seed.length) val hashedAnswer = sha512.digest(ByteString(answer + publicKey.toHexString())) // Hex string is lowercase for (i ← seed.indices) { result(i) = (seed(i) ^ hashedAnswer(i & 63)).toByte } ByteString(result) } /** * Calculates the signature for post * @param post Unsigned message * @param guess Captcha answer * @return EdDSA digital signature */ def signature(post: ByteString, guess: String): ByteString = { val privateKey = Ed25519.privateKey(decryptSeed(guess)) Ed25519.sign(privateKey, post) } /** * Verifies the signature for post * @param post Unsigned message * @param signature EdDSA digital signature * @return Is signature valid */ def verify(post: ByteString, signature: ByteString): Boolean = { Ed25519.verify(Ed25519.publicKey(publicKey), post, signature) } } /** * Nanoboard captcha utility */ object NanoboardCaptcha { /** * Reads captcha block from bytes * @param byteString Encoded captcha block */ def fromBytes(byteString: ByteString): NanoboardCaptcha = { assert(byteString.length == Constants.BLOCK_LENGTH, "Invalid captcha block length") NanoboardCaptcha(byteString.take(Constants.PUBLIC_KEY_LENGTH), byteString.drop(Constants.PUBLIC_KEY_LENGTH).take(Constants.SEED_LENGTH), byteString.drop(Constants.PUBLIC_KEY_LENGTH + Constants.SEED_LENGTH).take(Constants.IMAGE_LENGTH)) } /** * Verifies POW and signature of the message * @param message Signed message * @param pow Proof-of-work calculator * @param captcha Captcha storage * @param ec Execution context * @return Is message valid */ def verify(message: NanoboardMessage, pow: NanoboardPow, captcha: NanoboardCaptchaSource)(implicit ec: ExecutionContext): Future[Boolean] = { Future.reduce(Seq( Future(pow.verify(message)), captcha(pow.getCaptchaIndex(message, captcha.length)).map(_.verify(pow.getSignPayload(message), message.signature)) ))(_ && _) } private def renderBufferedImage(captcha: NanoboardCaptcha, width: Int = 50, height: Int = 20): BufferedImage = { var bii, byi = 0 val image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) for (x ← 0 until width; y ← 0 until height) { val color = if ((captcha.image(byi) & (1 << bii).toByte) != 0) Color.BLACK else Color.WHITE bii += 1 if (bii >= 8) { bii = 0 byi += 1 } image.setRGB(x, y, color.getRGB) } image } /** * Renders encoded captcha image to png * @param captcha Encoded captcha image * @param width Output width * @param height Output height * @return Captcha image, rendered as png */ def render(captcha: NanoboardCaptcha, width: Int = 50, height: Int = 20): ByteString = { renderBufferedImage(captcha, width, height).toBytes("png") } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/captcha/NanoboardPow.scala version [711a64017e].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
package com.karasiq.nanoboard.captcha import java.util.concurrent.Executors import scala.concurrent.{ExecutionContext, Future} import akka.util.ByteString import com.typesafe.config.{Config, ConfigFactory} import com.karasiq.nanoboard.NanoboardMessage import com.karasiq.nanoboard.captcha.impl.{NanoboardPowV1, NanoboardPowV2} trait NanoboardPow { /** * Verifies the message proof-of-work value * @param message Message with calculated POW * @return Is POW valid */ def verify(message: NanoboardMessage): Boolean /** * Calculates nanoboard proof-of-work value * @param message Message without a calculated POW * @return Proof-of-work value */ def calculate(message: NanoboardMessage): Future[ByteString] /** * Calculates captcha index * @param message Unsigned message * @param max Maximum captcha index * @return Index of the captcha to be solved */ def getCaptchaIndex(message: NanoboardMessage, max: Int): Int /** * Data to sign/verify with the EdDSA digital signature * @param message Message * @return ReplyTo + Text + PowValue */ def getSignPayload(message: NanoboardMessage): ByteString } object NanoboardPow { /** * Creates nanoboard POW calculator from config * @param config Configuration object * @return Proof-of-work calculator */ def apply(config: Config = ConfigFactory.load())(implicit ec: ExecutionContext): NanoboardPow = { val offset = config.getInt("nanoboard.pow.offset") val length = config.getInt("nanoboard.pow.length") val threshold = config.getInt("nanoboard.pow.threshold") config.getString("nanoboard.pow.version") match { case "v1" ⇒ new NanoboardPowV1(offset, length, threshold) case "v2" ⇒ new NanoboardPowV2(offset, length, threshold) } } /** * Provides execution context, optimised for nanoboard proof-of-work calculation * @return Work stealing pool execution context */ def executionContext() = { ExecutionContext.fromExecutorService(Executors.newWorkStealingPool(Runtime.getRuntime.availableProcessors())) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/captcha/impl/NanoboardPowV1.scala version [f8acc224cf].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
package com.karasiq.nanoboard.captcha.impl import java.util.concurrent.RejectedExecutionException import scala.concurrent.{ExecutionContext, Future, Promise} import scala.util.Random import akka.util.ByteString import org.bouncycastle.crypto.digests.SHA256Digest import com.karasiq.nanoboard.NanoboardMessage import com.karasiq.nanoboard.captcha.NanoboardPow import com.karasiq.nanoboard.encoding.NanoboardCrypto._ import com.karasiq.nanoboard.utils.ByteStringOps private object NanoboardPowV1 { def withoutXMG(text: String) = { val regex = "\\[xmg=[^\\]]*\\]".r regex.replaceAllIn(text, { rm ⇒ sha256.digest(ByteString(rm.group(0))).toHexString() }) } } /** * Nanoboard proof-of-work calculator * @param offset Hash offset for bytes verification * @param length Required consequent bytes * @param threshold Maximum byte value * @see [[https://github.com/nanoboard/nanoboard/commit/ef747596802919c270d0de61bd9bcdf319c787f0]] * @note {{{ * PowValue = "[pow=$Hex(RandomBytes(128))]" * PowHash = SHA256(ReplyTo + Text + PowValue) * }}} */ final class NanoboardPowV1(offset: Int, length: Int, threshold: Int)(implicit ec: ExecutionContext) extends NanoboardPow { // For verification with cached SHA256 state private def verify(update: ByteString, md: SHA256Digest): Boolean = { val hash = md.digest(update) var maxLength = 0 for (i ← offset until hash.length if maxLength < this.length) { if (java.lang.Byte.toUnsignedInt(hash(i)) <= threshold) maxLength += 1 else maxLength = 0 } maxLength >= this.length } /** * Verifies the message proof-of-work value * @param message Message with calculated POW * @return Is POW valid */ def verify(message: NanoboardMessage): Boolean = { verify(getVerifyPayload(message), sha256) } /** * Calculates nanoboard proof-of-work value * @param message Message without a calculated POW * @return `[pow]` tag to be appended to message */ def calculate(message: NanoboardMessage): Future[ByteString] = { val payload = getPOWPayload(message) val open = ByteString("[pow=") val preHashed = sha256.updated(payload ++ open) val close = ByteString("]") val result = Promise[ByteString] def submitTasks(): Unit = { try { Future.sequence(for (_ ← 0 to 100) yield Future { val array = Array.ofDim[Byte](NanoboardMessage.POWLength) Random.nextBytes(array) val powValue = ByteString(array) val powHexString = ByteString(powValue.toHexString()) if (verify(powHexString ++ close, new SHA256Digest(preHashed))) { result.success(powValue) } }).foreach { _ ⇒ if (!result.isCompleted) { submitTasks() } } } catch { case _: RejectedExecutionException ⇒ // Pass case e: Throwable ⇒ result.failure(e) } } submitTasks() result.future } def getCaptchaIndex(message: NanoboardMessage, max: Int): Int = { sha256.digest(getVerifyPayload(message)).take(3).map(java.lang.Byte.toUnsignedInt) match { case Seq(b0, b1, b2) ⇒ (b0 + b1 * 256 + b2 * 256 * 256) % max } } def getSignPayload(message: NanoboardMessage) = { ByteString(message.parent + message.text + NanoboardMessage.getPOWTag(message.pow)) } private[this] def getPOWPayload(message: NanoboardMessage) = { ByteString(message.parent + NanoboardPowV1.withoutXMG(message.text)) } private[this] def getVerifyPayload(message: NanoboardMessage) = { getPOWPayload(message) ++ ByteString(NanoboardMessage.getPOWTag(message.pow)) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/captcha/impl/NanoboardPowV2.scala version [005ddf73b3].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
package com.karasiq.nanoboard.captcha.impl import java.util.concurrent.RejectedExecutionException import scala.concurrent.{ExecutionContext, Future, Promise} import scala.util.Random import akka.util.ByteString import org.bouncycastle.crypto.digests.SHA256Digest import com.karasiq.nanoboard.NanoboardMessage import com.karasiq.nanoboard.captcha.NanoboardPow import com.karasiq.nanoboard.encoding.NanoboardCrypto.{sha256, BCDigestOps} /** * Nanoboard proof-of-work calculator * @param offset Hash offset for bytes verification * @param length Required consequent bytes * @param threshold Maximum byte value * @see [[https://github.com/nanoboard/nanoboard/commit/ef747596802919c270d0de61bd9bcdf319c787f0]] * @note {{{ * PowValue = RandomBytes(128) * PowHash = SHA256(ReplyTo + Text + PowValue) * }}} */ final class NanoboardPowV2(offset: Int, length: Int, threshold: Int)(implicit ec: ExecutionContext) extends NanoboardPow { // For verification with cached SHA256 state private def verify(bytes: ByteString, md: SHA256Digest): Boolean = { val hash = md.digest(bytes) var maxLength = 0 for (i ← offset until hash.length if maxLength < this.length) { if (java.lang.Byte.toUnsignedInt(hash(i)) <= threshold) maxLength += 1 else maxLength = 0 } maxLength >= this.length } /** * Verifies the message proof-of-work value * @param message Message with calculated POW * @return Is POW valid */ def verify(message: NanoboardMessage): Boolean = { verify(getSignPayload(message), sha256) } /** * Calculates nanoboard proof-of-work value * @param message Message without a calculated POW * @return Proof-of-work value */ def calculate(message: NanoboardMessage): Future[ByteString] = { val payload = getPOWPayload(message) val preHashed = sha256.updated(payload) val result = Promise[ByteString] def submitTasks(): Unit = { try { Future.sequence(for (_ ← 0 to 100) yield Future { val array = Array.ofDim[Byte](NanoboardMessage.POWLength) Random.nextBytes(array) val data = ByteString(array) if (verify(data, new SHA256Digest(preHashed))) { result.success(data) } }).foreach { _ ⇒ if (!result.isCompleted) { submitTasks() } } } catch { case _: RejectedExecutionException ⇒ // Pass case e: Throwable ⇒ result.failure(e) } } submitTasks() result.future } def getCaptchaIndex(message: NanoboardMessage, max: Int): Int = { sha256.digest(getSignPayload(message)).take(3).map(java.lang.Byte.toUnsignedInt) match { case Seq(b0, b1, b2) ⇒ (b0 + b1 * 256 + b2 * 256 * 256) % max } } def getSignPayload(message: NanoboardMessage): ByteString = { getPOWPayload(message) ++ message.pow } private[this] def getPOWPayload(message: NanoboardMessage) = { ByteString(message.parent + message.text) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/captcha/internal/Constants.scala version [6210998a75].
> > > > > > > > > |
1 2 3 4 5 6 7 8 9 |
package com.karasiq.nanoboard.captcha.internal private[captcha] object Constants { // Captcha pack sizes val PUBLIC_KEY_LENGTH = 32 val SEED_LENGTH = 32 val IMAGE_LENGTH = 125 val BLOCK_LENGTH = PUBLIC_KEY_LENGTH + SEED_LENGTH + IMAGE_LENGTH // 189 } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/captcha/internal/Ed25519.scala version [8e39782c25].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
package com.karasiq.nanoboard.captcha.internal import java.util.function.Supplier import akka.util.ByteString import net.i2p.crypto.eddsa.{EdDSAEngine, EdDSAPrivateKey, EdDSAPublicKey} import net.i2p.crypto.eddsa.spec.{EdDSANamedCurveTable, EdDSAPrivateKeySpec, EdDSAPublicKeySpec} /** * Ed25519 digital signature utility */ private[captcha] object Ed25519 { /** * Default curve specification */ private val curve25519 = EdDSANamedCurveTable.getByName("Ed25519") //noinspection ConvertExpressionToSAM private val tlEngine = ThreadLocal.withInitial(new Supplier[EdDSAEngine] { override def get(): EdDSAEngine = new EdDSAEngine() }) /** * Reads encoded public key * @param data Public key data * @return EdDSA public key */ def publicKey(data: ByteString): EdDSAPublicKey = { new EdDSAPublicKey(new EdDSAPublicKeySpec(data.toArray[Byte], curve25519)) } /** * Reads private key from seed * @param seed Private key 32-byte seed * @return EdDSA private key */ def privateKey(seed: ByteString): EdDSAPrivateKey = { new EdDSAPrivateKey(new EdDSAPrivateKeySpec(seed.toArray[Byte], curve25519)) } /** * Verifies EdDSA digital signature with the provided key * @param key EdDSA public key * @param data Signed data * @param signature EdDSA digital signature * @return Is signature valid */ def verify(key: EdDSAPublicKey, data: ByteString, signature: ByteString): Boolean = { val engine = tlEngine.get() engine.initVerify(key) engine.verifyOneShot(data.toArray, signature.toArray[Byte]) } /** * Signs data with the provided key * @param key EdDSA private key * @param data Data to sign * @return EdDSA digital signature */ def sign(key: EdDSAPrivateKey, data: ByteString): ByteString = { val engine = tlEngine.get() engine.initSign(key) ByteString(engine.signOneShot(data.toArray)) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/captcha/storage/NanoboardCaptchaFileSource.scala version [d1fa92fd09].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
package com.karasiq.nanoboard.captcha.storage import java.io.{Closeable, RandomAccessFile} import java.util.concurrent.Executors import akka.util.ByteString import com.karasiq.nanoboard.captcha.NanoboardCaptcha import com.karasiq.nanoboard.captcha.internal.Constants import org.apache.commons.io.IOUtils import scala.concurrent.{ExecutionContext, Future} /** * Nanoboard captcha file storage * @param file File path */ final class NanoboardCaptchaFileSource(file: String) extends NanoboardCaptchaSource with Closeable { private implicit val ec = ExecutionContext.fromExecutorService(Executors.newSingleThreadExecutor()) private val randomAccessFile = new RandomAccessFile(file, "r") private val buffer = new Array[Byte](Constants.BLOCK_LENGTH) def apply(index: Int): Future[NanoboardCaptcha] = { try { assert(index < this.length, s"Invalid index: $index, captcha blocks: $length") Future { randomAccessFile.seek(index.toLong * Constants.BLOCK_LENGTH) randomAccessFile.read(buffer, 0, Constants.BLOCK_LENGTH) NanoboardCaptcha.fromBytes(ByteString(buffer)) } } catch { case e: Throwable ⇒ Future.failed(e) } } override val length: Int = { val length = randomAccessFile.length() / Constants.BLOCK_LENGTH math.min(length, Int.MaxValue).toInt } /** * Closes this file */ override def close(): Unit = { ec.shutdownNow() IOUtils.closeQuietly(randomAccessFile) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/captcha/storage/NanoboardCaptchaMemorySource.scala version [c6ff955fda].
> > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package com.karasiq.nanoboard.captcha.storage import akka.util.ByteString import com.karasiq.nanoboard.captcha.NanoboardCaptcha import com.karasiq.nanoboard.captcha.internal.Constants import scala.concurrent.Future /** * Nanoboard captcha memory storage * @param data Encoded captcha blocks */ final class NanoboardCaptchaMemorySource(data: ByteString) extends NanoboardCaptchaSource { override def apply(index: Int): Future[NanoboardCaptcha] = { assert(index < this.length, s"Invalid index: $index, captcha blocks: $length") val offset: Int = index * Constants.BLOCK_LENGTH val block: ByteString = data.slice(offset, offset + Constants.BLOCK_LENGTH) assert(block.length == Constants.BLOCK_LENGTH, "Invalid captcha block") Future.successful(NanoboardCaptcha.fromBytes(block)) } override val length: Int = data.length / Constants.BLOCK_LENGTH } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/captcha/storage/NanoboardCaptchaSource.scala version [c1559cf000].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
package com.karasiq.nanoboard.captcha.storage import java.io.{ByteArrayOutputStream, InputStream} import akka.util.ByteString import com.karasiq.nanoboard.captcha.NanoboardCaptcha import org.apache.commons.io.IOUtils import scala.concurrent.Future /** * Nanoboard captcha source */ trait NanoboardCaptchaSource extends IndexedSeq[Future[NanoboardCaptcha]] { /** * Extracts the captcha block with specified index * @param index Captcha block index * @return Captcha block */ def apply(index: Int): Future[NanoboardCaptcha] /** * Captcha blocks count */ def length: Int } object NanoboardCaptchaSource { /** * Opens nanoboard captcha file storage * @param file File path * @return Nanoboard captcha file storage */ def fromFile(file: String) = { new NanoboardCaptchaFileSource(file) } /** * Opens encoded nanoboard captcha storage * @param data Captcha storage data * @return Nanoboard captcha memory storage */ def fromBytes(data: ByteString) = { new NanoboardCaptchaMemorySource(data) } /** * Reads input stream into memory and decodes as captcha storage * @param inputStream Input stream * @return Nanoboard captcha memory storage */ def fromInputStream(inputStream: InputStream) = { val outputStream = new ByteArrayOutputStream() try { IOUtils.copyLarge(inputStream, outputStream) fromBytes(ByteString(outputStream.toByteArray)) } finally { IOUtils.closeQuietly(outputStream) } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/encoding/DataEncodingStage.scala version [9857d4840d].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
package com.karasiq.nanoboard.encoding import akka.util.ByteString import scala.language.implicitConversions object DataEncodingStage { /** * Creates sequential data encoder, which is applied one after another in encoding, and backwards in decoding. */ implicit def stageSeqToStage(seq: Seq[DataEncodingStage]): DataEncodingStage = new DataEncodingStage { override def encode(data: ByteString): ByteString = { seq.foldLeft(data)((data, stage) ⇒ stage.encode(data)) } override def decode(data: ByteString): ByteString = { seq.foldRight(data)((stage, data) ⇒ stage.decode(data)) } override def toString: String = { s"Sequential(${seq.mkString(", ")})" } } } /** * Generic data encoding stage */ trait DataEncodingStage { /** * Encodes data * @param data Source data * @return Encoded data */ def encode(data: ByteString): ByteString /** * Decodes encoded data * @param data Previously encoded data * @return Source data */ def decode(data: ByteString): ByteString } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/encoding/NanoboardCrypto.scala version [1530f93813].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
package com.karasiq.nanoboard.encoding import akka.util.ByteString import org.bouncycastle.crypto.digests.{SHA256Digest, SHA512Digest} import org.bouncycastle.crypto.{Digest, StreamCipher} import org.bouncycastle.jce.provider.BouncyCastleProvider /** * Internal cryptography utility */ private[nanoboard] object NanoboardCrypto { val provider = new BouncyCastleProvider @inline def sha256 = new SHA256Digest() @inline def sha512 = new SHA512Digest() implicit class BCDigestOps[T <: Digest](md: T) { def digest(data: ByteString): ByteString = { md.update(data.toArray, 0, data.length) val hash = Array.ofDim[Byte](md.getDigestSize) md.doFinal(hash, 0) ByteString(hash) } def updated(data: ByteString): T = { md.update(data.toArray, 0, data.length) md } } implicit class BCStreamCipherOps[T <: StreamCipher](cipher: T) { def process(data: ByteString): ByteString = { val buffer = Array.ofDim[Byte](data.length) val length = cipher.processBytes(data.toArray, 0, data.length, buffer, 0) ByteString(buffer).take(length) } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/encoding/NanoboardEncoding.scala version [3121a18770].
> > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package com.karasiq.nanoboard.encoding import com.karasiq.nanoboard.encoding.DataEncodingStage._ import com.karasiq.nanoboard.encoding.stages.{GzipCompression, PngEncoding, SalsaCipher} import com.typesafe.config.{Config, ConfigFactory} object NanoboardEncoding { /** * Creates default nanoboard data encoder with specified config * @param config Configuration object * @param pngEncoding PNG encoder * @return Default nanoboard data encoder * @see [[https://github.com/nanoboard/nanoboard/wiki/%D0%9D%D0%B0%D0%BD%D0%BE%D0%B1%D0%BE%D1%80%D0%B4%D0%B0 Original specification (Russian)]] */ def fromConfig(config: Config, pngEncoding: PngEncoding = PngEncoding.default): DataEncodingStage = { Seq(GzipCompression(), SalsaCipher.fromConfig(config), pngEncoding) } /** * Same as [[com.karasiq.nanoboard.encoding.NanoboardEncoding#fromConfig(com.typesafe.config.Config, com.karasiq.nanoboard.encoding.stages.PngEncoding) fromConfig]], * but uses root configuration. */ def apply(config: Config = ConfigFactory.load(), pngEncoding: PngEncoding = PngEncoding.default): DataEncodingStage = { fromConfig(config.getConfig("nanoboard"), pngEncoding) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/encoding/formats/CBORMessagePackFormat.scala version [b43c56c078].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
package com.karasiq.nanoboard.encoding.formats import akka.util.ByteString import com.karasiq.nanoboard.NanoboardMessage import com.upokecenter.cbor.CBORObject import scala.collection.JavaConversions._ /** * CBOR message pack format * @see [[http://cbor.io Concise Binary Object Representation]] */ trait CBORMessagePackFormat extends MessagePackFormat { /** * Parses messages from serialized data * @param payload Serialized messages * @return Parsed messages */ override def parseMessages(payload: ByteString): Vector[NanoboardMessage] = { val messages = CBORObject.DecodeFromBytes(payload.toArray).get("messages") messages.getValues.toVector.map { message ⇒ NanoboardMessage(message.get("parent").AsString(), message.get("text").AsString(), ByteString(message.get("pow").GetByteString()), ByteString(message.get("sign").GetByteString())) } } /** * Serializes messages to byte string * @param messages Messages * @return Serialized messages */ override def writeMessages(messages: Seq[NanoboardMessage]): ByteString = { val messagesArray = messages.foldLeft(CBORObject.NewArray()) { case (array, NanoboardMessage(parent, text, pow, signature)) ⇒ array.Add( CBORObject.NewMap() .Add("parent", parent) .Add("text", text) .Add("pow", pow.toArray[Byte]) .Add("sign", signature.toArray[Byte]) ) } val payload = CBORObject.NewMap().Add("messages", messagesArray) ByteString(payload.EncodeToBytes()) } } object CBORMessagePackFormat extends CBORMessagePackFormat |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/encoding/formats/MessagePackFormat.scala version [0bb6d85aff].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
package com.karasiq.nanoboard.encoding.formats import akka.util.ByteString import com.typesafe.config.Config import com.karasiq.nanoboard.NanoboardMessage /** * Nanoboard message pack format */ trait MessagePackFormat { /** * Parses messages from serialized data * @param payload Serialized messages * @return Parsed messages */ def parseMessages(payload: ByteString): Vector[NanoboardMessage] /** * Serializes messages to byte string * @param messages Messages * @return Serialized messages */ def writeMessages(messages: Seq[NanoboardMessage]): ByteString } object MessagePackFormat { def apply(config: Config): MessagePackFormat = config.getString("nanoboard.message-pack-format").toLowerCase match { case "text" ⇒ TextMessagePackFormat case "cbor" ⇒ CBORMessagePackFormat case format ⇒ throw new IllegalArgumentException(s"Invalid format: $format") } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/encoding/formats/TextMessagePackFormat.scala version [bfe1772cc7].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
package com.karasiq.nanoboard.encoding.formats import scala.annotation.tailrec import akka.util.ByteString import com.karasiq.nanoboard.NanoboardMessage /** * Legacy nanoboard message pack format. * Output starts with six-digits hex numbers, the first of which is a count of messages, * and subsequent are lengths of the messages itself, and followed by concatenated messages in format `replyTo` + `text`. * @see [[https://github.com/nanoboard/nanoboard/blob/master/Database/NanoPostPackUtil.cs Original implementation]] */ trait TextMessagePackFormat extends MessagePackFormat { final def parseMessages(payloadBs: ByteString): Vector[NanoboardMessage] = { val payload = payloadBs.utf8String val sizes: Vector[Int] = { val sizes: Iterator[Int] = payload.grouped(6) .map(bs ⇒ Integer.parseInt(bs, 16)) val count = if (sizes.nonEmpty) sizes.next() else 0 sizes.take(count).toVector } @tailrec def parse(str: String, sizes: Vector[Int], messages: Vector[NanoboardMessage] = Vector.empty): Vector[NanoboardMessage] = { if (sizes.isEmpty) { messages } else { val (data, rest) = str.splitAt(sizes.head) val (hash, rawMessage) = data.splitAt(NanoboardMessage.HashLength) val (message, pow, signature) = NanoboardMessage.stripSignatureTags(rawMessage) parse(rest, sizes.tail, messages :+ NanoboardMessage(hash, message, pow.getOrElse(NanoboardMessage.NoPOW), signature.getOrElse(NanoboardMessage.NoSignature))) } } val data = payload.drop((sizes.length + 1) * 6) assert(sizes.forall(_ > NanoboardMessage.HashLength) && sizes.sum <= data.length, "Invalid message sizes") parse(data, sizes) } final def writeMessages(messages: Seq[NanoboardMessage]): ByteString = { val payloads = messages.map(m ⇒ m.parent + NanoboardMessage.textWithSignatureTags(m)) val sizes = Vector(payloads.length) ++ payloads.map(_.length) ByteString((sizes.map(size ⇒ f"$size%06x") ++ payloads).mkString) } } object TextMessagePackFormat extends TextMessagePackFormat |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/encoding/stages/GzipCompression.scala version [939a5e3b44].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
package com.karasiq.nanoboard.encoding.stages import java.io.{ByteArrayInputStream, ByteArrayOutputStream} import java.util.zip.{GZIPInputStream, GZIPOutputStream} import akka.util.ByteString import com.karasiq.nanoboard.encoding.DataEncodingStage /** * GZIP compression data stage. */ object GzipCompression extends GzipCompression { def apply(): GzipCompression = this } sealed class GzipCompression extends DataEncodingStage { /** * Compresses data with GZIP * @param data Source data * @return Compressed data */ override def encode(data: ByteString): ByteString = { val inputStream = new ByteArrayInputStream(data.toArray) val byteArrayOutputStream = new ByteArrayOutputStream() val outputStream = new GZIPOutputStream(byteArrayOutputStream, 1024, true) try { val buffer = new Array[Byte](1024) var len = 0 while (len != -1) { len = inputStream.read(buffer) if (len > 0) outputStream.write(buffer, 0, len) } outputStream.finish() outputStream.flush() ByteString(byteArrayOutputStream.toByteArray) } finally { inputStream.close() outputStream.close() } } /** * Decompresses GZIP-compressed data * @param data Previously compressed data * @return Source data */ override def decode(data: ByteString): ByteString = { val inputStream = new GZIPInputStream(new ByteArrayInputStream(data.toArray), 1024) val outputStream = new ByteArrayOutputStream() try { val buffer = new Array[Byte](1024) var len = 0 while (len != -1) { len = inputStream.read(buffer) if (len > 0) outputStream.write(buffer, 0, len) } ByteString(outputStream.toByteArray) } finally { inputStream.close() outputStream.close() } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/encoding/stages/PngEncoding.scala version [adb7b66685].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
package com.karasiq.nanoboard.encoding.stages import java.awt.image.BufferedImage import java.nio.ByteOrder import java.util import akka.util.ByteString import com.karasiq.nanoboard.encoding.DataEncodingStage import com.karasiq.nanoboard.utils._ object PngEncoding { /** * Creates PNG encoder * @param sourceImage Source image provider * @return PNG encoder */ def apply(sourceImage: ByteString ⇒ BufferedImage): PngEncoding = { new PngEncoding(sourceImage) } /** * Creates PNG encoder, that uses single image for encoding * @param imageData Encoded image, must be readable with [[javax.imageio.ImageIO#read(java.io.InputStream) Java ImageIO]] * @return PNG encoder */ def fromEncodedImage(imageData: ByteString): PngEncoding = { apply(_ ⇒ BufferedImages.fromBytes(imageData)) } /** * Creates PNG encoder, that always generates random image for encoding * @return PNG encoder */ def fromRandomImage(): PngEncoding = { apply { data ⇒ val size = math.ceil(math.sqrt(requiredImageBytes(data) / 3)).toInt BufferedImages.generateImage(size, size) } } /** * Default PNG encoder */ val default = fromRandomImage() /** * Image bytes, required to encode provided data * @param data Payload * @return Required image length */ def requiredImageBytes(data: ByteString): Int = { (4 + data.length) * 8 } /** * Available image size * @param image Source image * @return Actual image length */ def imageBytes(image: BufferedImage): Int = { image.getWidth * image.getHeight * 3 } } /** * PNG steganography encoding stage. Hides provided data in image color bits. * @param sourceImage Source image provider * @see [[http://blog.andersen.im/2014/11/hiding-your-bits-in-the-bytes/ Original algorithm]] */ final class PngEncoding(sourceImage: ByteString ⇒ BufferedImage) extends DataEncodingStage { private implicit val byteOrder = ByteOrder.LITTLE_ENDIAN @inline private def asInt(bytes: ByteString): Int = { bytes.padTo(4, 0.toByte).toByteBuffer.order(byteOrder).getInt } @inline private def asBytes(int: Int): ByteString = { ByteString.newBuilder .putInt(int) .result() } /** * Decodes hidden data from RGB array * @param bytes RGB array * @param dataLength Data length * @param index Data offset * @return Extracted data */ private def readBytes(bytes: Array[Int], dataLength: Int, index: Int): ByteString = { val bitCount = dataLength * 8 val offset = index * 8 val required: Int = offset + bitCount - 1 assert(bytes.length >= required, s"Invalid data length, $required bytes required") val result = new util.BitSet(bitCount) for (i ← 0 until bitCount) { result.set(i, bytes(offset + i) % 2 == 1) } ByteString(result.toByteArray).take(dataLength) } /** * Encodes provided data to RGB bytes * @param bytes RGB array * @param data Payload * @param byteIndex Data offset */ private def writeBytes(bytes: Array[Int], data: ByteString, byteIndex: Int): Unit = { val bitOffset = byteIndex * 8 val bitSet = util.BitSet.valueOf(data.toArray) val bitCount = data.length * 8 for (i ← 0 until bitCount) { val index: Int = bitOffset + i val evenByte = bytes(index) - (bytes(index) % 2) bytes(index) = evenByte + (if (bitSet.get(i)) 1 else 0) } } override def encode(data: ByteString): ByteString = { // Request source image for provided data val img = sourceImage(data) assert(img.ne(null), "Container image not found") // Decode RGB data val bytes: Array[Int] = img.toRgbArray val requiredSize: Int = PngEncoding.requiredImageBytes(data) assert(bytes.length >= requiredSize, s"Image is too small, $requiredSize bits required") // Write length writeBytes(bytes, asBytes(data.length), 0) // Write payload writeBytes(bytes, data, 4) // Convert bytes to RGB data val rgb = BufferedImages.toColorArray(bytes) img.setRGB(0, 0, img.getWidth, img.getHeight, rgb, 0, img.getWidth) // Render image as PNG img.toBytes("png") } override def decode(data: ByteString): ByteString = { // Decode image val img = BufferedImages.fromBytes(data) assert(img.ne(null), "Invalid image") // Decode RGB data val bytes: Array[Int] = img.toRgbArray // Read data length val length: Int = asInt(readBytes(bytes, 4, 0)) // Read payload readBytes(bytes, length, 4) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/encoding/stages/SalsaCipher.scala version [39c60bdf75].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
package com.karasiq.nanoboard.encoding.stages import akka.util.ByteString import com.karasiq.nanoboard.encoding.DataEncodingStage import com.karasiq.nanoboard.encoding.NanoboardCrypto.{BCDigestOps, BCStreamCipherOps, sha256} import com.typesafe.config.Config import org.bouncycastle.crypto.engines.Salsa20Engine import org.bouncycastle.crypto.params.{KeyParameter, ParametersWithIV} object SalsaCipher { /** * Creates Salsa20 cipher stage from specified config * @param nbConfig Configuration object * @return Salsa20 cipher stage */ def fromConfig(nbConfig: Config): SalsaCipher = { apply(nbConfig.getString("encryption-key")) } /** * Creates Salsa20 cipher stage with the specified key * @param key Encryption key * @return Salsa20 cipher stage */ def apply(key: String): SalsaCipher = { new SalsaCipher(key) } } /** * Salsa20 stream cipher encryption stage * @param key Encryption key */ final class SalsaCipher(key: String) extends DataEncodingStage { private def createCipher(encryption: Boolean): Salsa20Engine = { val cipher = new Salsa20Engine() val keyBytes = ByteString(key) val secretKey = new KeyParameter(sha256.digest(keyBytes).toArray, 0, 32) val iv: Array[Byte] = sha256.digest(keyBytes.reverse).toArray cipher.init(encryption, new ParametersWithIV(secretKey, iv, 0, 8)) cipher } /** * Encrypts provided data with the Salsa20 stream cipher * @param data Source data * @return Encrypted data */ override def encode(data: ByteString): ByteString = { createCipher(encryption = true).process(data) } /** * Decrypts data, encrypted with the Salsa20 stream cipher * @param data Encrypted data * @return Source data */ override def decode(data: ByteString): ByteString = { createCipher(encryption = false).process(data) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/sources/bitmessage/BitMessageTransport.scala version [7c2fa28a0f].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
package com.karasiq.nanoboard.sources.bitmessage import java.nio.charset.StandardCharsets import scala.concurrent.Future import akka.actor.ActorSystem import akka.http.scaladsl.Http import akka.http.scaladsl.model._ import akka.http.scaladsl.server.Directives._ import akka.stream.{ActorMaterializer, OverflowStrategy} import akka.stream.scaladsl.{Sink, Source} import com.typesafe.config.{Config, ConfigFactory} import org.apache.commons.codec.binary.Base64 import play.api.libs.json._ import com.karasiq.nanoboard.NanoboardMessage object BitMessageTransport { def fromConfig(bmConfig: Config)(implicit ac: ActorSystem, am: ActorMaterializer) = { val chanAddress = bmConfig.getString("chan-address") val apiAddress = bmConfig.getString("host") val apiPort = bmConfig.getInt("port") val apiUsername = bmConfig.getString("username") val apiPassword = bmConfig.getString("password") new BitMessageTransport(chanAddress, apiAddress, apiPort, apiUsername, apiPassword) } def apply(config: Config = ConfigFactory.load())(implicit ac: ActorSystem, am: ActorMaterializer) = { fromConfig(config.getConfig("nanoboard.bitmessage")) } def wrap(messages: NanoboardMessage*): String = { val wrappedMessages = messages.map(m ⇒ WrappedNanoboardMessage(m.hash, asBase64(NanoboardMessage.textWithSignatureTags(m)), m.parent)) Json.toJson(wrappedMessages).toString() } def unwrap(bitMessage: String): Vector[NanoboardMessage] = { val messages = Json.parse(bitMessage).as[Vector[WrappedNanoboardMessage]] messages.map { wrapped ⇒ val (text, pow, signature) = NanoboardMessage.stripSignatureTags(fromBase64(wrapped.message)) NanoboardMessage(wrapped.replyTo, text, pow.getOrElse(NanoboardMessage.NoPOW), signature.getOrElse(NanoboardMessage.NoSignature)) } } @inline private[bitmessage] def asBase64(string: String): String = { Base64.encodeBase64String(string.getBytes(StandardCharsets.UTF_8)) } @inline private[bitmessage] def fromBase64(string: String): String = { new String(Base64.decodeBase64(string), StandardCharsets.UTF_8) } } /** * Nanoboard BitMessage transport, compatible with official implementation. * @see [[https://github.com/nanoboard/nanoboard-bittransport]] */ final class BitMessageTransport(chanAddress: String, apiAddress: String, apiPort: Int, apiUsername: String, apiPassword: String)(implicit ac: ActorSystem, am: ActorMaterializer) { import XmlRpcProxy._ private val http = Http() private val xmlRpcProxy = new XmlRpcProxy(http, apiAddress, apiPort, apiUsername, apiPassword) def sendMessage(message: NanoboardMessage): Future[HttpResponse] = { xmlRpcProxy.sendMessage(chanAddress, chanAddress, (), BitMessageTransport.asBase64(BitMessageTransport.wrap(message)), 2, 21600) } def receiveMessages(host: String, port: Int, sink: Sink[NanoboardMessage, _]): Future[Http.ServerBinding] = { http.bindAndHandle(route(sink), host, port) } private def route(sink: Sink[NanoboardMessage, _]) = { val queue = Source .queue(20, OverflowStrategy.dropHead) .to(sink) .run() post { (path("api" / "add" / NanoboardMessage.HashFormat) & entity(as[String])) { (parent, message) ⇒ val (text, pow, signature) = NanoboardMessage.stripSignatureTags(BitMessageTransport.fromBase64(message)) queue.offer(NanoboardMessage(parent, text, pow.getOrElse(NanoboardMessage.NoPOW), signature.getOrElse(NanoboardMessage.NoSignature))) complete(StatusCodes.OK) } } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/sources/bitmessage/WrappedNanoboardMessage.scala version [967b210438].
> > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package com.karasiq.nanoboard.sources.bitmessage import play.api.libs.json.Json import com.karasiq.nanoboard.NanoboardMessage @SerialVersionUID(0L) private[bitmessage] final case class WrappedNanoboardMessage(hash: String, message: String, replyTo: String) { assert(hash.length == NanoboardMessage.HashLength && replyTo.length == NanoboardMessage.HashLength, "Invalid hashes") } private[bitmessage] object WrappedNanoboardMessage { implicit val wrappedNanoboardMessageFormat = Json.format[WrappedNanoboardMessage] } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/sources/bitmessage/XmlRpcProxy.scala version [1fc8f7a139].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
package com.karasiq.nanoboard.sources.bitmessage import scala.concurrent.Future import scala.language.{dynamics, implicitConversions} import akka.http.scaladsl.HttpExt import akka.http.scaladsl.model._ import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials} import akka.stream.ActorMaterializer import scalatags.Text.all._ import scalatags.text.Builder import com.karasiq.nanoboard.sources.bitmessage.XmlRpcProxy.{XmlRpcParameter, XmlRpcTags} /** * Simple XML-RPC wrapper, based on `akka-http` */ private[bitmessage] final class XmlRpcProxy(http: HttpExt, apiAddress: String, apiPort: Int, apiUsername: String, apiPassword: String)(implicit am: ActorMaterializer) extends Dynamic { def applyDynamic(method: String)(args: XmlRpcParameter*): Future[HttpResponse] = { import XmlRpcTags._ val entity = "<?xml version=\"1.0\"?>" + methodCall( methodName(method), params( for (arg ← args) yield param(value(arg)) ) ) val authentication = Authorization(BasicHttpCredentials(apiUsername, apiPassword)) val url = s"http://$apiAddress:$apiPort/" http.singleRequest(HttpRequest(method = HttpMethods.POST, uri = url, entity = HttpEntity(ContentTypes.`text/xml(UTF-8)`, entity), headers = List(authentication))) } } private[bitmessage] object XmlRpcProxy { object XmlRpcTags { val methodCall = tag("methodCall") val methodName = tag("methodName") val params = tag("params") val param = tag("param") val value = tag("value") val int = tag("int") } sealed trait XmlDataWrapper[T] { def toModifier(value: T): Modifier } implicit object StringXmlDataWrapper extends XmlDataWrapper[String] { def toModifier(value: String) = value } implicit object IntXmlDataWrapper extends XmlDataWrapper[Int] { def toModifier(value: Int) = XmlRpcTags.int(value) } implicit object UnitXmlDataWrapper extends XmlDataWrapper[Unit] { def toModifier(value: Unit) = () } sealed trait XmlRpcParameter extends Modifier implicit def anyToXmlRpcParameter[T: XmlDataWrapper](value: T): XmlRpcParameter = new XmlRpcParameter { def applyTo(t: Builder) = implicitly[XmlDataWrapper[T]].toModifier(value).applyTo(t) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/sources/png/BoardPngSource.scala version [919194d3eb].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
package com.karasiq.nanoboard.sources.png import java.net.{InetSocketAddress, URL} import scala.collection.JavaConversions._ import scala.util.Try import akka.actor.ActorSystem import akka.http.scaladsl.{ClientTransport, Http} import akka.http.scaladsl.model.HttpRequest import akka.http.scaladsl.settings.ConnectionPoolSettings import akka.stream.ActorMaterializer import akka.stream.scaladsl.Source import akka.util.ByteString import com.typesafe.config.Config import org.jsoup.Jsoup import org.jsoup.nodes.{Document, Element} import com.karasiq.nanoboard.NanoboardMessage import com.karasiq.nanoboard.encoding.DataEncodingStage private object BoardPngSource { def createHttpSettings(config: Config) = { val settings = ConnectionPoolSettings(config) val proxy = Try(config.getString("nanoboard.proxy")).toOption proxy match { case Some(proxy) if proxy.contains(":") ⇒ val Array(host, port) = proxy.split(":", 2) settings.withTransport(ClientTransport.httpsProxy(InetSocketAddress.createUnresolved(host, port.toInt))) case Some(proxy) if proxy.nonEmpty ⇒ settings.withTransport(ClientTransport.httpsProxy(InetSocketAddress.createUnresolved(proxy, 8080))) case _ ⇒ settings } } } /** * Generic imageboard PNG downloader * @param encoding PNG data decoder */ class BoardPngSource(encoding: DataEncodingStage)(implicit as: ActorSystem, am: ActorMaterializer) extends UrlPngSource { protected final val http = Http() protected val httpSettings = BoardPngSource.createHttpSettings(as.settings.config) def messagesFromImage(url: String): Source[NanoboardMessage, akka.NotUsed] = { Source.fromFuture(http.singleRequest(HttpRequest(uri = url), settings = httpSettings)) .flatMapConcat(_.entity.dataBytes.fold(ByteString.empty)(_ ++ _)) .mapConcat { data ⇒ // println(encoding.decode(data).utf8String) NanoboardMessage.parseMessages(encoding.decode(data)) } .recoverWithRetries(1, { case _ ⇒ Source.empty }) .named("boardImageMessages") } def imagesFromPage(url: String): Source[String, akka.NotUsed] = { Source.fromFuture(http.singleRequest(HttpRequest(uri = url), settings = httpSettings)) .flatMapConcat(_.entity.dataBytes.fold(ByteString.empty)(_ ++ _)) .flatMapConcat(data ⇒ imagesFromPage(Jsoup.parse(data.utf8String, url))) .recoverWithRetries(1, { case _ ⇒ Source.empty }) .named("boardImages") } protected def imagesFromPage(page: Document): Source[String, akka.NotUsed] = { val urls = page.select("a").flatMap(getUrl(_, "href")) Source(urls.distinct.toVector) } protected def getUrl(e: Element, attr: String): Option[String] = { Try(new URL(e.absUrl(attr))) .toOption .filter(_.getPath.toLowerCase.endsWith(".png")) // .filter(_.getPath.matches("([^\\?\\s]+)?/src/([^\\?\\s]+)?\\.png")) .map(_.toString) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/sources/png/UrlPngSource.scala version [85eeb16a75].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
package com.karasiq.nanoboard.sources.png import akka.actor.ActorSystem import akka.stream.ActorMaterializer import akka.stream.scaladsl.Source import com.typesafe.config.Config import com.karasiq.nanoboard.NanoboardMessage import com.karasiq.nanoboard.encoding.{DataEncodingStage, NanoboardEncoding} /** * PNG downloader interface */ trait UrlPngSource { /** * Downloads and parses the messages from provided URL * @param url PNG image URL * @return Nanoboard messages */ def messagesFromImage(url: String): Source[NanoboardMessage, akka.NotUsed] /** * Downloads and provides list of available PNG images from the page * @param url Page URL * @return PNG images URL */ def imagesFromPage(url: String): Source[String, akka.NotUsed] } object UrlPngSource { def fromConfig(config: Config)(implicit as: ActorSystem, am: ActorMaterializer): UrlPngSource = { apply(NanoboardEncoding.fromConfig(config)) } def apply(config: Config)(implicit as: ActorSystem, am: ActorMaterializer): UrlPngSource = { apply(NanoboardEncoding(config)) } def apply(encoding: DataEncodingStage)(implicit as: ActorSystem, am: ActorMaterializer): UrlPngSource = { new BoardPngSource(encoding) } def apply()(implicit as: ActorSystem, am: ActorMaterializer): UrlPngSource = { apply(as.settings.config) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/main/scala/com/karasiq/nanoboard/utils/package.scala version [8b2cb024c7].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
package com.karasiq.nanoboard import java.awt.Color import java.awt.image.BufferedImage import java.io.{ByteArrayInputStream, ByteArrayOutputStream} import javax.imageio.ImageIO import akka.util.ByteString import org.apache.commons.codec.binary.Hex import org.apache.commons.io.IOUtils import scala.util.Random package object utils { private[nanoboard] implicit class ByteStringCompanionOps(private val bs: ByteString.type) extends AnyVal { /** * Creates byte string from hex string (each byte represented as two chars from 00 to FF) * @param string Hex string * @return Byte string * @see [[org.apache.commons.codec.binary.Hex#decodeHex(char[])]] */ def fromHexString(string: String): ByteString = { bs.apply(Hex.decodeHex(string.toCharArray)) } } private[nanoboard] implicit class ByteStringOps(private val bs: ByteString) extends AnyVal { /** * Encodes byte string as hex string (each byte represented as two chars from 00 to FF) * @param lowerCase Use lower-case hex chars * @return Hex string * @see [[org.apache.commons.codec.binary.Hex#encodeHex(byte[], boolean)]] */ def toHexString(lowerCase: Boolean = true): String = { String.valueOf(Hex.encodeHex(bs.toArray, lowerCase)) } } private[nanoboard] object BufferedImages { /** * Convers color array to RGB array * Input format: {{{Array(rgb)}}} * Output format: {{{Array(red, green, blue)}}} * @param colors Color array * @return RGB array */ def toRgbArray(colors: Array[Int]): Array[Int] = { val bytes = new Array[Int](colors.length * 3) for (i ← colors.indices) { val color = new Color(colors(i)) bytes(i * 3) = color.getRed bytes(i * 3 + 1) = color.getGreen bytes(i * 3 + 2) = color.getBlue } bytes } /** * Converts RGB array to color array, which can be used in [[java.awt.image.BufferedImage#setRGB(int, int, int, int, int[], int, int) setRGB function]]. * Input format: {{{Array(red, green, blue)}}} * Output format: {{{Array(rgb)}}} * @param arr RGB array * @return Color array */ def toColorArray(arr: Array[Int]): Array[Int] = { val result = new Array[Int](arr.length / 3) for (i ← result.indices) { result(i) = new Color(arr(i * 3), arr(i * 3 + 1), arr(i * 3 + 2)).getRGB } result } /** * Generates random image * @param width Image width * @param height Image height * @return Generated image */ def generateImage(width: Int, height: Int): BufferedImage = { val image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) val rgb = Array.fill[Int](width * height)(Random.nextInt()) image.setRGB(0, 0, width, height, rgb, 0, width) image } /** * Reads image from bytes * @param data Encoded image * @return Decoded image * @see [[javax.imageio.ImageIO#read(java.io.InputStream)]] */ def fromBytes(data: ByteString): BufferedImage = { val inputStream = new ByteArrayInputStream(data.toArray) try { ImageIO.read(inputStream) } finally { IOUtils.closeQuietly(inputStream) } } } private[nanoboard] implicit class BufferedImageOps(private val img: BufferedImage) extends AnyVal { /** * Encodes image to color array * Output format: {{{Array(rgb)}}} * @return Color array */ def toColorArray: Array[Int] = { val colors = new Array[Int](img.getWidth * img.getHeight) img.getRGB(0, 0, img.getWidth, img.getHeight, colors, 0, img.getWidth) colors } /** * Decodes image to RGB array. * Output format: {{{Array(red, green, blue)}}} * @return RGB array */ def toRgbArray: Array[Int] = { BufferedImages.toRgbArray(toColorArray) } /** * Encodes image to bytes * @param format Image encoding format * @return Encoded image * @see [[javax.imageio.ImageIO#write(java.awt.image.RenderedImage, java.lang.String, java.io.OutputStream)]] */ def toBytes(format: String): ByteString = { val outputStream = new ByteArrayOutputStream() try { ImageIO.write(img, format, outputStream) ByteString(outputStream.toByteArray) } finally { IOUtils.closeQuietly(outputStream) } } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/test/resources/reference.conf version [7db183f0c0].
> > > |
1 2 3 |
nanoboard { encryption-key = "nano3" } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/test/resources/test-captcha.nbc version [13ebde12a4].
cannot compute difference between binary files
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/test/resources/test-encoded.png version [6b896136f5].
cannot compute difference between binary files
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/test/scala/com/karasiq/nanoboard/test/BitMessageTest.scala version [cd2bf02a62].
> > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package com.karasiq.nanoboard.test import org.scalatest.{FlatSpec, Matchers} import com.karasiq.nanoboard.NanoboardMessage import com.karasiq.nanoboard.sources.bitmessage.BitMessageTransport class BitMessageTest extends FlatSpec with Matchers { "BitMessage transport" should "encode message" in { val message = NanoboardMessage("e062de33e103343281ececdd645f8632", "Test") val wrapped = BitMessageTransport.wrap(message) wrapped shouldBe """[{"hash":"95a8c5c2ddec08a2eac00a24281bf5a2","message":"VGVzdA==","replyTo":"e062de33e103343281ececdd645f8632"}]""" BitMessageTransport.unwrap(wrapped) shouldBe Vector(message) } it should "decode message" in { BitMessageTransport.unwrap("""[{"hash":"90b90b6b88eeaf0660f260bd6bedcdcf","message":"W2ddVHVlLCA4L01hci8yMDE2LCAwMjozNjo1My45OTkgKEV1cm9wZS9Nb3Njb3cpLCBjbGllbnQ6IGthcmFzaXEtbmFub2JvYXJkIHYxLjAuM1svZ10K0KLQtdGB0YIg0LLQvdC10YjQvdC10Lkg0LrQsNGA0YLQuNC90LrQuApbc2ltZz1odHRwOi8vZnM1LmRpcmVjdHVwbG9hZC5uZXQvaW1hZ2VzLzE2MDMwOC95aWhwOTNzbC5wbmdd","replyTo":"f1fbb838193bb358c9d00fdfcfa028fe"}]""") shouldBe Vector(NanoboardMessage("f1fbb838193bb358c9d00fdfcfa028fe", "[g]Tue, 8/Mar/2016, 02:36:53.999 (Europe/Moscow), client: karasiq-nanoboard v1.0.3[/g]\nТест внешней картинки\n[simg=http://fs5.directupload.net/images/160308/yihp93sl.png]")) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/test/scala/com/karasiq/nanoboard/test/CaptchaTest.scala version [f79c3f78ce].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
package com.karasiq.nanoboard.test import java.nio.file.{Files, Paths} import scala.concurrent.Await import scala.concurrent.duration._ import akka.util.ByteString import org.scalatest.{FlatSpec, Matchers} import com.karasiq.nanoboard.NanoboardMessage import com.karasiq.nanoboard.captcha.{NanoboardCaptcha, NanoboardPow} import com.karasiq.nanoboard.captcha.impl.NanoboardPowV2 import com.karasiq.nanoboard.captcha.storage.NanoboardCaptchaSource import com.karasiq.nanoboard.encoding.NanoboardCrypto._ import com.karasiq.nanoboard.test.utils.TestFiles import com.karasiq.nanoboard.utils._ class CaptchaTest extends FlatSpec with Matchers { implicit val executionContext = NanoboardPow.executionContext() val post = NanoboardMessage("0" * 32, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.") val preCalculatedPow = ByteString.fromHexString("40ecb63c4168ceed2d018d0f756b8f07e66d4a31054db005766b56ad3f745c06e56fcf7f3030deb1d264cdba0af0b332ef11f83c951ed42a166f8d24efdcd010196b3acdeed8e6527b4fb45bcd15ef09ad16541322423d609019d76c2534680b3f3ab918caedfb45727548c3462ca29efe58a5c7f192bcb43995c071a907099e") val powCalculator = new NanoboardPowV2(3, 3, 1) "Captcha file" should "be parsed" in { val captchaFileName = TestFiles.unpackResource("test-captcha.nbc") val captchaFile = NanoboardCaptchaSource.fromFile(captchaFileName) try { val signedPost = post.copy(pow = preCalculatedPow) val signPayload = powCalculator.getSignPayload(signedPost) val captcha = Await.result(captchaFile(powCalculator.getCaptchaIndex(signedPost, captchaFile.length)), Duration.Inf) val captchaPng = NanoboardCaptcha.render(captcha, 50, 20) // CaptchaTest.saveToFile(captchaPng, "test-captcha.png") val validSignature = captcha.signature(signPayload, "sovyo") captcha.verify(signPayload, validSignature) shouldBe true val invalidSignature = captcha.signature(signPayload, "11111") captcha.verify(signPayload, invalidSignature) shouldBe false validSignature.toHexString() shouldBe "fcba6fa8b4d853a676dfa2033868276785ca2ed160333525fe1f506d4e1c60df5e52ad0f1d4c949e4bea14fddfc0c347ca4b16bbe2a87e55a57681d4fe04a303" sha256.digest(captchaPng).toHexString() shouldBe "7573466048bb005b5f04f3a198907b62d748c434230bd537afca1d39e135b5cc" sha256.digest(captcha.image).toHexString() shouldBe "55b95e7a9af5dcf2f48f0f417c59a72748fa8db7b141bdd78580443d9a64c413" captcha.publicKey.toHexString() shouldBe "7aed3dc8d009c749970356b64ddb445068fec933293a5cf56ff65a36d97e8079" captcha.seed.toHexString() shouldBe "fad5a58c217e0c6985e3241dd275bf667c47d3773b904e24cf0a39707741f815" Await.result(NanoboardCaptcha.verify(post.copy(pow = preCalculatedPow, signature = validSignature), powCalculator, captchaFile)(scala.concurrent.ExecutionContext.global), Duration.Inf) shouldBe true } finally { captchaFile.close() Files.delete(Paths.get(captchaFileName)) } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/test/scala/com/karasiq/nanoboard/test/FileEncodingTest.scala version [52f49a54ce].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
package com.karasiq.nanoboard.test import scala.util.Random import akka.util.ByteString import org.scalatest.{FlatSpec, Matchers} import com.karasiq.nanoboard.NanoboardMessage import com.karasiq.nanoboard.encoding.DataEncodingStage import com.karasiq.nanoboard.encoding.DataEncodingStage._ import com.karasiq.nanoboard.encoding.formats.{CBORMessagePackFormat, TextMessagePackFormat} import com.karasiq.nanoboard.encoding.stages.{GzipCompression, PngEncoding, SalsaCipher} import com.karasiq.nanoboard.test.utils.TestFiles //noinspection ScalaDeprecation class FileEncodingTest extends FlatSpec with Matchers { val testImage = TestFiles.resource("test-encoded.png") val testMessages = Vector(NanoboardMessage("0" * 32, "Test message 1"), NanoboardMessage("1" * 32, "Test message 2")) val pngEncoding = PngEncoding.fromRandomImage() val gzipCompression = GzipCompression() val salsaCipher = SalsaCipher("nano") val testData = { val array = new Array[Byte](50000) Random.nextBytes(array) ByteString(array) } "PNG encoder" should "encode data to png image" in { val encoded = pngEncoding.encode(testData) val decoded = pngEncoding.decode(encoded) decoded shouldBe testData } it should "compress and encrypt data" in { val stages = Seq[DataEncodingStage]( gzipCompression, salsaCipher, Seq(gzipCompression, salsaCipher) ) stages.foreach { stage ⇒ println(s"Testing stage: $stage") val encoded = stage.encode(testData) val decoded = stage.decode(encoded) decoded shouldBe testData } } it should "decode ciphered data" in { val stage = Seq(gzipCompression, salsaCipher, pngEncoding) val decoded = stage.decode(testImage) decoded.length shouldBe 279352 val encoded = stage.encode(decoded) assert(stage.decode(encoded) == decoded && decoded.utf8String.startsWith("0000270000aa000083006cc0000259003f3a0050ea0062150091cd000083003f8b0000800001170005ca00014500136000ea370000360000d8003b3c0034fd0000880000350032d500008c0000cb00007f002e4600005e00008e0000b70000d00000780000a100005e00008b00003700007100005e00226c9953dabbec38c625670087e8be5eca66[g]01/Mar/2016, 01:15:26 (UTC), client: nboard v1.7.13[/g]")) NanoboardMessage.parseMessages(decoded) // .foreach(println) } "Message pack" should "be encoded in text format" in { val result = TextMessagePackFormat.writeMessages(testMessages) TextMessagePackFormat.parseMessages(result) shouldBe testMessages result.hashCode() shouldBe 2029017934 // println(result.utf8String) // TestFiles.saveToFile(result, "text-test.txt") } it should "be encoded in CBOR format" in { val result = CBORMessagePackFormat.writeMessages(testMessages) CBORMessagePackFormat.parseMessages(result) shouldBe testMessages result.hashCode() shouldBe -1960199224 // TestFiles.saveToFile(result, "cbor-test.bin") } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/test/scala/com/karasiq/nanoboard/test/NanoboardPowV1Test.scala version [002052a513].
cannot compute difference between binary files
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/test/scala/com/karasiq/nanoboard/test/NanoboardPowV2Test.scala version [ae1ba27044].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package com.karasiq.nanoboard.test import scala.concurrent.Await import scala.concurrent.duration.Duration import akka.util.ByteString import org.scalatest.{FlatSpec, Matchers} import com.karasiq.nanoboard.NanoboardMessage import com.karasiq.nanoboard.captcha.NanoboardPow import com.karasiq.nanoboard.captcha.impl.NanoboardPowV2 import com.karasiq.nanoboard.utils._ class NanoboardPowV2Test extends FlatSpec with Matchers { val post = NanoboardMessage("0" * 32, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.") val preCalculatedPow = ByteString.fromHexString("40ecb63c4168ceed2d018d0f756b8f07e66d4a31054db005766b56ad3f745c06e56fcf7f3030deb1d264cdba0af0b332ef11f83c951ed42a166f8d24efdcd010196b3acdeed8e6527b4fb45bcd15ef09ad16541322423d609019d76c2534680b3f3ab918caedfb45727548c3462ca29efe58a5c7f192bcb43995c071a907099e") val powCalculator = new NanoboardPowV2(3, 3, 1)(NanoboardPow.executionContext()) "POW calculator" should "verify hash" in { val signedPost = post.copy(pow = preCalculatedPow) powCalculator.verify(post) shouldBe false powCalculator.verify(signedPost) shouldBe true powCalculator.getCaptchaIndex(signedPost, 18000) shouldBe 14104 } it should "calculate valid hash" in { val powResult = Await.result(powCalculator.calculate(post), Duration.Inf) powCalculator.verify(post.copy(pow = powResult)) shouldBe true } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/test/scala/com/karasiq/nanoboard/test/SourceTest.scala version [ec9b2f2c99].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
package com.karasiq.nanoboard.test import scala.concurrent.Await import scala.concurrent.duration._ import akka.actor.ActorSystem import akka.stream.ActorMaterializer import akka.stream.scaladsl.Sink import org.scalatest.{BeforeAndAfterAll, FlatSpec, Matchers} import org.scalatest.tags.Network import com.karasiq.nanoboard.sources.png.UrlPngSource @Network class SourceTest extends FlatSpec with Matchers with BeforeAndAfterAll { implicit val actorSystem = ActorSystem("source-test") implicit val actorMaterializer = ActorMaterializer() val source = UrlPngSource() "Source loader" should "load messages from image" in { val imageSource = source.messagesFromImage("https://endchan.xyz/.media/78db64519e62d9edce7b6a8dbb47fd17-imagepng.png") val messages = Await.result(imageSource.runWith(Sink.seq), Duration.Inf) messages.length shouldBe 29 } it should "load messages from thread" in { val imageSource = source.imagesFromPage("https://endchan.xyz/test/res/971.html").flatMapConcat(source.messagesFromImage) val messages = Await.result(imageSource.take(100).runWith(Sink.seq), Duration.Inf) messages.length shouldBe 100 } override protected def afterAll(): Unit = { actorSystem.terminate() super.afterAll() } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/library/src/test/scala/com/karasiq/nanoboard/test/utils/TestFiles.scala version [a8e2c1fdf8].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
package com.karasiq.nanoboard.test.utils import java.io.{ByteArrayInputStream, FileOutputStream} import java.nio.file.Files import akka.util.ByteString import org.apache.commons.io.IOUtils object TestFiles { def resource(name: String): ByteString = { val input = getClass.getClassLoader.getResourceAsStream("test-encoded.png") try { ByteString(IOUtils.toByteArray(input)) } finally { IOUtils.closeQuietly(input) } } def unpackResource(name: String): String = { val fileName = Files.createTempFile("captcha", ".nbc").toString val input = getClass.getClassLoader.getResourceAsStream(name) val output = new FileOutputStream(fileName) try { IOUtils.copyLarge(input, output) fileName } finally { IOUtils.closeQuietly(input) IOUtils.closeQuietly(output) } } def saveToFile(data: ByteString, fileName: String): Unit = { val input = new ByteArrayInputStream(data.toArray) val output = new FileOutputStream(fileName) try { IOUtils.copyLarge(input, output) } finally { IOUtils.closeQuietly(input) IOUtils.closeQuietly(output) } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/project/NanoboardAssets.scala version [600e9124a8].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
import scalatags.Text.all._ object NanoboardAssets { def index = { "<!DOCTYPE html>" + html( head( base(href := "/"), meta(name := "viewport", content := "width=device-width, initial-scale=1.0"), link(rel := "shortcut icon", href := "favicon.ico", `type` := "image/x-icon") ), body( // Empty ) ) } def style = { """ |td.buttons { | text-align: center; |} | |.panel-primary .panel-head-buttons .glyphicon { | color: white; |} | |.glyphicon { | margin-left: 2px; | margin-right: 2px; |} | |.panel-title .glyphicon { | margin-right: 10px; |} """.stripMargin } def highlightJsLanguages = Vector( "bash", "clojure", "coffeescript", "cpp", "cs", "d", "delphi", "erlang", "fsharp", "go", "groovy", "haskell", "java", "javascript", "json", "lua", "lisp", "markdown", "objectivec", "perl", "php", "python", "ruby", "rust", "scala", "scheme", "sql", "swift", "typescript", "css", "xml" ) def highlightJsStyle = "monokai-sublime" } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/project/build.properties version [d94a10d4f3].
> |
1 |
sbt.version = 0.13.16
|
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/project/plugins.sbt version [e2cc5727de].
> > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
logLevel := Level.Warn addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.1") addSbtPlugin("com.github.karasiq" % "sbt-scalajs-bundler" % "1.0.7") addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.22") addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2") libraryDependencies ++= Seq( "com.lihaoyi" %% "scalatags" % "0.5.4", "org.webjars.bower" % "marked" % "0.3.5", "org.webjars" % "highlightjs" % "9.2.0" ) |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/setup/categories.txt version [bbc89d9a5f].
> > > > > > |
1 2 3 4 5 6 |
cd94a3d60f2f521806abebcd3dc3f549 Бред/Разное 355c600bef54376b89fd62ff4d636daf Обсуждение Наноборды 6aebf50d682b475265b24636fe2054e2 18+ |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/setup/launch4j.xml version [5dbdf2e995].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<?xml version="1.0" encoding="UTF-8"?> <launch4jConfig> <dontWrapJar>false</dontWrapJar> <headerType>console</headerType> <jar>..\target\universal\stage\lib\nanoboard-server.jar</jar> <outfile>..\target\universal\nanoboard.exe</outfile> <errTitle></errTitle> <cmdLine></cmdLine> <chdir>.</chdir> <priority>normal</priority> <downloadUrl>http://java.com/download</downloadUrl> <supportUrl></supportUrl> <stayAlive>false</stayAlive> <restartOnCrash>false</restartOnCrash> <manifest></manifest> <icon>..\frontend\files\favicon.ico</icon> <singleInstance> <mutexName>nanoboard-server</mutexName> <windowTitle></windowTitle> </singleInstance> <jre> <path>jre1.8.0_102</path> <bundledJre64Bit>true</bundledJre64Bit> <bundledJreAsFallback>true</bundledJreAsFallback> <minVersion>1.8.0</minVersion> <maxVersion></maxVersion> <jdkPreference>preferJre</jdkPreference> <runtimeBits>64/32</runtimeBits> <initialHeapSize>128</initialHeapSize> <maxHeapSize>512</maxHeapSize> </jre> </launch4jConfig> |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/setup/places.txt version [64aeac6687].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
http://dobrochan.com/mad/res/75979.xhtml http://volgach.ru/b/res/5226.html http://02ch.su/b/res/7379.html http://chaos.cyberpunk.us/st/50 http://endchan.xyz/test/res/971.html http://xynta.ch/b/res/10540.html http://www.nowere.net/wa/res/6271.html http://alphachan.org/art/res/329789.html http://hamstakilla.com/b/22279 https://2ch.hk/srv/res/1930.html https://2ch.hk/crypt/ http://cn.urbanculture.in/bb/res/16.html https://0chan.pl/_frct/res/1.html https://2ch.hk/obr/res/488.html https://2ch.hk/cute/res/5790.html https://2ch.hk/mlpr/res/92518.html https://volgach.ru/b/res/5226.html https://2-ch.su/b/res/553021.html http://syn-ch.ru/b/res/3916184.html http://gaika.ch/w/res/206.html https://0chan.ru.net/b/res/10023531.html https://02ch.su/b/res/7379.html https://2ch.hk/m/res/3730.html http://0chan.ru.net/b/res/10023531.html https://2ch.hk/who/res/1394.html https://2ch.hk/wp/res/56561.html https://2ch.hk/8/res/2570.html https://2ch.hk/test/res/6599.html https://hamstakilla.com/b/22279 https://www.nowere.net/wa/res/6271.html https://xynta.ch/b/res/10540.html https://endchan.xyz/test/res/971.html https://lolifox.org/b/res/2581.html https://www.0nyan.space/b/res/20743.html |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/setup/setup.iss version [bad97634a4].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#define OutputName "nanoboard-server" #define MyAppName "Nanoboard" #define MyAppVersion "1.3.2" #define MyAppPublisher "Karasiq, Inc." #define MyAppURL "http://www.github.com/Karasiq/nanoboard" #define MyAppExeName "nanoboard.exe" #define ProjectFolder "..\" [Setup] AppId={{4e1629c6-a53d-48ee-80b8-bd6644e62a1f}} AppName={#MyAppName} AppVersion={#MyAppVersion} ;AppVerName={#MyAppName} {#MyAppVersion} AppPublisher={#MyAppPublisher} AppPublisherURL={#MyAppURL} AppSupportURL={#MyAppURL} AppUpdatesURL={#MyAppURL} DefaultDirName={pf}\{#MyAppName} DefaultGroupName={#MyAppName} OutputDir={#ProjectFolder}\target\iss OutputBaseFilename={#OutputName}-{#MyAppVersion} SetupIconFile={#ProjectFolder}\frontend\files\favicon.ico Compression=lzma SolidCompression=true PrivilegesRequired=admin [Languages] Name: english; MessagesFile: compiler:Default.isl Name: russian; MessagesFile: compiler:Languages\Russian.isl [Tasks] Name: desktopicon; Description: {cm:CreateDesktopIcon}; GroupDescription: {cm:AdditionalIcons}; Languages: [Files] Source: {#ProjectFolder}\target\universal\nanoboard.exe; DestDir: {app}; Flags: ignoreversion Source: c:\Program Files\Java\jre1.8.0_102\*; DestDir: {app}\jre1.8.0_102; Flags: recursesubdirs ignoreversion Source: {#ProjectFolder}\setup\places.txt; DestDir: {app}; Flags: ignoreversion Source: {#ProjectFolder}\setup\categories.txt; DestDir: {app}; Flags: ignoreversion Source: {#ProjectFolder}\frontend\files\favicon.ico; DestDir: {app}; Flags: ignoreversion [Icons] Name: {group}\{#MyAppName}; Filename: {app}\{#MyAppExeName}; IconFilename: {app}\favicon.ico; WorkingDir: {app} Name: {commondesktop}\{#MyAppName}; Filename: {app}\{#MyAppExeName}; Tasks: desktopicon; IconFilename: {app}\favicon.ico; WorkingDir: {app} [Run] Filename: {app}\{#MyAppExeName}; Description: {cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}; Flags: shellexec postinstall skipifsilent; WorkingDir: {app} |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/shared/shared/src/main/scala/com/karasiq/nanoboard/api/NanoboardCaptchaRequest.scala version [a1a989c664].
> > > > > > > |
1 2 3 4 5 6 7 |
package com.karasiq.nanoboard.api case class NanoboardCaptchaImage(index: Int, image: Array[Byte]) case class NanoboardCaptchaRequest(postHash: String, pow: Array[Byte], captcha: NanoboardCaptchaImage) case class NanoboardCaptchaAnswer(request: NanoboardCaptchaRequest, answer: String) |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/shared/shared/src/main/scala/com/karasiq/nanoboard/api/NanoboardCategory.scala version [0c9cbc9ffe].
> > > |
1 2 3 |
package com.karasiq.nanoboard.api case class NanoboardCategory(hash: String, name: String) |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/shared/shared/src/main/scala/com/karasiq/nanoboard/api/NanoboardContainer.scala version [d721a7bd0a].
> > > |
1 2 3 |
package com.karasiq.nanoboard.api case class NanoboardContainer(id: Long, url: String, time: Long, posts: Int) |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/shared/shared/src/main/scala/com/karasiq/nanoboard/api/NanoboardMessageData.scala version [8e404c426f].
> > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.karasiq.nanoboard.api object NanoboardMessageData { val NO_POW = Array.fill[Byte](128)(0) val NO_SIGNATURE = Array.fill[Byte](64)(0) } case class NanoboardMessageData(containerId: Option[Long], parent: Option[String], hash: String, text: String, answers: Int, pow: Array[Byte] = NanoboardMessageData.NO_POW, signature: Array[Byte] = NanoboardMessageData.NO_SIGNATURE) { def isSigned: Boolean = { !signature.sameElements(NanoboardMessageData.NO_SIGNATURE) } def isCategory: Boolean = { parent.isEmpty } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/shared/shared/src/main/scala/com/karasiq/nanoboard/api/NanoboardReply.scala version [b728c94483].
> > > |
1 2 3 |
package com.karasiq.nanoboard.api case class NanoboardReply(parent: String, message: String) |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/shared/shared/src/main/scala/com/karasiq/nanoboard/streaming/NanoboardEvent.scala version [98445fdd53].
> > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package com.karasiq.nanoboard.streaming import boopickle.CompositePickler import boopickle.Default._ import com.karasiq.nanoboard.api.NanoboardMessageData sealed trait NanoboardEvent object NanoboardEvent { case class PostAdded(post: NanoboardMessageData) extends NanoboardEvent case class PostDeleted(hash: String) extends NanoboardEvent case class PostVerified(post: NanoboardMessageData) extends NanoboardEvent implicit val eventPickler: CompositePickler[NanoboardEvent] = compositePickler[NanoboardEvent] eventPickler .addConcreteType[PostAdded] .addConcreteType[PostDeleted] .addConcreteType[PostVerified] } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/shared/shared/src/main/scala/com/karasiq/nanoboard/streaming/NanoboardEventSeq.scala version [e73b1bcc90].
> > > > > > > > > |
1 2 3 4 5 6 7 8 9 |
package com.karasiq.nanoboard.streaming import boopickle.Default._ final case class NanoboardEventSeq(events: Seq[NanoboardEvent]) object NanoboardEventSeq { implicit val nanoboardEventSeqFormat = generatePickler[NanoboardEventSeq] } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/shared/shared/src/main/scala/com/karasiq/nanoboard/streaming/NanoboardSubscription.scala version [44af7c33ca].
> > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package com.karasiq.nanoboard.streaming import boopickle.Default._ sealed trait NanoboardSubscription object NanoboardSubscription { case object Unfiltered extends NanoboardSubscription case class PostHashes(hashes: Set[String]) extends NanoboardSubscription implicit val subscriptionPickler = compositePickler[NanoboardSubscription] subscriptionPickler.addConcreteType[PostHashes] subscriptionPickler.addConcreteType[Unfiltered.type] } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/resources/application.conf version [f57eab8893].
> > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
akka.http { server { server-header = nanoboard/${nanoboard.version} request-timeout = 3600s idle-timeout = 24h socket-options { tcp-no-delay = true } } client { parsing.max-content-length = 1g user-agent-header = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.52 Safari/537.36 OPR/31.0.1889.50 (Edition beta)" } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/resources/reference.conf version [977734d5e7].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
nanoboard { external-config-file = nanoboard.conf max-post-size = 200k pow-required = true database { url = "jdbc:h2:file:"${user.home}/.nanoboard/index_v12 driver = org.h2.Driver connectionPool = disabled keepAliveConnection = true } scheduler { update-interval = 15m posts-per-container = 100 spam-filter = [ // "(?i)^[a-z0-9+/=]+$" ] } server { host = 127.0.0.1 port = 7347 } bitmessage { receive = true send = true listen-host = 127.0.0.1 listen-port = 7346 host = 127.0.0.1 port = 8442 username = "nanoapi" password = "nano" } captcha { download-url = "https://github.com/Karasiq/nanoboard/releases/download/v1.2.0/ffeaeb19.nbc" storage = ${user.home}/.nanoboard } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/scala/com/karasiq/nanoboard/dispatcher/NanoboardDispatcher.scala version [32955f870f].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package com.karasiq.nanoboard.dispatcher import akka.util.ByteString import com.karasiq.nanoboard.api.{NanoboardCaptchaRequest, NanoboardContainer, NanoboardMessageData} import com.karasiq.nanoboard.{NanoboardCategory, NanoboardMessage} import scala.concurrent.Future trait NanoboardDispatcher { def createContainer(pending: Int, random: Int, format: String, container: ByteString): Future[ByteString] def recent(offset: Long, count: Long): Future[Seq[NanoboardMessageData]] def pending(offset: Long, count: Long): Future[Seq[NanoboardMessageData]] def places(): Future[Seq[String]] def categories(): Future[Seq[NanoboardMessageData]] def post(hash: String): Future[Option[NanoboardMessageData]] def thread(hash: String, offset: Long, count: Long): Future[Seq[NanoboardMessageData]] def addPost(source: String, message: NanoboardMessage): Future[Int] def reply(parent: String, text: String): Future[NanoboardMessageData] def markAsNotPending(message: String): Future[Unit] def markAsPending(message: String): Future[Unit] def delete(hash: String): Future[Seq[String]] def delete(offset: Long, count: Long): Future[Seq[String]] def clearDeleted(): Future[Int] def updatePlaces(places: Seq[String]): Future[Unit] def updateCategories(categories: Seq[NanoboardCategory]): Future[Unit] def containers(offset: Long, count: Long): Future[Seq[NanoboardContainer]] def clearContainer(id: Long): Future[Seq[String]] def requestVerification(hash: String): Future[NanoboardCaptchaRequest] def verifyPost(request: NanoboardCaptchaRequest, answer: String): Future[NanoboardMessageData] } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/scala/com/karasiq/nanoboard/dispatcher/NanoboardSlickDispatcher.scala version [c9c64709d1].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
package com.karasiq.nanoboard.dispatcher import scala.concurrent.{ExecutionContext, Future} import akka.actor.ActorSystem import akka.stream.{ActorMaterializer, OverflowStrategy} import akka.stream.scaladsl.{Sink, Source} import akka.util.ByteString import com.typesafe.config.{Config, ConfigFactory} import slick.driver.H2Driver.api._ import com.karasiq.nanoboard.{NanoboardCategory, NanoboardMessage, NanoboardMessageGenerator} import com.karasiq.nanoboard.api.{NanoboardCaptchaImage, NanoboardCaptchaRequest, NanoboardContainer, NanoboardMessageData} import com.karasiq.nanoboard.captcha.{NanoboardCaptcha, NanoboardPow} import com.karasiq.nanoboard.captcha.storage.NanoboardCaptchaSource import com.karasiq.nanoboard.encoding.NanoboardEncoding import com.karasiq.nanoboard.encoding.formats.MessagePackFormat import com.karasiq.nanoboard.encoding.stages.PngEncoding import com.karasiq.nanoboard.model.{categories ⇒ categoriesTable, _} import com.karasiq.nanoboard.model.MessageConversions._ import com.karasiq.nanoboard.streaming.NanoboardEvent object NanoboardSlickDispatcher { def apply(db: Database, captcha: NanoboardCaptchaSource, config: Config = ConfigFactory.load(), eventSink: Sink[NanoboardEvent, _] = Sink.ignore) (implicit ec: ExecutionContext, as: ActorSystem, am: ActorMaterializer): NanoboardDispatcher = { new NanoboardSlickDispatcher(db, captcha, config, eventSink) } } private[dispatcher] final class NanoboardSlickDispatcher(db: Database, captcha: NanoboardCaptchaSource, config: Config, eventSink: Sink[NanoboardEvent, _]) (implicit ec: ExecutionContext, as: ActorSystem, am: ActorMaterializer) extends NanoboardDispatcher { private[this] val powContext = NanoboardPow.executionContext() private[this] val powCalculator = NanoboardPow(config)(powContext) private[this] val messageGenerator = NanoboardMessageGenerator(config) private[this] val messageFormat = MessagePackFormat(config) private[this] val eventQueue = Source.queue(20, OverflowStrategy.dropHead) .to(eventSink) .run() override def createContainer(pendingCount: Int, randomCount: Int, format: String, container: ByteString) = { val pending = Post.pending(0, pendingCount) val rand = SimpleFunction.nullary[Double]("rand") val random = for (ps ← posts.sortBy(_ ⇒ rand).take(randomCount).result) yield ps.map(MessageConversions.wrapDbPost(_, 0)) val query = for (p ← pending; r ← random) yield (p ++ r).toVector val stage = NanoboardEncoding(config, if (container.nonEmpty) PngEncoding.fromEncodedImage(container) else PngEncoding.default) val future = db.run(query).map { posts ⇒ val data = messageFormat.writeMessages(posts.map(MessageConversions.unwrapToMessage)) val encoded = stage.encode(data) // assert(stage.decode(encoded) == data, "Container is broken") posts.map(_.hash) → encoded } future.flatMap { case (hashes, result) ⇒ db.run(for (_ ← pendingPosts.filter(_.hash inSet hashes).delete) yield result) } } override def recent(offset: Long, count: Long): Future[Seq[NanoboardMessageData]] = { db.run(Post.recent(offset, count)) } override def pending(offset: Long, count: Long): Future[Seq[NanoboardMessageData]] = { db.run(Post.pending(offset, count)) } override def places(): Future[Seq[String]] = { db.run(Place.list()) } override def categories(): Future[Seq[NanoboardMessageData]] = { db.run(Category.list()) } override def post(hash: String): Future[Option[NanoboardMessageData]] = { db.run(Post.get(hash)) } override def thread(hash: String, offset: Long, count: Long): Future[Seq[NanoboardMessageData]] = { db.run(Post.thread(hash, offset, count)) } override def markAsNotPending(message: String): Future[Unit] = { db.run(DBIO.seq(pendingPosts.filter(_.hash === message).delete)) } override def markAsPending(message: String): Future[Unit] = { db.run(DBIO.seq(pendingPosts.insertOrUpdate(message))) } override def delete(hash: String): Future[Seq[String]] = { val future = db.run(Post.deleteCascade(hash)) future.foreach { deleted ⇒ deleted.foreach(hash ⇒ eventQueue.offer(NanoboardEvent.PostDeleted(hash))) } future } override def delete(offset: Long, count: Long): Future[Seq[String]] = { val query = for { ps ← posts.sortBy(_.firstSeen.desc).drop(offset).take(count).result deleted ← DBIO.sequence(ps.map(p ⇒ Post.delete(p.hash))) } yield deleted val future = db.run(query) future.foreach { deleted ⇒ deleted.foreach(hash ⇒ eventQueue.offer(NanoboardEvent.PostDeleted(hash))) } future } override def clearDeleted(): Future[Int] = { db.run(deletedPosts.delete) } override def addPost(source: String, message: NanoboardMessage): Future[Int] = { val query = for { container ← Container.forUrl(source) inserted ← Post.insertMessage(container, message) } yield (container, inserted) val future = db.run(query) future.foreach { case (container, inserted) ⇒ if (inserted > 0) eventQueue.offer(NanoboardEvent.PostAdded(wrapMessage(message, Some(container)))) } future.map(_._2) } override def reply(parent: String, text: String): Future[NanoboardMessageData] = { val newMessage: NanoboardMessage = messageGenerator.newMessage(parent, text) val future = db.run(Post.addReply(newMessage)) future.foreach { msg ⇒ eventQueue.offer(NanoboardEvent.PostAdded(msg)) } future } override def updatePlaces(places: Seq[String]): Future[Unit] = { db.run(Place.update(places)) } override def updateCategories(categories: Seq[NanoboardCategory]): Future[Unit] = { db.run(Category.update(categories)) } override def containers(offset: Long, count: Long): Future[Seq[NanoboardContainer]] = { db.run(Container.list(offset, count)) } override def clearContainer(id: Long): Future[Seq[String]] = { db.run(Container.clearPosts(id)) } def requestVerification(hash: String): Future[NanoboardCaptchaRequest] = { val query = for { (parent, text) ← posts.filter(_.hash === hash).map(p ⇒ (p.parent, p.text)).result.head powMessage = NanoboardMessage(parent, text) pow ← DBIO.from(powCalculator.calculate(powMessage)) captchaId = powCalculator.getCaptchaIndex(powMessage.copy(pow = pow), captcha.length) captchaImage ← DBIO.from(captcha(captchaId)) } yield NanoboardCaptchaRequest(hash, pow.toArray, NanoboardCaptchaImage(captchaId, NanoboardCaptcha.render(captchaImage).toArray)) db.run(query) } def verifyPost(request: NanoboardCaptchaRequest, answer: String): Future[NanoboardMessageData] = { val query = for { DBPost(_, parent, text, firstSeen, containerId, _, _) ← posts.filter(_.hash === request.postHash).result.head pow ← DBIO.successful(ByteString(request.pow)) unsigned = NanoboardMessage(parent, text, pow) signPayload = powCalculator.getSignPayload(unsigned) captchaId = powCalculator.getCaptchaIndex(unsigned, captcha.length) captcha ← DBIO.from(this.captcha(captchaId)) signature = captcha.signature(signPayload, answer) if captcha.verify(signPayload, signature) newMessage = NanoboardMessage(parent, text, pow, signature) newPost ← DBIO.seq( posts.filter(_.hash === request.postHash).delete, posts += DBPost(newMessage.hash, newMessage.parent, newMessage.text, firstSeen, containerId, pow, signature), pendingPosts += newMessage.hash ) } yield MessageConversions.wrapMessage(newMessage, Some(containerId)) val future = db.run(query) future.foreach { message ⇒ eventQueue.offer(NanoboardEvent.PostVerified(message)) } future } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/scala/com/karasiq/nanoboard/model/ConfigQueries.scala version [b8bc744488].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
package com.karasiq.nanoboard.model import com.karasiq.nanoboard.NanoboardCategory import slick.driver.H2Driver.api._ import scala.concurrent.ExecutionContext trait ConfigQueries { self: Tables ⇒ object Place { def list() = { places.result } def update(newList: Seq[String])(implicit ec: ExecutionContext) = DBIO.sequence( newList.map(url ⇒ places.insertOrUpdate(url)) :+ places.filterNot(_.url inSet newList).delete ).map(_ ⇒ ()) } object Category { def list()(implicit ec: ExecutionContext) = { val query = categories.map { c ⇒ (c, posts.filter(_.parent === c.hash).length) } query.result.map(_.map { case (category, answers) ⇒ MessageConversions.wrapCategory(category, answers) }) } def update(newList: Seq[NanoboardCategory])(implicit ec: ExecutionContext) = DBIO.sequence( newList.map(c ⇒ add(c.hash, c.name)):+ categories.filterNot(_.hash inSet newList.map(_.hash)).delete ).map(_ ⇒ ()) def add(hash: String, name: String) = DBIO.seq( deletedPosts.filter(_.hash === hash).delete, categories.insertOrUpdate(NanoboardCategory(hash, name)) ) def delete(hash: String) = { categories.filter(_.hash === hash).delete } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/scala/com/karasiq/nanoboard/model/ContainerQueries.scala version [b204640e69].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
package com.karasiq.nanoboard.model import java.time.Instant import com.karasiq.nanoboard.api.NanoboardContainer import slick.driver.H2Driver.api._ import scala.concurrent.ExecutionContext trait ContainerQueries { self: Tables with PostQueries ⇒ object Container { def create(url: String)(implicit ec: ExecutionContext) = { containers.returning(containers.map(_.id)) += DBContainer(0, url, Instant.now().toEpochMilli) } def forUrl(url: String)(implicit ec: ExecutionContext) = { containers.filter(_.url === url).map(_.id).result.headOption.flatMap { case Some(id) ⇒ DBIO.successful(id) case None ⇒ create(url) } } private def listQuery(offset: ConstColumn[Long], count: ConstColumn[Long]) = { containers .map(c ⇒ (c, posts.filter(_.containerId === c.id).length)) .filter(_._2 > 0) .sortBy(_._1.time.desc) .drop(offset) .take(count) } private val listCompiled = Compiled(listQuery _) def list(offset: Long, count: Long)(implicit ec: ExecutionContext) = { listCompiled(offset, count) .result .map(_.map { case (DBContainer(id, url, time), posts) ⇒ NanoboardContainer(id, url, time, posts) }) } def clearPosts(id: Long)(implicit ec: ExecutionContext) = { for { hashes ← posts.filter(_.containerId === id).map(_.hash).result deleted ← DBIO.sequence(hashes.map(Post.delete)) } yield deleted } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/scala/com/karasiq/nanoboard/model/MessageConversions.scala version [f1e2336599].
> > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
package com.karasiq.nanoboard.model import akka.util.ByteString import com.karasiq.nanoboard.api.NanoboardMessageData import com.karasiq.nanoboard.{NanoboardCategory, NanoboardMessage} import scala.language.implicitConversions private[nanoboard] object MessageConversions { def wrapMessage(message: NanoboardMessage, container: Option[Long] = None, answers: Int = 0): NanoboardMessageData = { NanoboardMessageData(container, Some(message.parent), message.hash, message.text, answers, message.pow.toArray, message.signature.toArray) } def wrapDbPost[P <: Tables#DBPost](dbPost: P, answers: Int = 0): NanoboardMessageData = { NanoboardMessageData(Some(dbPost.containerId), Some(dbPost.parent), dbPost.hash, dbPost.text, answers, dbPost.pow.toArray, dbPost.signature.toArray) } def wrapCategory(category: NanoboardCategory, answers: Int = 0): NanoboardMessageData = { NanoboardMessageData(None, None, category.hash, category.name, answers) } def unwrapToMessage(message: NanoboardMessageData): NanoboardMessage = { NanoboardMessage(message.parent.getOrElse(throw new IllegalArgumentException("Invalid parent hash")), message.text, ByteString(message.pow), ByteString(message.signature)) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/scala/com/karasiq/nanoboard/model/PostQueries.scala version [62a14cf449].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
package com.karasiq.nanoboard.model import java.time.{Instant, LocalDate} import com.karasiq.nanoboard.NanoboardMessage import slick.driver.H2Driver.api._ import slick.lifted.{Compiled, ConstColumn} import scala.concurrent.ExecutionContext trait PostQueries { self: Tables with ConfigQueries with ContainerQueries ⇒ object Post { def addReply(m: NanoboardMessage)(implicit ec: ExecutionContext) = { for { container ← Container.forUrl(s"local://${LocalDate.now()}") _ ← DBIO.seq(insertMessage(container, m), /* pendingPosts.forceInsertQuery { val exists = (for (p <- pendingPosts if p.hash === m.hash) yield ()).exists for (message <- Query(m.hash) if !exists) yield message }, */ deletedPosts.filter(_.hash === m.hash).delete) } yield MessageConversions.wrapMessage(m, Some(container)) } def insertMessage(container: Long, m: NanoboardMessage) = posts.forceInsertQuery { val deleted = (for (p <- deletedPosts if p.hash inSet Seq(m.hash, m.parent)) yield ()).exists val exists = (for (p <- posts if p.hash === m.hash) yield ()).exists val insert = (m.hash, m.parent, m.text, Instant.now().toEpochMilli, container, m.pow, m.signature) <> (DBPost.tupled, DBPost.unapply) for (message <- Query(insert) if !deleted && !exists) yield message } def insertMessages(container: Long, messages: Seq[NanoboardMessage]) = { DBIO.sequence(messages.map(insertMessage(container, _))) } private def recentQuery(offset: ConstColumn[Long], count: ConstColumn[Long]) = { posts .sortBy(_.firstSeen.desc) .drop(offset) .take(count) .map(post ⇒ (post, posts.filter(_.parent === post.hash).length)) } private val recentCompiled = Compiled(recentQuery _) def recent(offset: Long, count: Long)(implicit ec: ExecutionContext) = { for (ps ← recentCompiled(offset, count).result) yield ps.map { case (post, answers) ⇒ MessageConversions.wrapDbPost(post, answers) } } private def pendingQuery(offset: ConstColumn[Long], count: ConstColumn[Long]) = { pendingPosts .flatMap(_.post) .sortBy(_.firstSeen.asc) .drop(offset) .take(count) } private val pendingCompiled = Compiled(pendingQuery _) def pending(offset: Long, count: Long)(implicit ec: ExecutionContext) = { for (ps ← pendingCompiled(offset, count).result) yield ps.map(MessageConversions.wrapDbPost(_, 0)) } private def getQuery(hash: Rep[String]) = { posts .filter(_.hash === hash) .map(post ⇒ (post, posts.filter(_.parent === post.hash).length)) } private val getCompiled = Compiled(getQuery _) def get(hash: String)(implicit ec: ExecutionContext) = { for (p ← getCompiled(hash).result.headOption) yield p.map { case (post, answers) ⇒ MessageConversions.wrapDbPost(post, answers) } } private def threadQuery(hash: Rep[String], offset: ConstColumn[Long], count: ConstColumn[Long]) = { val query = posts.filter(_.parent === hash) .sortBy(_.firstSeen.desc) .drop(offset) .take(count) def withAnswerCount(query: Query[Post, DBPost, Seq]) = query.map { post ⇒ (post, posts.filter(_.parent === post.hash).length) } withAnswerCount(query) } private val threadCompiled = Compiled(threadQuery _) def thread(hash: String, offset: Long, count: Long)(implicit ec: ExecutionContext) = { for { opPost ← get(hash) answers ← threadCompiled(hash, offset, count).result } yield opPost.toVector ++ answers.map { case (post, answers) ⇒ MessageConversions.wrapDbPost(post, answers) } } def delete(hash: String)(implicit ec: ExecutionContext) = { for { _ ← DBIO.seq( pendingPosts.filter(_.hash === hash).delete, Category.delete(hash), deletedPosts.insertOrUpdate(hash), posts.filter(_.hash === hash).delete ) } yield hash } def deleteCascade(hash: String)(implicit ec: ExecutionContext) = { def deleteCascadeRec(hash: String): DBIOAction[Seq[String], NoStream, Effect.Write with Effect.Read] = { val query = posts.filter(_.parent === hash) for { deleted ← query.map(_.hash).result descendants ← DBIO.sequence(deleted.map(deleteCascadeRec)) _ ← delete(hash) } yield Vector(hash) ++ deleted ++ descendants.flatten } deleteCascadeRec(hash) } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/scala/com/karasiq/nanoboard/model/Tables.scala version [1a22defbc2].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
package com.karasiq.nanoboard.model import scala.language.postfixOps import akka.util.ByteString import slick.driver.H2Driver.api._ import slick.lifted.TableQuery import com.karasiq.nanoboard.{NanoboardCategory, NanoboardMessage} trait Tables { implicit val byteStringColumnType = MappedColumnType.base[ByteString, Array[Byte]](_.toArray[Byte], ByteString.apply) case class DBPost(hash: String, parent: String, text: String, firstSeen: Long, containerId: Long, pow: ByteString, signature: ByteString) // TODO: Descending recent index: https://github.com/slick/slick/issues/1035 class Post(tag: Tag) extends Table[DBPost](tag, "posts") { def hash = column[String]("hash", O.SqlType(s"char(${NanoboardMessage.HashLength})"), O.PrimaryKey) def parent = column[String]("parent_hash", O.SqlType(s"char(${NanoboardMessage.HashLength})")) def text = column[String]("message") def firstSeen = column[Long]("first_seen") def containerId = column[Long]("container_id") def pow = column[ByteString]("pow_value", O.SqlType(s"binary(${NanoboardMessage.POWLength})")) def signature = column[ByteString]("signature", O.SqlType(s"binary(${NanoboardMessage.SignatureLength})")) def container = foreignKey("post_container", containerId, containers)(_.id, ForeignKeyAction.Restrict, ForeignKeyAction.Cascade) def threadIdx = index("thread_index", (parent, firstSeen), unique = false) def recentIdx = index("recent_index", firstSeen, unique = false) def containerIdx = index("container_index", containerId, unique = false) def * = (hash, parent, text, firstSeen, containerId, pow, signature) <> (DBPost.tupled, DBPost.unapply) } val posts = TableQuery[Post] class DeletedPost(tag: Tag) extends Table[String](tag, "posts_deleted") { def hash = column[String]("hash", O.SqlType(s"char(${NanoboardMessage.HashLength})"), O.PrimaryKey) def * = hash } val deletedPosts = TableQuery[DeletedPost] class PendingPost(tag: Tag) extends Table[String](tag, "pending_posts") { def hash = column[String]("hash", O.SqlType(s"char(${NanoboardMessage.HashLength})"), O.PrimaryKey) def post = foreignKey("post_fk", hash, posts)(_.hash, onUpdate = ForeignKeyAction.Restrict, onDelete = ForeignKeyAction.Cascade) def * = hash } val pendingPosts = TableQuery[PendingPost] class Place(tag: Tag) extends Table[String](tag, "places") { def url = column[String]("place_url", O.PrimaryKey) def * = url } val places = TableQuery[Place] class Category(tag: Tag) extends Table[NanoboardCategory](tag, "categories") { def hash = column[String]("category_hash", O.SqlType(s"char(${NanoboardMessage.HashLength})"), O.PrimaryKey) def name = column[String]("category_name") def * = (hash, name) <> (NanoboardCategory.tupled, NanoboardCategory.unapply) } val categories = TableQuery[Category] case class DBContainer(id: Long, url: String, time: Long) class Container(tag: Tag) extends Table[DBContainer](tag, "containers") { def id = column[Long]("container_id", O.PrimaryKey, O.AutoInc) def url = column[String]("container_url") def time = column[Long]("container_time") def idx = index("container_url_index", url, unique = true) def timeIdx = index("container_time_index", time) def * = (id, url, time) <> (DBContainer.tupled, DBContainer.unapply) } val containers = TableQuery[Container] } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/scala/com/karasiq/nanoboard/model/package.scala version [3591da5758].
> > > > > |
1 2 3 4 5 |
package com.karasiq.nanoboard import scala.language.postfixOps package object model extends Tables with ConfigQueries with PostQueries with ContainerQueries |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/scala/com/karasiq/nanoboard/server/BinaryMarshaller.scala version [7c73ac8e59].
> > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package com.karasiq.nanoboard.server import akka.http.scaladsl.marshalling._ import akka.http.scaladsl.model._ import akka.http.scaladsl.unmarshalling.Unmarshaller import akka.util.ByteString import boopickle.Default._ private[server] trait BinaryMarshaller { implicit def defaultMarshaller[T: Pickler]: ToEntityMarshaller[T] = { val contentType = ContentTypes.`application/octet-stream` Marshaller.withFixedContentType(contentType)((value: T) ⇒ HttpEntity(contentType, ByteString(Pickle.intoBytes(value)))) } def defaultUnmarshaller[A, B](implicit ev: Pickler[B], m: Unmarshaller[A, ByteString]): Unmarshaller[A, B] = { m.map(bs ⇒ Unpickle[B].fromBytes(bs.toByteBuffer)) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/scala/com/karasiq/nanoboard/server/JsonMarshaller.scala version [5228081926].
> > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.karasiq.nanoboard.server import akka.http.scaladsl.marshalling._ import akka.http.scaladsl.model._ import akka.http.scaladsl.unmarshalling.Unmarshaller import play.api.libs.json._ private[server] trait JsonMarshaller { implicit def defaultMarshaller[T: Writes]: ToEntityMarshaller[T] = { Marshaller.withFixedContentType(ContentTypes.`application/json`)((value: T) ⇒ HttpEntity(ContentTypes.`application/json`, Json.toJson(value).toString())) } def defaultUnmarshaller[A, B](implicit ev: Reads[B], m: Unmarshaller[A, String]): Unmarshaller[A, B] = { m.map(str ⇒ Json.parse(str).as[B]) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/scala/com/karasiq/nanoboard/server/Main.scala version [9de586f46a].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
package com.karasiq.nanoboard.server import java.nio.file.{Files, Paths} import java.time.LocalDate import java.util.concurrent.TimeUnit import akka.actor.ActorSystem import akka.event.Logging import akka.http.scaladsl.Http import akka.http.scaladsl.Http.ServerBinding import akka.stream._ import akka.stream.scaladsl._ import com.karasiq.nanoboard.dispatcher.NanoboardSlickDispatcher import com.karasiq.nanoboard.model.{Place, _} import com.karasiq.nanoboard.server.streaming.BitMessagePublisher import com.karasiq.nanoboard.server.utils.{CaptchaLoader, MessageValidator} import com.karasiq.nanoboard.sources.bitmessage.BitMessageTransport import com.karasiq.nanoboard.sources.png.UrlPngSource import com.karasiq.nanoboard.{NanoboardCategory, NanoboardLegacy, NanoboardMessage} import com.typesafe.config.ConfigFactory import slick.driver.H2Driver.api._ import slick.jdbc.meta.MTable import scala.concurrent.duration._ import scala.concurrent.{Await, Future} import scala.language.postfixOps import scala.util.{Failure, Success} object Main extends App { // Initialize configuration val config = { val default = ConfigFactory.load() val external = Paths.get(default.getString("nanoboard.external-config-file")) if (Files.isRegularFile(external)) { ConfigFactory.parseFile(external.toFile) .withFallback(default) .resolve() } else { default } } implicit val actorSystem = ActorSystem("nanoboard-server", config) implicit val executionContext = actorSystem.dispatcher implicit val actorMaterializer = ActorMaterializer(ActorMaterializerSettings(actorSystem)) val db = Database.forConfig("nanoboard.database", config) actorSystem.registerOnTermination(db.close()) Runtime.getRuntime.addShutdownHook(new Thread(new Runnable { override def run(): Unit = { actorSystem.log.info("Shutting down nanoboard-server") Await.result(actorSystem.terminate(), Duration.Inf) } })) // Initialize database def createSchema = DBIO.seq( containers.schema.create, posts.schema.create, deletedPosts.schema.create, pendingPosts.schema.create, categories.schema.create, places.schema.create, categories += NanoboardCategory("bdd4b5fc1b3a933367bc6830fef72a35", "Metacategory"), categories ++= NanoboardLegacy.categoriesFromTxt("categories.txt"), places ++= NanoboardLegacy.placesFromTxt("places.txt") ) val schema = Source.fromPublisher(db.stream(MTable.getTables)) .runWith(Sink.headOption) .flatMap { case None ⇒ db.run(createSchema) case _ ⇒ Future.successful(()) } // Initialize server actorSystem.log.info("Loading captcha file") schema.flatMap(_ ⇒ CaptchaLoader.load(config)).foreach { captcha ⇒ actorSystem.registerOnTermination(captcha.close()) actorSystem.log.info("Captcha file loaded successfully ({} entries)", captcha.length) val messageValidator = MessageValidator(captcha, config) // Initialize transport val bitMessage = BitMessageTransport(config) val bitMessagePublisher = actorSystem.actorOf(BitMessagePublisher.props(bitMessage), "bitMessagePublisher") val dispatcher = NanoboardSlickDispatcher(db, captcha, config, Sink.foreach(actorSystem.eventStream.publish)) // Bitmessage if (config.getBoolean("nanoboard.bitmessage.receive")) { val host = config.getString("nanoboard.bitmessage.listen-host") val port = config.getInt("nanoboard.bitmessage.listen-port") bitMessage.receiveMessages(host, port, Sink.foreach { (message: NanoboardMessage) ⇒ messageValidator.isMessageValid(message) .filter(identity) .foreach(_ ⇒ dispatcher.addPost(s"bitmessage://${LocalDate.now()}", message)) }) } // Imageboards PNG val messageSource = UrlPngSource(config) val updateInterval = FiniteDuration(config.getDuration("nanoboard.scheduler.update-interval", TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS) val maxNewPosts = config.getInt("nanoboard.scheduler.posts-per-container") val placeFlow = Flow[String] .named("board-png-flow") .flatMapMerge(4, messageSource.imagesFromPage) .mapAsync(1)(url ⇒ db.run(for (e ← containers.filter(_.url === url).exists.result) yield (e, url))) .filterNot(_._1) .map(_._2) .log("board-png-source") .flatMapMerge(4, url ⇒ messageSource.messagesFromImage(url).fold(Vector.empty[NanoboardMessage])(_ :+ _).map((url, _))) .withAttributes(ActorAttributes.supervisionStrategy(Supervision.restartingDecider) and Attributes.logLevels(Logging.InfoLevel, onFailure = Logging.WarningLevel)) Source.tick(10 seconds, updateInterval, akka.NotUsed) .flatMapConcat(_ ⇒ Source.fromPublisher(db.stream(Place.list()))) .via(placeFlow) .runForeach { case (url, messages) ⇒ def insertMessages(messages: Seq[NanoboardMessage], inserted: Int = 0): Unit = messages match { case Seq(message, ms @ _*) if inserted < maxNewPosts ⇒ messageValidator.isMessageValid(message) .flatMap(valid ⇒ if (valid) dispatcher.addPost(url, message) else Future.successful(0)) .foreach(i ⇒ insertMessages(ms, inserted + i)) case Seq(message) if inserted < maxNewPosts ⇒ messageValidator.isMessageValid(message) .filter(identity) .foreach(_ ⇒ dispatcher.addPost(url, message)) case _ ⇒ () } db.run(Container.create(url)).foreach { _ ⇒ insertMessages(messages) } } // REST server val server = NanoboardServer(dispatcher) val host = config.getString("nanoboard.server.host") val port = config.getInt("nanoboard.server.port") Http().bindAndHandle(server.route, host, port).onComplete { case Success(ServerBinding(address)) ⇒ actorSystem.log.info("Nanoboard server listening at {}", address) case Failure(exc) ⇒ actorSystem.log.error(exc, "Port binding failure") } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/scala/com/karasiq/nanoboard/server/NanoboardServer.scala version [97ec7db633].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
package com.karasiq.nanoboard.server import scala.concurrent.ExecutionContext import scala.util.{Failure, Success} import akka.actor.ActorSystem import akka.http.scaladsl.model._ import akka.http.scaladsl.model.MediaType.Compressible import akka.http.scaladsl.model.headers.{`Cache-Control`, CacheDirectives} import akka.http.scaladsl.server.Directives._ import akka.stream.ActorMaterializer import akka.util.ByteString import boopickle.Default._ import com.karasiq.nanoboard.{NanoboardCategory, NanoboardMessage} import com.karasiq.nanoboard.api.{NanoboardCaptchaAnswer, NanoboardReply} import com.karasiq.nanoboard.dispatcher.NanoboardDispatcher import com.karasiq.nanoboard.server.streaming.NanoboardMessageStream import com.karasiq.nanoboard.server.utils.{AttachmentGenerator, FractalMusic} object NanoboardServer { def apply(dispatcher: NanoboardDispatcher)(implicit actorSystem: ActorSystem, actorMaterializer: ActorMaterializer): NanoboardServer = { new NanoboardServer(dispatcher) } } private[server] final class NanoboardServer(dispatcher: NanoboardDispatcher)(implicit actorSystem: ActorSystem, actorMaterializer: ActorMaterializer) extends BinaryMarshaller { private implicit def ec: ExecutionContext = actorSystem.dispatcher private val maxPostSize = actorSystem.settings.config.getMemorySize("nanoboard.max-post-size").toBytes val route = { get { // Single post path("post" / NanoboardMessage.HashFormat) { hash ⇒ complete(StatusCodes.OK, dispatcher.post(hash)) } ~ (pathPrefix("posts") & parameters('offset.as[Long].?(0), 'count.as[Long].?(100))) { (offset, count) ⇒ // Thread path(NanoboardMessage.HashFormat) { hash ⇒ complete(StatusCodes.OK, dispatcher.thread(hash, offset, count)) } ~ // Recent posts pathEndOrSingleSlash { complete(StatusCodes.OK, dispatcher.recent(offset, count)) } } ~ // Pending posts (path("pending") & parameters('offset.as[Long].?(0), 'count.as[Long].?(100))) { (offset, count) ⇒ complete(StatusCodes.OK, dispatcher.pending(offset, count)) } ~ // Categories path("categories") { complete(StatusCodes.OK, dispatcher.categories()) } ~ // Places path("places") { complete(StatusCodes.OK, dispatcher.places()) } ~ // Containers (path("containers") & parameters('offset.as[Long].?(0), 'count.as[Long].?(100))) { (offset, count) ⇒ complete(StatusCodes.OK, dispatcher.containers(offset, count)) } ~ // Fractal music renderer (path("fractal_music" / Segment) & respondWithHeaders(`Cache-Control`(CacheDirectives.public, CacheDirectives.`max-age`(100000000L)))) { formula ⇒ complete(StatusCodes.OK, FractalMusic(formula).map(HttpEntity(ContentType(MediaType.audio("wav", Compressible)), _))) } ~ // Verification data (path("verify" / NanoboardMessage.HashFormat)) { hash ⇒ complete(StatusCodes.OK, dispatcher.requestVerification(hash)) } ~ // Static files encodeResponse(pathEndOrSingleSlash(getFromResource("webapp/index.html")) ~ getFromResourceDirectory("webapp")) } ~ post { // New reply (path("post") & entity(as[NanoboardReply](defaultUnmarshaller))) { case NanoboardReply(parent, message) ⇒ if (message.length <= maxPostSize) { complete(StatusCodes.OK, dispatcher.reply(parent, message)) } else { complete(StatusCodes.custom(400, s"Message is too long. Max size is $maxPostSize bytes"), HttpEntity("")) } } ~ // Create container (path("container") & parameters('pending.as[Int].?(10), 'random.as[Int].?(50), 'format.?("png")) & entity(as[ByteString]) & extractLog) { (pending, random, format, entity, log) ⇒ onComplete(dispatcher.createContainer(pending, random, format, entity)) { case Success(data) ⇒ complete(StatusCodes.OK, HttpEntity(data)) case Failure(exc) ⇒ log.error(exc, "Container creation error") complete(StatusCodes.custom(500, "Container creation error"), HttpEntity(ByteString.empty)) } } ~ // Generate attachment (path("attachment") & parameters('format.?("jpeg"), 'size.as[Int].?(500), 'quality.as[Int].?(70)) & entity(as[ByteString])) { (format, size, quality, data) ⇒ complete(StatusCodes.OK, HttpEntity(ContentTypes.`text/plain(UTF-8)`, AttachmentGenerator.createImage(format, size, quality, data))) } ~ // Verify post (path("verify") & entity[NanoboardCaptchaAnswer](defaultUnmarshaller)) { answer ⇒ complete(StatusCodes.OK, dispatcher.verifyPost(answer.request, answer.answer)) } } ~ delete { // Delete single post path("post" / NanoboardMessage.HashFormat) { hash ⇒ extractLog { log ⇒ log.info("Post permanently deleted: {}", hash) complete(StatusCodes.OK, dispatcher.delete(hash)) } } ~ // Delete container posts (path("posts") & parameter('container.as[Long])) { container ⇒ complete(StatusCodes.OK, dispatcher.clearContainer(container)) } ~ // Delete post from pending list path("pending" / NanoboardMessage.HashFormat) { hash ⇒ complete(StatusCodes.OK, dispatcher.markAsNotPending(hash)) } ~ // Batch delete recent posts (path("posts") & parameters('offset.as[Long].?(0), 'count.as[Long])) { (offset, count) ⇒ // Batch delete complete(StatusCodes.OK, dispatcher.delete(offset, count)) } ~ // Clear deleted posts cache path("deleted") { complete(StatusCodes.OK, dispatcher.clearDeleted()) } } ~ put { // Update places list (path("places") & entity(as[Seq[String]](defaultUnmarshaller)) & extractLog) { (places, log) ⇒ log.info("Places updated: {}", places) complete(StatusCodes.OK, dispatcher.updatePlaces(places)) } ~ // Update categories list (path("categories") & entity(as[Seq[NanoboardCategory]](defaultUnmarshaller)) & extractLog) { (categories, log) ⇒ log.info("Categories updated: {}", categories) complete(StatusCodes.OK, dispatcher.updateCategories(categories)) } ~ // Add post to pending list path("pending" / NanoboardMessage.HashFormat) { hash ⇒ complete(StatusCodes.OK, dispatcher.markAsPending(hash)) } } ~ // Event channel path("live") { handleWebSocketMessages(NanoboardMessageStream.flow) } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/scala/com/karasiq/nanoboard/server/streaming/BitMessagePublisher.scala version [551a708881].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
package com.karasiq.nanoboard.server.streaming import akka.actor.{Actor, ActorLogging, Props} import com.karasiq.nanoboard.model.MessageConversions import com.karasiq.nanoboard.sources.bitmessage.BitMessageTransport import com.karasiq.nanoboard.streaming.NanoboardEvent import scala.util.{Failure, Success} private[server] object BitMessagePublisher { def props(bitMessage: BitMessageTransport) = { Props(classOf[BitMessagePublisher], bitMessage) } } private[server] class BitMessagePublisher(bitMessage: BitMessageTransport) extends Actor with ActorLogging { import context.dispatcher override def preStart(): Unit = { super.preStart() context.system.eventStream.subscribe(self, classOf[NanoboardEvent]) } override def postStop(): Unit = { context.system.eventStream.unsubscribe(self) super.postStop() } override def receive: Receive = { case NanoboardEvent.PostVerified(message) ⇒ log.debug("Sending message to BM transport: {}", message) bitMessage.sendMessage(MessageConversions.unwrapToMessage(message)).onComplete { case Success(response) ⇒ log.info("Message was sent to BM transport: {}", response) case Failure(exc) ⇒ if (log.isDebugEnabled) { log.error(exc, "Error sending message to BM transport: {}", message) } } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/scala/com/karasiq/nanoboard/server/streaming/NanoboardMessagePublisher.scala version [2cf6e9f307].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
package com.karasiq.nanoboard.server.streaming import akka.stream.actor.ActorPublisher import akka.stream.actor.ActorPublisherMessage.{Cancel, Request} import com.karasiq.nanoboard.streaming.NanoboardEvent import scala.annotation.tailrec private[server] class NanoboardMessagePublisher extends ActorPublisher[NanoboardEvent] { override def preStart(): Unit = { super.preStart() context.system.eventStream.subscribe(self, classOf[NanoboardEvent]) } override def postStop(): Unit = { context.system.eventStream.unsubscribe(self) super.postStop() } val maxBufferSize = 20 var messageBuffer = Vector.empty[NanoboardEvent] override def receive: Receive = { case m: NanoboardEvent ⇒ if (messageBuffer.isEmpty && totalDemand > 0) { onNext(m) } else { if (messageBuffer.length >= maxBufferSize) { messageBuffer = messageBuffer.tail :+ m } else { messageBuffer :+= m } deliverBuffer() } case Request(_) ⇒ deliverBuffer() case Cancel ⇒ context.stop(self) } @tailrec final def deliverBuffer(): Unit = { if (totalDemand > 0) { if (totalDemand <= Int.MaxValue) { val (use, keep) = messageBuffer.splitAt(totalDemand.toInt) messageBuffer = keep use foreach onNext } else { val (use, keep) = messageBuffer.splitAt(Int.MaxValue) messageBuffer = keep use foreach onNext deliverBuffer() } } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/scala/com/karasiq/nanoboard/server/streaming/NanoboardMessageStream.scala version [dac0877cea].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
package com.karasiq.nanoboard.server.streaming import scala.concurrent.duration._ import scala.language.postfixOps import akka.actor.Props import akka.http.scaladsl.model.ws.{BinaryMessage, Message, TextMessage} import akka.stream._ import akka.stream.scaladsl.{Flow, GraphDSL, Source} import akka.stream.stage.{GraphStage, GraphStageLogic, InHandler, OutHandler} import akka.util.ByteString import boopickle.Default._ import com.karasiq.nanoboard.streaming.{NanoboardEvent, NanoboardEventSeq, NanoboardSubscription} import com.karasiq.nanoboard.streaming.NanoboardSubscription.{PostHashes, Unfiltered} private[server] final class NanoboardMessageStream extends GraphStage[FanInShape2[NanoboardSubscription, NanoboardEvent, NanoboardEvent]] { val input: Inlet[NanoboardSubscription] = Inlet("SubscriptionInput") val events: Inlet[NanoboardEvent] = Inlet("EventStream") val output: Outlet[NanoboardEvent] = Outlet("EventOutput") override def shape = new FanInShape2(input, events, output) override def createLogic(inheritedAttributes: Attributes) = new GraphStageLogic(shape) { private var subscription: NanoboardSubscription = PostHashes(Set.empty) def request(): Unit = { if (!hasBeenPulled(events)) { pull(events) } if (!hasBeenPulled(input)) { pull(input) } } setHandler(input, new InHandler { override def onPush(): Unit = { subscription = grab(input) request() } }) setHandler(events, new InHandler { override def onPush(): Unit = { grab(events) match { case added @ NanoboardEvent.PostAdded(message) ⇒ subscription match { case PostHashes(hashes) ⇒ if (hashes.exists(message.parent.contains) || hashes.contains(message.hash)) { emit(output, added) } case Unfiltered ⇒ emit(output, added) } case event ⇒ emit(output, event) } request() } }) setHandler(output, new OutHandler { override def onPull(): Unit = { request() } }) } } private[server] object NanoboardMessageStream { import com.karasiq.nanoboard.streaming.NanoboardSubscription._ def flow = Flow.fromGraph(GraphDSL.create() { implicit b: GraphDSL.Builder[akka.NotUsed] ⇒ import GraphDSL.Implicits._ val in = b.add { Flow[Message] .named("websocketInput") .flatMapConcat { case bm: BinaryMessage ⇒ bm.dataStream.fold(ByteString.empty)(_ ++ _) case tm: TextMessage ⇒ tm.textStream.fold("")(_ ++ _).map(ByteString(_)) } .map(bs ⇒ Unpickle[NanoboardSubscription].fromBytes(bs.toByteBuffer)) } val out = b.add { Flow[NanoboardEvent] .groupedWithin(1000, 5 seconds) .filter(_.nonEmpty) .map(events ⇒ BinaryMessage(ByteString(Pickle.intoBytes(NanoboardEventSeq(events))))) .named("websocketOutput") } val messages = b.add(Source.actorPublisher[NanoboardEvent](Props[NanoboardMessagePublisher])) val processor = b.add(new NanoboardMessageStream) in.out ~> processor.in0 messages.out ~> processor.in1 processor.out ~> out.in FlowShape(in.in, out.out) }) } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/scala/com/karasiq/nanoboard/server/utils/AttachmentGenerator.scala version [d2e225e272].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
package com.karasiq.nanoboard.server.utils import java.awt.RenderingHints._ import java.awt.image._ import java.awt.{Dimension, Image, Toolkit} import java.io.ByteArrayOutputStream import javax.imageio.stream.ImageOutputStream import javax.imageio.{IIOImage, ImageIO, ImageWriteParam, ImageWriter} import akka.util.ByteString import org.apache.commons.codec.binary.Base64 import org.apache.commons.io.IOUtils import scala.collection.JavaConversions._ object AttachmentGenerator { private val resizer = new ImageResizingUtil def createImage(format: String, size: Int, quality: Int, data: ByteString): ByteString = { ByteString(Base64.encodeBase64(resizer.compress(resizer.resize(data.toArray, size), format, quality))) } } private[utils] final class ImageResizingUtil { private def getScaledDimension(imgSize: Dimension, boundary: Dimension): Dimension = { var newWidth: Int = imgSize.width var newHeight: Int = imgSize.height if (imgSize.width > boundary.width) { newWidth = boundary.width newHeight = (newWidth * imgSize.height) / imgSize.width } if (newHeight > boundary.height) { newHeight = boundary.height newWidth = (newHeight * imgSize.width) / imgSize.height } new Dimension(newWidth, newHeight) } private def redrawImage(img: Image, size: Dimension): BufferedImage = { val bufferedImage = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_RGB) val graphics = bufferedImage.createGraphics() try { // Max quality settings graphics.setRenderingHints(Map( KEY_RENDERING → VALUE_RENDER_QUALITY, KEY_COLOR_RENDERING → VALUE_COLOR_RENDER_QUALITY, KEY_ANTIALIASING → VALUE_ANTIALIAS_ON, KEY_INTERPOLATION → VALUE_INTERPOLATION_BICUBIC )) graphics.drawImage(img, 0, 0, size.width, size.height, null) bufferedImage } finally { graphics.dispose() } } // JPG colors fix private def loadImage(image: Array[Byte]): BufferedImage = { val img = Toolkit.getDefaultToolkit.createImage(image) val RGB_MASKS: Array[Int] = Array(0xFF0000, 0xFF00, 0xFF) val RGB_OPAQUE: ColorModel = new DirectColorModel(32, RGB_MASKS(0), RGB_MASKS(1), RGB_MASKS(2)) val pg = new PixelGrabber(img, 0, 0, -1, -1, true) pg.grabPixels() (pg.getWidth, pg.getHeight, pg.getPixels) match { case (width, height, pixels: Array[Int]) ⇒ val buffer = new DataBufferInt(pixels, width * height) val raster = Raster.createPackedRaster(buffer, width, height, width, RGB_MASKS, null) new BufferedImage(RGB_OPAQUE, raster, false, null) case _ ⇒ throw new IllegalArgumentException("Invalid image") } } def compress(image: BufferedImage, format: String, quality: Int): Array[Byte] = { val outputStream = new ByteArrayOutputStream() val imageOutputStream: ImageOutputStream = ImageIO.createImageOutputStream(outputStream) try { val writer: ImageWriter = ImageIO.getImageWritersByFormatName(format).next val iwp: ImageWriteParam = writer.getDefaultWriteParam if (iwp.canWriteCompressed) { iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT) iwp.setCompressionQuality(quality.toFloat / 100.0f) } writer.setOutput(imageOutputStream) writer.write(null, new IIOImage(image, null, null), iwp) writer.dispose() imageOutputStream.flush() outputStream.toByteArray } finally { IOUtils.closeQuietly(imageOutputStream) IOUtils.closeQuietly(outputStream) } } def resize(data: Array[Byte], size: Int): BufferedImage = { val image = loadImage(data) val imgSize = new Dimension(image.getWidth, image.getHeight) val boundary = new Dimension(size, size) val newSize = getScaledDimension(imgSize, boundary) redrawImage(image, newSize) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/scala/com/karasiq/nanoboard/server/utils/CaptchaLoader.scala version [6217b05108].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
package com.karasiq.nanoboard.server.utils import java.nio.file.{Files, Path, Paths} import akka.actor.ActorSystem import akka.http.scaladsl.Http import akka.http.scaladsl.model.headers.Location import akka.http.scaladsl.model.{HttpRequest, HttpResponse, Uri} import akka.stream.ActorMaterializer import akka.stream.scaladsl.{FileIO, Source} import com.karasiq.nanoboard.captcha.storage.{NanoboardCaptchaFileSource, NanoboardCaptchaSource} import com.typesafe.config.{Config, ConfigFactory} import scala.concurrent.{ExecutionContext, Future} object CaptchaLoader { def apply(config: Config = ConfigFactory.load())(implicit am: ActorMaterializer, as: ActorSystem, ec: ExecutionContext): CaptchaLoader = { new CaptchaLoader(Paths.get(config.getString("nanoboard.captcha.storage"))) } def load(config: Config = ConfigFactory.load())(implicit am: ActorMaterializer, as: ActorSystem, ec: ExecutionContext): Future[NanoboardCaptchaFileSource] = { apply(config).forUrl(config.getString("nanoboard.captcha.download-url")) } } final class CaptchaLoader(baseDir: Path)(implicit am: ActorMaterializer, as: ActorSystem, ec: ExecutionContext) { private val http = Http() // TODO: https://github.com/akka/akka/issues/15990 private def requestWithRedirects(uri: Uri): Future[HttpResponse] = { http.singleRequest(HttpRequest(uri = uri)).flatMap { response ⇒ val location = response.header[Location] if (response.status.isRedirection() && location.isDefined) { requestWithRedirects(location.get.uri) } else { Future.successful(response) } } } def forUrl(url: String): Future[NanoboardCaptchaFileSource] = { val fileName = baseDir.resolve(s"${Integer.toHexString(url.hashCode)}.nbc") if (Files.exists(fileName)) { assert(Files.isRegularFile(fileName), s"Not a file: $fileName") Future.successful(NanoboardCaptchaSource.fromFile(fileName.toString)) } else { Source .fromFuture(requestWithRedirects(url)) .flatMapConcat(_.entity.dataBytes) .toMat(FileIO.toFile(fileName.toFile))((_, r) ⇒ r.map { ioResult ⇒ if (ioResult.wasSuccessful) NanoboardCaptchaSource.fromFile(fileName.toString) else throw ioResult.getError }) .run() } } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/scala/com/karasiq/nanoboard/server/utils/FractalMusic.scala version [051f3de01a].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
package com.karasiq.nanoboard.server.utils import java.util.concurrent.{Executors, TimeUnit, TimeoutException} import javax.script.ScriptEngine import akka.util.ByteString import jdk.nashorn.api.scripting.NashornScriptEngineFactory import scala.concurrent.duration.{FiniteDuration, _} import scala.concurrent.{Future, Promise} import scala.language.postfixOps object FractalMusic extends FractalMusicGenerator class FractalMusicGenerator { private val scheduler = Executors.newScheduledThreadPool(1) private val engineFactory = new NashornScriptEngineFactory() protected def createScriptEngine(): ScriptEngine = { engineFactory.getScriptEngine(Array("-strict", "--no-java", "--no-syntax-extensions"), getClass.getClassLoader) } def apply(formula: String, timeLimit: FiniteDuration = 5 seconds): Future[ByteString] = { val source = s""" |(function(){ | var sampleRate = 8000; | var data = [] | for (var t = 0; t < 4*65536; t++) { | data[t] = $formula; | data[t] = (data[t] & 0xff) / 256.0; | } | | var n = data.length; | var integer = 0, i; | var header = 'RIFF<##>WAVEfmt \\x10\\x00\\x00\\x00\\x01\\x00\\x01\\x00<##><##>\\x01\\x00\\x08\\x00data<##>'; | | function insertLong(value) { | var bytes = ""; | for (i = 0; i < 4; ++i) { | bytes += String.fromCharCode(value % 256); | value = Math.floor(value / 256); | } | header = header.replace('<##>', bytes); | } | | insertLong(36 + n); | insertLong(sampleRate); | insertLong(sampleRate); | insertLong(n); | | for (var i = 0; i < n; ++i) { | header += String.fromCharCode(Math.round(Math.min(1, Math.max(-1, data[i])) * 127 + 127)); | } | return header; |})(); """.stripMargin val promise = Promise[ByteString] val thread = new Thread(new Runnable { override def run(): Unit = { try { val engine = createScriptEngine() promise.success(ByteString(engine.eval(source).asInstanceOf[String].toCharArray.map(_.toByte))) } catch { case exc: Throwable ⇒ promise.failure(exc) } } }) scheduler.schedule(new Runnable { override def run(): Unit = { if (!promise.isCompleted) { thread.interrupt() promise.failure(new TimeoutException("JavaScript execution timed out")) } } }, timeLimit.toMillis, TimeUnit.MILLISECONDS) thread.start() promise.future } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/main/scala/com/karasiq/nanoboard/server/utils/MessageValidator.scala version [3beb7a826f].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
package com.karasiq.nanoboard.server.utils import scala.collection.JavaConversions._ import scala.concurrent.{ExecutionContext, Future} import com.typesafe.config.{Config, ConfigFactory} import com.karasiq.nanoboard.NanoboardMessage import com.karasiq.nanoboard.captcha.{NanoboardCaptcha, NanoboardPow} import com.karasiq.nanoboard.captcha.storage.NanoboardCaptchaSource private[server] object MessageValidator { def apply(captcha: NanoboardCaptchaSource, config: Config = ConfigFactory.load())(implicit ec: ExecutionContext): MessageValidator = { new MessageValidator(captcha, config) } } private[server] final class MessageValidator(captcha: NanoboardCaptchaSource, config: Config)(implicit ec: ExecutionContext) { private[this] val requirePow = config.getBoolean("nanoboard.pow-required") private[this] val maxPostSize = config.getMemorySize("nanoboard.max-post-size").toBytes private[this] val spamFilter = config.getStringList("nanoboard.scheduler.spam-filter").toVector private[this] val powCalculator = NanoboardPow(config) def isMessageValid(message: NanoboardMessage): Future[Boolean] = { Future.reduce(Seq( Future.successful( message.parent.matches(NanoboardMessage.HashFormat.regex) && message.text.nonEmpty && message.text.length <= maxPostSize && spamFilter.forall(!message.text.matches(_)) ), if (requirePow) NanoboardCaptcha.verify(message, powCalculator, captcha) else Future.successful(true) ))(_ && _) } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/test/resources/application.conf version [73cf384a3a].
> > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 |
nanoboard { test-database { url = "jdbc:h2:mem:test1" driver = org.h2.Driver connectionPool = disabled keepAliveConnection = true } } akka.loglevel = DEBUG |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/test/scala/com/karasiq/nanoboard/server/test/DatabaseTest.scala version [cf0f293518].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
package com.karasiq.nanoboard.server.test import com.karasiq.nanoboard.NanoboardMessageGenerator import com.karasiq.nanoboard.api.NanoboardMessageData import com.karasiq.nanoboard.model._ import org.scalatest.{BeforeAndAfterAll, FlatSpec, Matchers} import slick.driver.H2Driver.api._ import scala.concurrent.Await import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ class DatabaseTest extends FlatSpec with Matchers with BeforeAndAfterAll { val testMessage = NanoboardMessageGenerator().newMessage("8b8cfb7574741838450e286909e8fd1f", "Hello world!") val db = Database.forConfig("nanoboard.test-database") "Database" should "add entry" in { println(testMessage) assert(testMessage.parent.length == testMessage.hash.length) val query = for { _ ← DBIO.seq(containers.schema.create, posts.schema.create, deletedPosts.schema.create, pendingPosts.schema.create, categories.schema.create) c ← Container.forUrl("local://test") _ ← Post.insertMessage(c, testMessage) message ← Post.get(testMessage.hash) } yield message val result: Option[NanoboardMessageData] = Await.result(db.run(query), Duration.Inf) println(result) result.get.hash shouldBe testMessage.hash val answers: Vector[NanoboardMessageData] = Await.result(db.run(Post.thread("8b8cfb7574741838450e286909e8fd1f", 0, 10)), Duration.Inf) answers.map(_.hash) shouldBe Vector(testMessage.hash) Await.result(db.run(Container.forUrl("local://test")), Duration.Inf) shouldBe result.get.containerId.get } it should "delete entry" in { val delete = Post.delete(testMessage.hash) val query = for (_ ← delete; ps ← posts.result) yield ps val result = Await.result(db.run(query), Duration.Inf) result shouldBe empty } override protected def afterAll(): Unit = { db.close() super.afterAll() } } |
Added wiki_references/2017/software/steganography_related_references/karasiq-nanoboard/src_from_GitHub/the_repository_clones/nanoboard/src/test/scala/com/karasiq/nanoboard/server/test/FractalMusicTest.scala version [ca419ceff6].
> > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package com.karasiq.nanoboard.server.test import com.karasiq.nanoboard.encoding.NanoboardCrypto._ import com.karasiq.nanoboard.server.utils.FractalMusic import com.karasiq.nanoboard.utils._ import org.scalatest.{FlatSpec, Matchers} import scala.concurrent.duration._ import scala.concurrent.{Await, TimeoutException} import scala.language.postfixOps class FractalMusicTest extends FlatSpec with Matchers { "Fractal music generator" should "generate WAV from formula" in { val result = Await.result(FractalMusic("(t%((t>>3)+((t>>2)%2?-5:-10)/t>>8&(130+((t%65536)>>10))))<<2", 10 minutes), Duration.Inf) sha256.digest(result).toHexString() shouldBe "5b19d273699ecb5009bdf505221c4cd05cde3cb8d81b35d34dc0ca19b472f815" } it should "halt the execution of infinite loop" in { intercept[TimeoutException](Await.result(FractalMusic("(function(){while(true){};})()", 1 seconds), Duration.Inf)) } } |