1
0
mirror of https://github.com/nokonoko/Uguu.git synced 2024-01-06 13:35:15 +00:00

Merge branch 'master' into master

This commit is contained in:
Selvakumar 2023-02-10 22:14:36 +05:30 committed by GitHub
commit f253f45673
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1057 additions and 5834 deletions

3
.gitignore vendored
View File

@ -8,3 +8,6 @@ uguu.sq3
.idea .idea
.phpdoc .phpdoc
.vscode .vscode
composer.phar
composer.lock
docker/uguuForDocker.tar.gz

1
.node_version Normal file
View File

@ -0,0 +1 @@
v19.3.0

1
.php-cs-fixer.cache Normal file
View File

@ -0,0 +1 @@
{"php":"8.1.13","version":"3.13.1","indent":" ","lineEnding":"\n","rules":{"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"braces":{"allow_single_line_anonymous_class_with_empty_body":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_typehint":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_braces":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"imports_order":["class","function","const"],"sort_algorithm":"none"},"return_type_declaration":true,"short_scalar_cast":true,"single_blank_line_before_namespace":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline"},"no_break_comment":true,"no_closing_tag":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true},"hashes":{"\/private\/var\/folders\/5_\/px3cvvbs4rd5bjzl8py2mjnc0000gq\/T\/PHP CS Fixertemp_folder3788\/src\/static\/php\/upload.php":"b626750d2d9ac4f2b2f98a8ec8219533","src\/static\/php\/upload.php":"2aa4d70e18ce7445ddd4ba0f76fa0621","\/private\/var\/folders\/5_\/px3cvvbs4rd5bjzl8py2mjnc0000gq\/T\/PHP CS Fixertemp_folder1142\/src\/static\/php\/upload.php":"b626750d2d9ac4f2b2f98a8ec8219533","\/private\/var\/folders\/5_\/px3cvvbs4rd5bjzl8py2mjnc0000gq\/T\/PHP CS Fixertemp_folder24\/src\/static\/php\/upload.php":"2aa4d70e18ce7445ddd4ba0f76fa0621","\/private\/var\/folders\/5_\/px3cvvbs4rd5bjzl8py2mjnc0000gq\/T\/PHP CS Fixertemp_folder1007\/src\/static\/php\/upload.php":"2aa4d70e18ce7445ddd4ba0f76fa0621","\/private\/var\/folders\/5_\/px3cvvbs4rd5bjzl8py2mjnc0000gq\/T\/PHP CS Fixertemp_folder3242\/src\/static\/php\/upload.php":"2aa4d70e18ce7445ddd4ba0f76fa0621","\/private\/var\/folders\/5_\/px3cvvbs4rd5bjzl8py2mjnc0000gq\/T\/PHP CS Fixertemp_folder681\/src\/static\/php\/upload.php":"2aa4d70e18ce7445ddd4ba0f76fa0621","\/private\/var\/folders\/5_\/px3cvvbs4rd5bjzl8py2mjnc0000gq\/T\/PHP CS Fixertemp_folder1535\/src\/static\/php\/upload.php":"2aa4d70e18ce7445ddd4ba0f76fa0621","\/private\/var\/folders\/5_\/px3cvvbs4rd5bjzl8py2mjnc0000gq\/T\/PHP CS Fixertemp_folder1163\/src\/static\/php\/upload.php":"2aa4d70e18ce7445ddd4ba0f76fa0621","\/private\/var\/folders\/5_\/px3cvvbs4rd5bjzl8py2mjnc0000gq\/T\/PHP CS Fixertemp_folder4666\/src\/static\/php\/upload.php":"2aa4d70e18ce7445ddd4ba0f76fa0621","\/private\/var\/folders\/5_\/px3cvvbs4rd5bjzl8py2mjnc0000gq\/T\/PHP CS Fixertemp_folder5333\/src\/static\/php\/upload.php":"dc38feb300c1c8ab4614639d5a7af4f7"}}

View File

@ -11,52 +11,63 @@ CONF="src/config.json"
PHP="php" PHP="php"
CURL="curl" CURL="curl"
DESTDIR = $(shell $(CURDIR)/$(NODEJQ) -r ".dest" $(CURDIR)/$(CONF)) DESTDIR = $(shell $(CURDIR)/$(NODEJQ) -r ".dest" $(CURDIR)/$(CONF))
NPX="npx" SITEDOMAIN = $(shell $(CURDIR)/$(NODEJQ) -r ".DOMAIN" $(CURDIR)/$(CONF))
FILESDOMAIN = $(shell $(CURDIR)/$(NODEJQ) -r ".FILE_DOMAIN" $(CURDIR)/$(CONF))
MAXSIZE = $(shell $(CURDIR)/$(NODEJQ) -r ".max_upload_size" $(CURDIR)/$(CONF))
CONTACT_EMAIL = $(shell $(CURDIR)/$(NODEJQ) -r ".infoContact" $(CURDIR)/$(CONF))
PKG_VERSION = $(shell $(CURDIR)/$(NODEJQ) -r ".version" $(CURDIR)/package.json) PKG_VERSION = $(shell $(CURDIR)/$(NODEJQ) -r ".version" $(CURDIR)/package.json)
EXPIRE_TIME = $(shell $(CURDIR)/$(NODEJQ) -r ".expireTime" $(CURDIR)/$(CONF))
TMPDIR = $(shell mktemp -d) TMPDIR = $(shell mktemp -d)
DOCKER_IMAGE = "$(shell basename $(CURDIR) | tr [:upper:] [:lower:])" DOCKER_IMAGE = "$(shell basename $(CURDIR) | tr [:upper:] [:lower:])"
DOCKER_TAG="$(DOCKER_TAG)" DOCKER_TAG = "$(DOCKER_TAG)"
CONTAINER_NAME="$(CONTAINER_NAME)" CONTAINER_NAME = "$(CONTAINER_NAME)"
# default modules
MODULES="php"
pageList = $(shell $(CURDIR)/$(NODEJQ) -r ".pages[]" $(CURDIR)/$(CONF)) pageList = $(shell $(CURDIR)/$(NODEJQ) -r ".pages[]" $(CURDIR)/$(CONF))
noExt = $(shell echo $(i) | cut -d '.' -f1) noExt = $(shell echo $(i) | cut -d '.' -f1)
all: builddirs npm_dependencies composer ejs minify-all copy-img copy-php all: builddirs npm_dependencies ejs "node_modules/minify/bin/minify.js"-all copy-img copy-php
ejs: ejs:
$(foreach i,$(pageList), \ $(foreach i,$(pageList), \
$(NPX) ejs -f $(CURDIR)/$(CONF) $(CURDIR)/src/templates/$(i) -o $(CURDIR)/build/html/unmin/$(noExt).html;) "node_modules/ejs/bin/cli.js" -f $(CURDIR)/$(CONF) $(CURDIR)/src/templates/$(i) -o $(CURDIR)/build/html/unmin/$(noExt).html;)
minify-all: "node_modules/minify/bin/minify.js"-all:
$(NPX) minify-all-cli -s $(CURDIR)/src/static/js -d $(CURDIR)/build/js "node_modules/minify/bin/minify.js" $(CURDIR)/src/static/js/uguu.js > $(CURDIR)/build/js/uguu.min.js
$(NPX) minify-all-cli -s $(CURDIR)/src/static/css -d $(CURDIR)/build/css "node_modules/minify/bin/minify.js" $(CURDIR)/src/static/css/uguu.css > $(CURDIR)/build/css/uguu.min.css
$(NPX) minify-all-cli -s $(CURDIR)/build/html/unmin/ -d $(CURDIR)/build/html/min/ -h "node_modules/minify/bin/minify.js" $(CURDIR)/build/html/unmin/faq.html > $(CURDIR)/build/html/min/faq.html
"node_modules/minify/bin/minify.js" $(CURDIR)/build/html/unmin/tools.html > $(CURDIR)/build/html/min/tools.html
"node_modules/minify/bin/minify.js" $(CURDIR)/build/html/unmin/index.html > $(CURDIR)/build/html/min/index.html
installdirs: installdirs:
mkdir -p $(DESTDIR)/ $(DESTDIR)/img mkdir -p $(DESTDIR)/ $(DESTDIR)/img
mkdir -p $(DESTDIR)/ $(DESTDIR)/img/grills mkdir -p $(DESTDIR)/ $(DESTDIR)/img/grills
copy-img: copy-img:
$(NPX) imagemin $(CURDIR)/src/static/img/*.png -o=$(CURDIR)/build/img/ "node_modules/imagemin-cli/cli.js" $(CURDIR)/src/static/img/*.png -o=$(CURDIR)/build/img/
$(NPX) imagemin $(CURDIR)/src/static/img/grills/*.png --plugin=pngquant -o=$(CURDIR)/build/img/grills/ "node_modules/imagemin-cli/cli.js" $(CURDIR)/src/static/img/grills/*.png --plugin=pngquant -o=$(CURDIR)/build/img/grills/
copy-php: copy-php:
cp -v $(CURDIR)/src/static/php/*.php $(CURDIR)/build/php/ cp -v $(CURDIR)/src/static/php/*.php $(CURDIR)/build/php/
cp -v $(CURDIR)/src/Classes/*.php $(CURDIR)/build/php/Classes/
install: installdirs install: installdirs
cp -rv $(CURDIR)/build/* $(DESTDIR)/ cp -rv $(CURDIR)/build/* $(DESTDIR)/
mv $(DESTDIR)/html/min/* $(DESTDIR)/ cp $(CURDIR)/src/*.json $(DESTDIR)/
mv $(DESTDIR)/js/* $(DESTDIR)/ mv $(DESTDIR)/html/min/* $(DESTDIR)/public/
mv $(DESTDIR)/css/* $(DESTDIR)/ mv $(DESTDIR)/js/* $(DESTDIR)/public/
mv $(DESTDIR)/css/* $(DESTDIR)/public/
mv $(DESTDIR)/php/* $(DESTDIR)/ mv $(DESTDIR)/php/* $(DESTDIR)/
rm -rf $(DESTDIR)/html rm -rf $(DESTDIR)/html
rm -rf $(DESTDIR)/css rm -rf $(DESTDIR)/css
rm -rf $(DESTDIR)/js rm -rf $(DESTDIR)/js
rm -rf $(DESTDIR)/php rm -rf $(DESTDIR)/php
mv $(DESTDIR)/uguu.css $(DESTDIR)/uguu.min.css mv $(DESTDIR)/img $(DESTDIR)/public/
mv $(DESTDIR)/uguu.js $(DESTDIR)/uguu.min.js mv $(DESTDIR)/grill.php $(DESTDIR)/public/
mv $(DESTDIR)/upload.php $(DESTDIR)/public/
cd $(DESTDIR)/ && $(CURL) -o composer-setup.php https://raw.githubusercontent.com/composer/getcomposer.org/main/web/installer
cd $(DESTDIR)/ && $(PHP) composer-setup.php --quiet
cd $(DESTDIR)/ && rm composer-setup.php
cd $(DESTDIR)/ && php composer.phar update && php composer.phar install && php composer.phar dump-autoload
submodule-update: submodule-update:
cd ansible && git clone git@github.com:s3lva-kumar/ansible-role-uguu.git && git submodule update --remote cd ansible && git clone git@github.com:s3lva-kumar/ansible-role-uguu.git && git submodule update --remote
@ -84,26 +95,27 @@ uninstall:
npm_dependencies: npm_dependencies:
$(NPM) install $(NPM) install
composer: build-container-no-cache:
$(CURL) -o composer-setup.php https://raw.githubusercontent.com/composer/getcomposer.org/main/web/installer tar --exclude='uguuForDocker.tar.gz' --exclude='vendor' --exclude='node_modules' --exclude='build' --exclude='dist' --exclude='.git' -czf uguuForDocker.tar.gz src docker Makefile package.json package-lock.json
$(PHP) composer-setup.php --quiet
rm composer-setup.php
php composer.phar update
php composer.phar install
build-image:
tar --exclude='./uguuForDocker.tar.gz' --exclude='./vendor' --exclude='./node_modules' -czf uguuForDocker.tar.gz .
mv uguuForDocker.tar.gz docker/ mv uguuForDocker.tar.gz docker/
docker build -f docker/Dockerfile --build-arg VERSION=$(UGUU_RELEASE_VER) --no-cache -t $(DOCKER_IMAGE):$(DOCKER_TAG) . docker build -f docker/Dockerfile --build-arg DOMAIN=$(SITEDOMAIN) --build-arg FILE_DOMAIN=$(FILESDOMAIN) --build-arg CONTACT_EMAIL=$(CONTACT_EMAIL) --build-arg MAX_SIZE=$(MAXSIZE) --build-arg EXPIRE_TIME=$(EXPIRE_TIME) --no-cache -t uguu:$(PKG_VERSION) .
build-container:
tar --exclude='uguuForDocker.tar.gz' --exclude='vendor' --exclude='node_modules' --exclude='build' --exclude='dist' --exclude='.git' -czf uguuForDocker.tar.gz src docker Makefile package.json package-lock.json
mv uguuForDocker.tar.gz docker/
docker build -f docker/Dockerfile --build-arg DOMAIN=$(SITEDOMAIN) --build-arg FILE_DOMAIN=$(FILESDOMAIN) --build-arg CONTACT_EMAIL=$(CONTACT_EMAIL) --build-arg MAX_SIZE=$(MAXSIZE) --build-arg EXPIRE_TIME=$(EXPIRE_TIME) -t uguu:$(PKG_VERSION) .
run-container: run-container:
docker run --name $(CONTAINER_NAME) -d -p 8080:80 -p 8081:443 $(DOCKER_IMAGE):$(DOCKER_TAG) docker run --name uguu -d -p 80:80 -p 443:443 uguu:$(PKG_VERSION)
purge-container: purge-containers:
if docker images | grep $(DOCKER_IMAGE); then \ if docker images | grep uguu; then \
docker rm -f $(CONTAINER_NAME) && docker rmi $(DOCKER_IMAGE):$(DOCKER_TAG) || true;\ docker rm -f uguu && docker rmi uguu:$(PKG_VERSION) || true;\
fi; fi;
builddirs: remove-container:
mkdir -p $(CURDIR)/build $(CURDIR)/build/img $(CURDIR)/build/html $(CURDIR)/build/html/min $(CURDIR)/build/html/unmin $(CURDIR)/build/js $(CURDIR)/build/css $(CURDIR)/build/php docker rm -f uguu
builddirs:
mkdir -p $(CURDIR)/build $(CURDIR)/build/img $(CURDIR)/build/html $(CURDIR)/build/html/min $(CURDIR)/build/html/unmin $(CURDIR)/build/js $(CURDIR)/build/css $(CURDIR)/build/php $(CURDIR)/build/php/Classes $(CURDIR)/build/public

View File

@ -1,24 +1,28 @@
# Uguu # Uguu
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=nokonoko_Uguu&metric=alert_status)](https://sonarcloud.io/dashboard?id=nokonoko_Uguu) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=nokonoko_Uguu&metric=alert_status)](https://sonarcloud.io/dashboard?id=nokonoko_Uguu)
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![Documentation Status](https://docs.uguu.se/img/flat.svg)](https://docs.uguu.se) [![Documentation Status](https://docs.uguu.se/img/flat.svg)](https://docs.uguu.se)
# What is Uguu? # What is Uguu?
Uguu is a simple lightweight temporary file hosting and sharing platform, but can also be used as a permanent file host. Uguu is a simple lightweight temporary file hosting and sharing platform, but can also be used as a permanent file host.
## Features ## Features
- One click uploading, no registration required - One click uploading, no registration required.
- A minimal, modern web interface - A minimal, modern web interface.
- Drag & Drop & Paste supported - Drag & Drop & Paste supported.
- Upload API with multiple response choices - Upload API with multiple response choices:
- JSON - JSON
- HTML - HTML
- Text - Text
- CSV - CSV
- Supports [ShareX](https://getsharex.com/) and other screenshot tools - Supports [ShareX](https://getsharex.com/) and other screenshot tools.
- File extension & MIME black/whitelisting.
- Upload rate limiting.
- File (hash) blacklisting.
- Run bare or as a container.
### Demo ### Demo
@ -26,7 +30,7 @@ See the real world site at [uguu.se](https://uguu.se).
## Requirements ## Requirements
Tested and working with Nginx + PHP-8.0/8.1 + SQLite/MySQL. Tested and working with Nginx + PHP-8.1 + SQLite/MySQL.
Node is used to compile Uguu, after that it runs on PHP. Node is used to compile Uguu, after that it runs on PHP.
@ -51,6 +55,13 @@ coding style guides. We use ESLint and PHPCS tools to enforce these standards.
You can also help by sending us feature requests or writing documentation and You can also help by sending us feature requests or writing documentation and
tests. tests.
## Upcoming Features
* PostgreSQL Support
* S3 Bucket Support
* Azure File Storage Support
* Temporal/RR Support
## Credits ## Credits
Uguu is based on [Pomf](http://github.com/pomf/pomf) which was written by Emma Lejack & Go Johansson (nekunekus) and with help from the open source community. Uguu is based on [Pomf](http://github.com/pomf/pomf) which was written by Emma Lejack & Go Johansson (nekunekus) and with help from the open source community.

View File

@ -1,31 +0,0 @@
{
"name": "pomf/uguu",
"description": "Uguu is a simple lightweight temporary file host with support for drop, paste, click and API uploading.",
"type": "library",
"license": "GPL-3.0",
"autoload": {
"psr-4": {
"Pomf\\Uguu\\": "src/",
"Pomf\\Uguu\\Classes\\": "src/Classes"
}
},
"authors": [
{
"name": "Go Johansson (neku)",
"email": "neku@pomf.se"
}
],
"minimum-stability": "stable",
"require": {
"ext-fileinfo": "*",
"ext-pdo": "*"
},
"require-dev": {
"phpunit/phpunit": "@stable",
"squizlabs/php_codesniffer": "@stable",
"phpmd/phpmd": "@stable",
"friendsofphp/php-cs-fixer": "@stable",
"phpstan/phpstan": "@stable",
"vimeo/psalm": "@stable"
}
}

5116
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +1,36 @@
FROM php:8.1-alpine FROM --platform=linux/amd64 debian:bullseye-slim
ARG VERSION # Install needed software
RUN apt-get update
RUN apt-get install -y lsb-release ca-certificates apt-transport-https software-properties-common gnupg2 curl cron socat
RUN echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" | tee /etc/apt/sources.list.d/sury-php.list
RUN curl -fsSL https://packages.sury.org/php/apt.gpg| gpg --dearmor -o /etc/apt/trusted.gpg.d/sury-keyring.gpg
RUN apt-get update
RUN apt-get upgrade -y
RUN curl -o nodejssetup.sh https://deb.nodesource.com/setup_19.x
RUN chmod a+x nodejssetup.sh
RUN ./nodejssetup.sh
RUN apt-get install -y nodejs gcc g++ make
RUN apt-get install -y build-essential nginx-full php8.1-fpm php8.1 sqlite3 php8.1-sqlite3 \
php8.1-curl php8.1-cli php8.1-lz4 \
php8.1-mcrypt php8.1-mysql php8.1-xdebug php8.1-zip \
php8.1-common php8.1-readline php8.1-bcmath php8.1-common php8.1-xml
# Set ENV values for configuration
ARG DOMAIN
ENV DOMAIN=$DOMAIN
RUN apk add --no-cache $PHPIZE_DEPS \ ARG FILE_DOMAIN
php-fpm zlib-dev libzip-dev libgomp; \ ENV FILE_DOMAIN=$FILE_DOMAIN
docker-php-ext-install intl pcntl gd exif zip pdo mysql dom; \
apk del $PHPIZE_DEPS; \
rm -rf /tmp/pear;
# Install the needed software ARG CONTACT_EMAIL
RUN apk add --no-cache sqlite nodejs git npm bash build-base supervisor curl wget nginx ENV CONTACT_EMAIL=$CONTACT_EMAIL
#php8-fileinfo php8-pdo php8-tokenizer php8-dom php8-iconv php8-xml php8-simplexml \
#php8-sqlite3 php8-opcache php8-mbstring php8-phar php8-openssl
# Create the www-data user and group ARG MAX_SIZE
#RUN set -x ; \ ENV MAX_SIZE=$MAX_SIZE
# addgroup -g 82 -S www-data ; \
# adduser -u 82 -D -S -G www-data www-data && exit 0 ; exit 1
# Copy supervisor conf file ARG EXPIRE_TIME
COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf ENV EXPIRE_TIME=$EXPIRE_TIME
# Set default workdir # Set default workdir
WORKDIR /var/www/ WORKDIR /var/www/
@ -37,45 +47,50 @@ RUN mkdir /var/www/files && \
mkdir /var/www/db mkdir /var/www/db
# Create the Sqlite DB # Create the Sqlite DB
RUN sqlite3 /var/www/db/uguu.sq3 -init /var/www/uguu/src/static/dbSchemas/sqlite_schema.sql && \ RUN sqlite3 /var/www/db/uguuDB.sq3 -init /var/www/uguu/src/static/dbSchemas/sqlite_schema.sql "" && \
chown -R www-data:www-data /var/www && \ chown -R www-data:www-data /var/www && \
chmod -R 775 /var/www/ chmod -R 775 /var/www/
# Add scripts to cron
RUN echo "0,30 * * * * bash /var/www/uguu/src/static/scripts/checkfiles.sh" >> /var/spool/cron/crontabs/www-data && \
echo "0,30 * * * * bash /var/www/uguu/src/static/scripts/checkdb.sh" >> /var/spool/cron/crontabs/www-data
# Fix script paths # Fix script paths
RUN chmod a+x /var/www/uguu/src/static/scripts/checkdb.sh && \ RUN chmod a+x /var/www/uguu/src/static/scripts/checkdb.sh && \
chmod a+x /var/www/uguu/src/static/scripts/checkfiles.sh && \ chmod a+x /var/www/uguu/src/static/scripts/checkfiles.sh && \
sed -i 's#/path/to/files/#/var/www/uguu/files/#g' /var/www/uguu/src/static/scripts/checkfiles.sh && \ sed -i 's#/path/to/files/#/var/www/uguu/files/#g' /var/www/uguu/src/static/scripts/checkfiles.sh && \
sed -i 's#/path/to/db/uguu.sq3#/var/www/db/uguu.sq3#g' /var/www/uguu/src/static/scripts/checkdb.sh sed -i 's#/path/to/db/uguu.sq3#/var/www/db/uguu.sq3#g' /var/www/uguu/src/static/scripts/checkdb.sh
# Add scripts to cron
RUN echo "0,30 * * * * bash /var/www/uguu/src/static/scripts/checkfiles.sh" >> /var/spool/cron/crontabs/www-data && \
echo "0,30 * * * * bash /var/www/uguu/src/static/scripts/checkdb.sh" >> /var/spool/cron/crontabs/www-data
# Copy Nginx Server conf # Copy Nginx Server conf
COPY docker/nginx/uguu.conf /etc/nginx/http.d/ COPY docker/nginx/uguu.conf /etc/nginx/sites-enabled/uguu.conf
COPY docker/nginx/nginx.conf /etc/nginx/nginx.conf
# Copy SSL certs # Modify expire time
COPY docker/ssl /etc/ssl RUN sed -i "s#XXX#${EXPIRE_TIME}#g" /var/www/uguu/src/static/scripts/checkfiles.sh
RUN sed -i "s#XXX#${EXPIRE_TIME}#g" /var/www/uguu/src/static/scripts/checkdb.sh
# Copy PHP config # Modify nginx values
COPY docker/php/php.ini /etc/php8/ RUN sed -i "s#XMAINDOMAINX#${DOMAIN}#g" /etc/nginx/sites-enabled/uguu.conf
COPY docker/php/www.conf /etc/php8/php-fpm.d/ RUN sed -i "s#XFILESDOMAINX#${FILE_DOMAIN}#g" /etc/nginx/sites-enabled/uguu.conf
RUN mkdir /var/run/php RUN sed -i "s#client_max_body_size 128M#client_max_body_size ${MAX_SIZE}M#g" /etc/nginx/nginx.conf
# Modify php-fpm values
RUN sed -i "s#post_max_size = 8M#post_max_size = ${MAX_SIZE}M#g" /etc/php/8.1/fpm/php.ini
RUN sed -i "s#upload_max_filesize = 2M#upload_max_filesize = ${MAX_SIZE}M#g" /etc/php/8.1/fpm/php.ini
# Copy Uguu config # Copy Uguu config
COPY src/config.json /var/www/uguu/config.json COPY src/config.json /var/www/uguu/config.json
# Give permissions to www-data
RUN chown -R www-data:www-data /run /var/lib/nginx /var/log/nginx /etc/nginx /etc/php8 /var/log/php8 /var/run/php
# Change user to www-data
USER www-data
# Expose port 80 from the container # Expose port 80 from the container
EXPOSE 80 EXPOSE 80
# Expose port 443 from the container # Expose port 443 from the container
EXPOSE 443 EXPOSE 443
# Install acme.sh
RUN curl -o acmeinstall.sh https://get.acme.sh
RUN chmod a+x acmeinstall.sh
RUN ./acmeinstall.sh
# Load entrypoint # Load entrypoint
ENTRYPOINT [ "bash", "/var/www/docker-entrypoint.sh" ] ENTRYPOINT [ "bash", "/var/www/docker-entrypoint.sh" ]

View File

@ -1,11 +1,12 @@
#!/bin/bash #!/bin/bash
cd /var/www/uguu/ cd /var/www/uguu || exit
npm install
make make
make install make install
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" service nginx stop
php -r "if (hash_file('sha384', 'composer-setup.php') === '55ce33d7678c5a611085589f1f3ddf8b3c52d662cd01d4ba75c0ee0459970c2200a51f492d557530c71c15d8dba01eae') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" rm /etc/nginx/sites-enabled/default
php composer-setup.php /root/.acme.sh/acme.sh --set-default-ca --server letsencrypt
php -r "unlink('composer-setup.php');" /root/.acme.sh/acme.sh --issue --standalone -d "$DOMAIN" -d "$FILE_DOMAIN"
php composer.phar install service nginx start
php composer.phar update service php8.1-fpm start
/usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf tail -f /dev/null

62
docker/nginx/nginx.conf Normal file
View File

@ -0,0 +1,62 @@
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024;
multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
client_max_body_size 128M;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log off;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}

View File

@ -1,24 +1,23 @@
server{ server {
listen 443 ssl http2;
server_name XMAINDOMAINX;
listen 80; ssl on;
server_name 192.168.240.9; ssl_certificate /root/.acme.sh/XMAINDOMAINX/fullchain.cer;
#ssl on; ssl_certificate_key /root/.acme.sh/XMAINDOMAINX/XMAINDOMAINX.key;
#ssl_certificate /etc/nginx/ssl/fullchain.pem; ssl_protocols TLSv1.2 TLSv1.3;
#ssl_certificate_key /etc/nginx/ssl/privkey.pem; ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
#ssl_protocols TLSv1.2 TLSv1.3; ssl_ecdh_curve secp384r1;
#ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
#ssl_ecdh_curve secp384r1;
root /var/www/uguu/dist/; root /var/www/uguu/dist/public/;
autoindex on; autoindex off;
access_log on; access_log off;
index index.html index.php; index index.html;
location ~* \.(css|js|jpg|jpeg|gif|png|ico|xml|eot|woff|woff2|ttf|svg|otf|x-icon|avif|webp|apng)$ { location ~* \.(css|js|jpg|jpeg|gif|png|ico|xml|eot|woff|woff2|ttf|svg|otf|x-icon|avif|webp|apng)$ {
expires 30d; expires 30d;
} }
client_max_body_size 128M;
gzip on; gzip on;
gzip_min_length 1000; gzip_min_length 1000;
gzip_comp_level 6; gzip_comp_level 6;
@ -26,7 +25,7 @@ server{
gzip_types text/css text/js text/javascript application/javascript application/x-javascript; gzip_types text/css text/js text/javascript application/javascript application/x-javascript;
location ~* \.php$ { location ~* \.php$ {
fastcgi_pass unix:/var/run/php/php8.0-fpm.sock; fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_intercept_errors on; fastcgi_intercept_errors on;
fastcgi_index index.php; fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(.*)$; fastcgi_split_path_info ^(.+\.php)(.*)$;
@ -34,3 +33,32 @@ server{
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
} }
} }
server {
listen 443 ssl;
server_name XFILESDOMAINX;
ssl on;
ssl_certificate /root/.acme.sh/XMAINDOMAINX/fullchain.cer;
ssl_certificate_key /root/.acme.sh/XMAINDOMAINX/XMAINDOMAINX.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
ssl_ecdh_curve secp384r1;
root /var/www/files/;
autoindex off;
access_log off;
index index.html;
}
server {
listen 80;
server_name XMAINDOMAINX;
return 301 https://XMAINDOMAINX$request_uri;
}
server {
listen 80;
server_name XFILESDOMAINX;
return 301 https://XFILESDOMAINX$request_uri;
}

View File

View File

@ -1,32 +0,0 @@
[supervisord]
nodaemon=true
logfile=/dev/null
logfile_maxbytes=0
pidfile=/run/supervisord.pid
[program:php-fpm]
command=php-fpm8 -F
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=false
startretries=0
[program:nginx]
command=nginx -g 'daemon off;'
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=false
startretries=0
[program:crond]
command=crond -b
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=false
startretries=0

View File

@ -1,8 +0,0 @@
#!/bin/bash
echo ">>> BUILDING UGUU CONTAINER <<<"
make UGUU_RELEASE_VER="$(cat package.json | grep version | cut -d '"' -f4)" DOCKER_TAG="$(cat package.json | grep version | cut -d '"' -f4)" build-image
echo ">>> DONE! <<<"
echo ">>> Starting Uguu container! <<<"
make DOCKER_TAG="$(cat package.json | grep version | cut -d '"' -f4)" CONTAINER_NAME=uguu run-container
echo ">>> DONE! <<<"

View File

@ -1,2 +0,0 @@
#!/bin/bash
make DOCKER_TAG="$(cat package.json | grep version | cut -d '"' -f4)" CONTAINER_NAME=uguu purge-container

View File

@ -1,6 +1,6 @@
{ {
"name": "uguu", "name": "uguu",
"version": "1.6.0", "version": "1.6.7",
"description": "Uguu is a simple lightweight temporary file host with support for drop, paste, click and API uploading.", "description": "Uguu is a simple lightweight temporary file host with support for drop, paste, click and API uploading.",
"homepage": "https://uguu.se", "homepage": "https://uguu.se",
"repository": { "repository": {
@ -16,11 +16,10 @@
"bugs": { "bugs": {
"url": "https://github.com/nokonoko/uguu/issues" "url": "https://github.com/nokonoko/uguu/issues"
}, },
"devDependencies" : { "devDependencies": {
"minify": "^9.1.0",
"ejs": "^3.1.8", "ejs": "^3.1.8",
"minify-all-cli": "^1.0.13", "node-jq": "^2.3.4",
"node-jq": "^2.3.3",
"npx": "^10.2.2",
"imagemin-cli": "^7.0.0", "imagemin-cli": "^7.0.0",
"imagemin-pngquant": "^9.0.2" "imagemin-pngquant": "^9.0.2"
} }

View File

@ -1,15 +0,0 @@
<?xml version="1.0"?>
<psalm
errorLevel="3"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="." />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
</psalm>

View File

@ -1,6 +1,5 @@
<?php <?php
/**
/**
* Uguu * Uguu
* *
* @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se> * @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se>
@ -19,18 +18,20 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
namespace Pomf\Uguu\Classes; namespace Pomf\Uguu\Classes;
use Exception; use Exception;
use PDO; use PDO;
class Connector extends Database class Connector extends Database
{ {
public PDO $DB; public PDO $DB;
public array $CONFIG; public array $CONFIG;
/** /**
* @throws Exception * Reads the config.json file and populates the CONFIG property with the settings
*
* @throws \Exception
*/ */
public function __construct() public function __construct()
{ {
@ -40,16 +41,19 @@ class Connector extends Database
try { try {
$this->CONFIG = json_decode( $this->CONFIG = json_decode(
file_get_contents(__DIR__ . '/../config.json'), file_get_contents(__DIR__ . '/../config.json'),
true true,
); );
$this->assemble(); $this->assemble();
} catch (Exception) { }
throw new Exception('Cant populate settings.', 500); catch (Exception $e) {
throw new Exception($e->getMessage(), 500);
} }
} }
/** /**
* @throws Exception * > Tries to connect to the database
*
* @throws \Exception
*/ */
public function assemble() public function assemble()
{ {
@ -59,8 +63,9 @@ class Connector extends Database
$this->CONFIG['DB_USER'], $this->CONFIG['DB_USER'],
$this->CONFIG['DB_PASS'] $this->CONFIG['DB_PASS']
); );
} catch (Exception) { }
catch (Exception) {
throw new Exception('Cant connect to DB.', 500); throw new Exception('Cant connect to DB.', 500);
} }
} }
} }

View File

@ -1,6 +1,5 @@
<?php <?php
/**
/**
* Uguu * Uguu
* *
* @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se> * @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se>
@ -19,13 +18,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
namespace Pomf\Uguu\Classes; namespace Pomf\Uguu\Classes;
class CuteGrills class CuteGrills
{ {
public array $GRILLS; public array $GRILLS;
public function showGrills(): void /**
* Loads the list of grills, then redirects to a random grill
*/
public function showGrills():void
{ {
$this->loadGrills(); $this->loadGrills();
if (!headers_sent()) { if (!headers_sent()) {
@ -33,13 +35,16 @@ class CuteGrills
'Location: /img/grills/' . 'Location: /img/grills/' .
$this->GRILLS[array_rand($this->GRILLS)], $this->GRILLS[array_rand($this->GRILLS)],
true, true,
303 303,
); );
} }
} }
public function loadGrills(): void /**
* Loads the images from the `img/grills/` directory into the `GRILLS` array
*/
public function loadGrills():void
{ {
$this->GRILLS = array_slice(scandir('img/grills/'), 2); $this->GRILLS = array_slice(scandir('img/grills/'), 2);
} }
} }

View File

@ -1,6 +1,6 @@
<?php <?php
/** /**
* Uguu * Uguu
* *
* @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se> * @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se>
@ -19,47 +19,68 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
namespace Pomf\Uguu\Classes; namespace Pomf\Uguu\Classes;
use Exception; use DateTimeZone;
use PDO; use Exception;
use PDO;
use DateTime;
class Database class Database
{ {
private PDO $DB; private PDO $DB;
public function setDB($DB): void /**
* Sets the value of the DB variable.
*
* @param $DB PDO The database connection.
*/
public function setDB(PDO $DB): void
{ {
$this->DB = $DB; $this->DB = $DB;
} }
/** /**
* @throws Exception * Checks if a file name exists in the database
*
* @param $name string The name of the file.
*
* @return bool The number of rows that match the query.
* @throws \Exception
*/ */
public function dbCheckNameExists($name): string public function dbCheckNameExists(string $name): bool
{ {
try { try {
$q = $this->DB->prepare('SELECT COUNT(filename) FROM files WHERE filename = (:name)'); $q = $this->DB->prepare('SELECT * FROM files WHERE EXISTS
(SELECT filename FROM files WHERE filename = (:name)) LIMIT 1');
$q->bindValue(':name', $name); $q->bindValue(':name', $name);
$q->execute(); $q->execute();
return $q->fetchColumn(); $result = $q->fetch();
if ($result) {
return true;
}
return false;
} catch (Exception) { } catch (Exception) {
throw new Exception('Cant check if name exists in DB.', 500); throw new Exception('Cant check if name exists in DB.', 500);
} }
} }
/** /**
* @throws Exception * Checks if the file is blacklisted
*
* @param $FILE_INFO array An array containing the following:
*
* @throws \Exception
*/ */
public function checkFileBlacklist($FILE_INFO): void public function checkFileBlacklist(array $FILE_INFO): void
{ {
try { try {
$q = $this->DB->prepare('SELECT hash, COUNT(*) AS count FROM blacklist WHERE hash = (:hash)'); $q = $this->DB->prepare('SELECT * FROM blacklist WHERE EXISTS
(SELECT hash FROM blacklist WHERE hash = (:hash)) LIMIT 1');
$q->bindValue(':hash', $FILE_INFO['SHA1']); $q->bindValue(':hash', $FILE_INFO['SHA1']);
$q->execute(); $q->execute();
$result = $q->fetch(); $result = $q->fetch();
if ($result['count'] > 0) { if ($result) {
throw new Exception('File blacklisted!', 415); throw new Exception('File blacklisted!', 415);
} }
} catch (Exception) { } catch (Exception) {
@ -68,25 +89,31 @@ class Database
} }
/** /**
* @throws Exception * Checks if the file already exists in the database
*
* @param $hash string The hash of the file you want to check for.
*
* @throws \Exception
*/ */
public function antiDupe($hash): bool | array | string public function antiDupe(string $hash): array
{ {
if (!$this->CONFIG['ANTI_DUPE']) {
return true;
}
try { try {
$q = $this->DB->prepare( $q = $this->DB->prepare(
'SELECT filename, COUNT(*) AS count FROM files WHERE hash = (:hash)' 'SELECT * FROM files WHERE EXISTS
(SELECT filename FROM files WHERE hash = (:hash)) LIMIT 1',
); );
$q->bindValue(':hash', $hash); $q->bindValue(':hash', $hash);
$q->execute(); $q->execute();
$result = $q->fetch(); $result = $q->fetch();
if ($result['count'] > 0) { if ($result) {
return $result['filename']; return [
'result' => true,
'name' => $result['filename'],
];
} else { } else {
return true; return [
'result' => false
];
} }
} catch (Exception) { } catch (Exception) {
throw new Exception('Cant check for dupes in DB.', 500); throw new Exception('Cant check for dupes in DB.', 500);
@ -94,14 +121,19 @@ class Database
} }
/** /**
* @throws Exception * Inserts a new file into the database
*
* @param $FILE_INFO array
* @param $fingerPrintInfo array
*
* @throws \Exception
*/ */
public function newIntoDB($FILE_INFO, $fingerPrintInfo): void public function newIntoDB(array $FILE_INFO, array $fingerPrintInfo): void
{ {
try { try {
$q = $this->DB->prepare( $q = $this->DB->prepare(
'INSERT INTO files (hash, originalname, filename, size, date, ip)' . 'INSERT INTO files (hash, originalname, filename, size, date, ip)' .
'VALUES (:hash, :orig, :name, :size, :date, :ip)' 'VALUES (:hash, :orig, :name, :size, :date, :ip)',
); );
$q->bindValue(':hash', $FILE_INFO['SHA1']); $q->bindValue(':hash', $FILE_INFO['SHA1']);
$q->bindValue(':orig', $FILE_INFO['NAME']); $q->bindValue(':orig', $FILE_INFO['NAME']);
@ -115,71 +147,116 @@ class Database
} }
} }
/**
public function createRateLimit($fingerPrintInfo): void * Creates a new row in the database with the information provided
*
* @param $fingerPrintInfo array
*
* @throws \Exception
*/
public function createRateLimit(array $fingerPrintInfo): void
{ {
try {
$q = $this->DB->prepare( $q = $this->DB->prepare(
'INSERT INTO timestamp (iphash, files, time)' . 'INSERT INTO ratelimit (iphash, files, time)' .
'VALUES (:iphash, :files, :time)' 'VALUES (:iphash, :files, :time)',
); );
$q->bindValue(':iphash', $fingerPrintInfo['ip_hash']); $q->bindValue(':iphash', $fingerPrintInfo['ip_hash']);
$q->bindValue(':files', $fingerPrintInfo['files_amount']); $q->bindValue(':files', $fingerPrintInfo['files_amount']);
$q->bindValue(':time', $fingerPrintInfo['timestamp']); $q->bindValue(':time', $fingerPrintInfo['timestamp']);
$q->execute(); $q->execute();
} catch (Exception $e) {
throw new Exception(500, $e->getMessage());
}
} }
public function updateRateLimit($fCount, $iStamp, $fingerPrintInfo): void /**
* Update the rate limit table with the new file count and timestamp
*
* @param $fCount int The number of files uploaded by the user.
* @param $iStamp bool A boolean value that determines whether or not to update the timestamp.
* @param $fingerPrintInfo array An array containing the following keys:
*
* @throws \Exception
*/
public function updateRateLimit(int $fCount, bool $iStamp, array $fingerPrintInfo): void
{ {
try {
if ($iStamp) { if ($iStamp) {
$q = $this->DB->prepare( $q = $this->DB->prepare(
'UPDATE ratelimit SET files = (:files), time = (:time) WHERE iphash = (:iphash)' 'UPDATE ratelimit SET files = (:files), time = (:time) WHERE iphash = (:iphash)',
); );
$q->bindValue(':time', $fingerPrintInfo['timestamp']); $q->bindValue(':time', $fingerPrintInfo['timestamp']);
} else { } else {
$q = $this->DB->prepare( $q = $this->DB->prepare(
'UPDATE ratelimit SET files = (:files) WHERE iphash = (:iphash)' 'UPDATE ratelimit SET files = (:files) WHERE iphash = (:iphash)',
); );
} }
$q->bindValue(':files', $fCount); $q->bindValue(':files', $fCount);
$q->bindValue(':iphash', $fingerPrintInfo['ip_hash']); $q->bindValue(':iphash', $fingerPrintInfo['ip_hash']);
$q->execute(); $q->execute();
} catch (Exception $e) {
throw new Exception(500, $e->getMessage());
}
} }
/**
* @throws \Exception
*/
public function compareTime(int $timestamp, int $seconds_d): bool
{
$dateTime_end = new DateTime('now', new DateTimeZone('Europe/Stockholm'));
$dateTime_start = new DateTime();
$dateTime_start->setTimestamp($timestamp);
$diff = strtotime($dateTime_end->format('Y-m-d H:i:s')) - strtotime($dateTime_start->format('Y-m-d H:i:s'));
if ($diff > $seconds_d) {
return true;
}
return false;
}
/**
public function checkRateLimit($fingerPrintInfo): bool * Checks if the user has uploaded more than 100 files in the last minute, if so it returns true,
* if not it updates the database with the new file
* count and timestamp
*
* @param $fingerPrintInfo array An array containing the following:
*
* @return bool A boolean value.
* @throws \Exception
*/
public function checkRateLimit(array $fingerPrintInfo, int $rateTimeout, int $fileLimit): bool
{ {
$q = $this->DB->prepare( $q = $this->DB->prepare(
'SELECT files, time, iphash, COUNT(*) AS count FROM ratelimit WHERE iphash = (:iphash)' 'SELECT files, time, iphash, COUNT(*) AS count FROM ratelimit WHERE iphash = (:iphash)',
); );
$q->bindValue(':iphash', $fingerPrintInfo['ip_hash']); $q->bindValue(':iphash', $fingerPrintInfo['ip_hash']);
$q->execute(); $q->execute();
$result = $q->fetch(); $result = $q->fetch();
$nTime = $fingerPrintInfo['timestamp'] - (60); //If there is no other match a record does not exist, create one.
if (!$result['count'] > 0) {
switch (true) {
//If more then 100 files trigger rate-limit
case $result['files'] > 100:
return true;
//if timestamp is older than one minute, set new files count and timestamp
case $result['time'] < $nTime:
$this->updateRateLimit($fingerPrintInfo['files_amount'], true, $fingerPrintInfo);
break;
//if timestamp isn't older than one-minute update the files count
case $result['time'] > $nTime:
$this->updateRateLimit($fingerPrintInfo['files_amount'] + $result['files'], false, $fingerPrintInfo);
break;
//If there is no other match a record does not exist, create one
default:
$this->createRateLimit($fingerPrintInfo); $this->createRateLimit($fingerPrintInfo);
break; return false;
} }
// Apply rate-limit when file count reached and timeout not reached.
if ($result['files'] === $fileLimit and !$this->compareTime($result['time'], $rateTimeout)) {
return true;
}
// Update timestamp if timeout reached.
if ($this->compareTime($result['time'], $rateTimeout)) {
$this->updateRateLimit($fingerPrintInfo['files_amount'], true, $fingerPrintInfo);
return false;
}
// Add filecount, timeout not reached.
if ($result['files'] < $fileLimit and !$this->compareTime($result['time'], $rateTimeout)) {
$this->updateRateLimit($result['files'] + $fingerPrintInfo['files_amount'], false, $fingerPrintInfo);
return false;
}
return false; return false;
} }
} }

View File

@ -0,0 +1,29 @@
<?php
/**
* Uguu
*
* @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace Pomf\Uguu\Classes;
class GrillLoader extends CuteGrills
{
public function __construct()
{
$this->showGrills();
}
}

View File

@ -1,6 +1,6 @@
<?php <?php
/** /**
* Uguu * Uguu
* *
* @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se> * @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se>
@ -19,13 +19,19 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
namespace Pomf\Uguu\Classes; namespace Pomf\Uguu\Classes;
class Response class Response
{ {
public mixed $type; public string $type;
public function __construct($response_type = "json") /**
* Takes a string as an argument and sets the header to the appropriate content type
*
* @param $response_type string The type of response you want to return.
* Valid options are: csv, html, json, text.
*/
public function __construct(string $response_type)
{ {
switch ($response_type) { switch ($response_type) {
case 'csv': case 'csv':
@ -51,12 +57,17 @@ class Response
default: default:
header('Content-Type: application/json; charset=UTF-8'); header('Content-Type: application/json; charset=UTF-8');
$this->type = 'json'; $this->type = 'json';
$this->error(400, 'Invalid response type. Valid options are: csv, html, json, text.');
break; break;
} }
} }
public function error($code, $desc): void /**
* Returns a string based on the type of response requested
*
* @param $code mixed The HTTP status code to return.
* @param $desc string The description of the error.
*/
public function error(int $code, string $desc): void
{ {
$response = match ($this->type) { $response = match ($this->type) {
'csv' => $this->csvError($desc), 'csv' => $this->csvError($desc),
@ -68,17 +79,34 @@ class Response
echo $response; echo $response;
} }
private static function csvError($description): string /* Returning a string that contains the error message. */
private static function csvError(string $description): string
{ {
return '"error"' . "\r\n" . "\"$description\"" . "\r\n"; return '"error"' . "\r\n" . "\"$description\"" . "\r\n";
} }
private static function htmlError($code, $description): string /**
* Returns a string containing an HTML paragraph element with the error code and description
*
* @param $code int|string The error code.
* @param $description string The description of the error.
*
* @return string A string.
*/
private static function htmlError(int|string $code, string $description): string
{ {
return '<p>ERROR: (' . $code . ') ' . $description . '</p>'; return '<p>ERROR: (' . $code . ') ' . $description . '</p>';
} }
private static function jsonError($code, $description): bool|string /**
* Returns a JSON string with the error code and description
*
* @param $code int|string The error code.
* @param $description string The description of the error.
*
* @return bool|string A JSON string
*/
private static function jsonError(int|string $code, string $description): bool|string
{ {
return json_encode([ return json_encode([
'success' => false, 'success' => false,
@ -87,13 +115,28 @@ class Response
], JSON_PRETTY_PRINT); ], JSON_PRETTY_PRINT);
} }
/**
private static function textError($code, $description): string * Returns a string that contains the error code and description
*
* @param $code int|string The error code.
* @param $description string The description of the error.
*
* @return string A string with the error code and description.
*/
private static function textError(int|string $code, string $description): string
{ {
return 'ERROR: (' . $code . ') ' . $description; return 'ERROR: (' . $code . ') ' . $description;
} }
public function send($files): void /**
* "If the type is csv, then call the csvSuccess function,
* if the type is html, then call the htmlSuccess function, etc."
*
* The `match` keyword is a new feature in PHP 8. It's a lot like a switch statement, but it's more powerful
*
* @param $files array An array of file objects.
*/
public function send(array $files): void
{ {
$response = match ($this->type) { $response = match ($this->type) {
'csv' => $this->csvSuccess($files), 'csv' => $this->csvSuccess($files),
@ -101,12 +144,18 @@ class Response
'json' => $this->jsonSuccess($files), 'json' => $this->jsonSuccess($files),
'text' => $this->textSuccess($files), 'text' => $this->textSuccess($files),
}; };
http_response_code(200); // "200 OK". Success. http_response_code(200); // "200 OK". Success.
echo $response; echo $response;
} }
private static function csvSuccess($files): string /**
* Takes an array of files and returns a CSV string
*
* @param $files array An array of files that have been uploaded.
*
* @return string A string of the files in the array.
*/
private static function csvSuccess(array $files): string
{ {
$result = '"name","url","hash","size"' . "\r\n"; $result = '"name","url","hash","size"' . "\r\n";
foreach ($files as $file) { foreach ($files as $file) {
@ -115,22 +164,33 @@ class Response
'"' . $file['hash'] . '"' . ',' . '"' . $file['hash'] . '"' . ',' .
'"' . $file['size'] . '"' . "\r\n"; '"' . $file['size'] . '"' . "\r\n";
} }
return $result; return $result;
} }
private static function htmlSuccess($files): string /**
* Takes an array of files and returns a string of HTML links
*
* @param $files array An array of files to be uploaded.
*
* @return string the result of the foreach loop.
*/
private static function htmlSuccess(array $files): string
{ {
$result = ''; $result = '';
foreach ($files as $file) { foreach ($files as $file) {
$result .= '<a href="' . $file['url'] . '">' . $file['url'] . '</a><br>'; $result .= '<a href="' . $file['url'] . '">' . $file['url'] . '</a><br>';
} }
return $result; return $result;
} }
private static function jsonSuccess($files): bool|string /**
* Returns a JSON string that contains a success message and the files that were uploaded
*
* @param $files array The files to be uploaded.
*
* @return bool|string A JSON string
*/
private static function jsonSuccess(array $files): bool|string
{ {
return json_encode([ return json_encode([
'success' => true, 'success' => true,
@ -138,14 +198,19 @@ class Response
], JSON_PRETTY_PRINT); ], JSON_PRETTY_PRINT);
} }
private static function textSuccess($files): string /**
* Takes an array of files and returns a string of URLs
*
* @param $files array The files to be uploaded.
*
* @return string the url of the file.
*/
private static function textSuccess(array $files): string
{ {
$result = ''; $result = '';
foreach ($files as $file) { foreach ($files as $file) {
$result .= $file['url'] . "\n"; $result .= $file['url'] . "\n";
} }
return $result; return $result;
} }
} }

View File

@ -1,6 +1,6 @@
<?php <?php
/** /**
* Uguu * Uguu
* *
* @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se> * @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se>
@ -19,9 +19,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
namespace Pomf\Uguu\Classes; namespace Pomf\Uguu\Classes;
use Exception; use Exception;
class Upload extends Response class Upload extends Response
{ {
@ -29,11 +29,16 @@ class Upload extends Response
public array $fingerPrintInfo; public array $fingerPrintInfo;
private mixed $Connector; private mixed $Connector;
/** /**
* @throws Exception * Takes an array of files, and returns an array of arrays containing the file's temporary name,
* name, size, SHA1 hash, extension, and MIME type
*
* @param $files array The files array from the $_FILES superglobal.
*
* @return array An array of arrays.
* @throws \Exception
*/ */
public function reFiles($files): array public function reFiles(array $files): array
{ {
$this->Connector = new Connector(); $this->Connector = new Connector();
$this->Connector->setDB($this->Connector->DB); $this->Connector->setDB($this->Connector->DB);
@ -43,25 +48,64 @@ class Upload extends Response
$hash = sha1_file($file['tmp_name']); $hash = sha1_file($file['tmp_name']);
$this->FILE_INFO = [ $this->FILE_INFO = [
'TEMP_NAME' => $file['tmp_name'], 'TEMP_NAME' => $file['tmp_name'],
'NAME' => strip_tags($file['name']), 'NAME' => strip_tags($this->checkNameLength($file['name'])),
'SIZE' => $file['size'], 'SIZE' => $file['size'],
'SHA1' => $hash, 'SHA1' => $hash,
'EXTENSION' => $this->fileExtension($file), 'EXTENSION' => $this->fileExtension($file),
'MIME' => $this->fileMIME($file), 'MIME' => $this->fileMIME($file),
'NEW_NAME' => $this->generateName($this->fileExtension($file), $hash)
]; ];
if ($this->Connector->CONFIG['ANTI_DUPE']) {
$dupeResult = $this->Connector->antiDupe($hash);
if ($dupeResult['result']) {
$this->FILE_INFO['NEW_NAME'] = $dupeResult['name'];
}
}
if (!isset($this->FILE_INFO['NEW_NAME'])) {
$this->FILE_INFO['NEW_NAME'] = $this->generateName($this->FILE_INFO['EXTENSION']);
}
$result[] = [ $result[] = [
$this->FILE_INFO['TEMP_NAME'], $this->FILE_INFO['TEMP_NAME'],
$this->FILE_INFO['NAME'], $this->FILE_INFO['NAME'],
$this->FILE_INFO['SIZE'], $this->FILE_INFO['SIZE'],
$this->FILE_INFO['SHA1'], $this->FILE_INFO['SHA1'],
$this->FILE_INFO['EXTENSION'], $this->FILE_INFO['EXTENSION'],
$this->FILE_INFO['MIME'] $this->FILE_INFO['MIME'],
]; ];
} }
return $result; return $result;
} }
public function diverseArray($files): array
/**
* Takes an array of arrays and returns an array of arrays with the keys and values swapped
*
* @param $files array an array of arrays
*
* @return array ```
* array:2 [
* 0 => array:2 [
* 'TEMP_NAME' => 'example'
* 'NAME' => 'example'
* 'SIZE' => 'example'
* 'SHA1' => 'example'
* 'EXTENSION' => 'example'
* 'MIME' => 'example'
*
* ]
* 1 => array:2 [
* 'TEMP_NAME' => 'example'
* 'NAME' => 'example'
* 'SIZE' => 'example'
* 'SHA1' => 'example'
* 'EXTENSION' => 'example'
* 'MIME' => 'example'
* ]
* ]
* ```
*/
public function diverseArray(array $files): array
{ {
$result = []; $result = [];
foreach ($files as $key1 => $value1) { foreach ($files as $key1 => $value1) {
@ -73,88 +117,149 @@ class Upload extends Response
} }
/** /**
* @throws Exception * Takes a file, checks if it's blacklisted, moves it to the file storage, and then logs it to the database
*
* @return array An array containing the hash, name, url, and size of the file.
* @throws \Exception
*/ */
public function uploadFile(): array public function uploadFile(): array
{ {
switch (true) {
if ($this->Connector->CONFIG['RATE_LIMIT']) { case $this->Connector->CONFIG['RATE_LIMIT']:
$this->Connector->checkRateLimit($this->fingerPrintInfo); if (
$this->Connector->checkRateLimit(
$this->fingerPrintInfo,
(int) $this->Connector->CONFIG['RATE_LIMIT_TIMEOUT'],
(int) $this->Connector->CONFIG['RATE_LIMIT_FILES']
)
) {
throw new Exception('Rate limit, please wait ' . $this->Connector->CONFIG['RATE_LIMIT_TIMEOUT'] .
' seconds before uploading again.', 500);
} }
// Continue
if ($this->Connector->CONFIG['BLACKLIST_DB']) { case $this->Connector->CONFIG['BLACKLIST_DB']:
$this->Connector->checkFileBlacklist($this->FILE_INFO); $this->Connector->checkFileBlacklist($this->FILE_INFO);
} // Continue
case $this->Connector->CONFIG['FILTER_MODE'] && empty($this->FILE_INFO['EXTENSION']):
if ($this->Connector->CONFIG['FILTER_MODE'] && empty($this->FILE_INFO['EXTENSION'])) {
$this->checkMimeBlacklist(); $this->checkMimeBlacklist();
} // Continue
case $this->Connector->CONFIG['FILTER_MODE'] && !empty($this->FILE_INFO['EXTENSION']):
if ($this->Connector->CONFIG['FILTER_MODE'] && !empty($this->FILE_INFO['EXTENSION'])) {
$this->checkMimeBlacklist(); $this->checkMimeBlacklist();
$this->checkExtensionBlacklist(); $this->checkExtensionBlacklist();
// Continue
} }
if (!is_dir($this->Connector->CONFIG['FILES_ROOT'])) { if (!is_dir($this->Connector->CONFIG['FILES_ROOT'])) {
throw new Exception('File storage path not accessible.', 500); throw new Exception('File storage path not accessible.', 500);
} }
if ( if (
!move_uploaded_file($this->FILE_INFO['TEMP_NAME'], $this->Connector->CONFIG['FILES_ROOT'] . !move_uploaded_file(
$this->FILE_INFO['NEW_NAME']) $this->FILE_INFO['TEMP_NAME'],
$this->Connector->CONFIG['FILES_ROOT'] .
$this->FILE_INFO['NEW_NAME'],
)
) { ) {
throw new Exception('Failed to move file to destination', 500); throw new Exception('Failed to move file to destination', 500);
} }
if (!chmod($this->Connector->CONFIG['FILES_ROOT'] . $this->FILE_INFO['NEW_NAME'], 0644)) { if (!chmod($this->Connector->CONFIG['FILES_ROOT'] . $this->FILE_INFO['NEW_NAME'], 0644)) {
throw new Exception('Failed to change file permissions', 500); throw new Exception('Failed to change file permissions', 500);
} }
if (!$this->Connector->CONFIG['LOG_IP']) {
$this->fingerPrintInfo['ip'] = null;
}
$this->Connector->newIntoDB($this->FILE_INFO, $this->fingerPrintInfo); $this->Connector->newIntoDB($this->FILE_INFO, $this->fingerPrintInfo);
return [ return [
'hash' => $this->FILE_INFO['SHA1'], 'hash' => $this->FILE_INFO['SHA1'],
'name' => $this->FILE_INFO['NAME'], 'name' => $this->FILE_INFO['NAME'],
'url' => $this->Connector->CONFIG['FILES_URL'] . '/' . $this->FILE_INFO['NEW_NAME'], 'url' => 'https://' . $this->Connector->CONFIG['FILE_DOMAIN'] . '/' . $this->FILE_INFO['NEW_NAME'],
'size' => $this->FILE_INFO['SIZE'] 'size' => $this->FILE_INFO['SIZE'],
]; ];
} }
public function fingerPrint($files_amount): void
{
$this->fingerPrintInfo = [
'timestamp' => time(),
'useragent' => $_SERVER['HTTP_USER_AGENT'],
'ip' => $_SERVER['REMOTE_ADDR'],
'ip_hash' => hash('sha1', $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']),
'files_amount' => $files_amount
];
}
public function fileMIME($file): string
{
$finfo = finfo_open(FILEINFO_MIME_TYPE);
return finfo_file($finfo, $file['tmp_name']);
}
public function fileExtension($file): ?string
{
$extension = explode('.', $file['name']);
if (substr_count($file['name'], '.') > 0) {
return end($extension);
} else {
return null;
}
}
/** /**
* @throws Exception * Takes the amount of files that are being uploaded, and creates a fingerprint of the user's IP address,
* user agent, and the amount of files being
* uploaded
*
* @param $files_amount int The amount of files that are being uploaded.
*
* @throws \Exception
*/
public function fingerPrint(int $files_amount): void
{
if (!empty($_SERVER['HTTP_USER_AGENT'])) {
$USER_AGENT = filter_var($_SERVER['HTTP_USER_AGENT'], FILTER_SANITIZE_ENCODED);
$ip = null;
if ($this->Connector->CONFIG['LOG_IP']) {
$ip = $_SERVER['REMOTE_ADDR'];
}
$this->fingerPrintInfo = [
'timestamp' => time(),
'useragent' => $USER_AGENT,
'ip' => $ip,
'ip_hash' => hash('sha1', $_SERVER['REMOTE_ADDR'] . $USER_AGENT),
'files_amount' => $files_amount,
];
} else {
throw new Exception('Invalid user agent.', 500);
}
}
/**
* Returns the MIME type of a file
*
* @param $file array The file to be checked.
*
* @return string The MIME type of the file.
*/
public function fileMIME(array $file): string
{
$FILE_INFO = finfo_open(FILEINFO_MIME_TYPE);
return finfo_file($FILE_INFO, $file['tmp_name']);
}
/**
* It takes an array of strings, and returns the last two strings joined by a dot,
* unless the last two strings are in the array of strings in the
* `DOUBLE_DOTS_EXTENSIONS` config variable, in which case it returns the last string
*
* @param $extension array The extension of the file.
*
* @return string The last two elements of the array are joined together and returned.
*/
public function doubleDotExtension(array $extension): string
{
$doubleDotArray = array_slice($extension, -2, 2);
$doubleDot = strtolower(preg_replace('/[^a-zA-Z.]/', '', join('.', $doubleDotArray)));
if (in_array($doubleDot, $this->Connector->CONFIG['DOUBLE_DOTS_EXTENSIONS'])) {
return $doubleDot;
} else {
return end($extension);
}
}
/**
* Takes a file and returns the file extension
*
* @param $file array The file you want to get the extension from.
*
* @return string The file extension of the file.
*/
public function fileExtension(array $file): string
{
$extension = explode('.', $file['name']);
$dotCount = substr_count($file['name'], '.');
return match ($dotCount) {
0 => null,
1 => end($extension),
2 => $this->doubleDotExtension($extension),
default => end($extension)
};
}
/**
* > Check if the file's MIME type is in the blacklist
*
* @throws \Exception
*/ */
public function checkMimeBlacklist(): void public function checkMimeBlacklist(): void
{ {
@ -164,10 +269,9 @@ class Upload extends Response
} }
/** /**
* Check if file extension is blacklisted * > Check if the file extension is in the blacklist
* if it does throw an exception.
* *
* @throws Exception * @throws \Exception
*/ */
public function checkExtensionBlacklist(): void public function checkExtensionBlacklist(): void
{ {
@ -176,31 +280,38 @@ class Upload extends Response
} }
} }
public function checkNameLength(string $fileName): string {
if (strlen($fileName) > 250) {
return substr($fileName, 0, 250);
} else {
return $fileName;
}
}
/** /**
* @throws Exception * Generates a random string of characters, checks if it exists in the database,
* and if it does, it generates another one
*
* @param $extension string The file extension.
*
* @return string A string
* @throws \Exception
*/ */
public function generateName($extension, $hash): string public function generateName(string $extension): string
{ {
$a = $this->Connector->antiDupe($hash);
if ($a === true) {
do { do {
if ($this->Connector->CONFIG['FILES_RETRIES'] === 0) { if ($this->Connector->CONFIG['FILES_RETRIES'] === 0) {
throw new Exception('Gave up trying to find an unused name!', 500); throw new Exception('Gave up trying to find an unused name!', 500);
} }
$NEW_NAME = ''; $NEW_NAME = '';
for ($i = 0; $i < $this->Connector->CONFIG['NAME_LENGTH']; ++$i) { $count = strlen($this->Connector->CONFIG['ID_CHARSET']);
$NEW_NAME .= $this->Connector->CONFIG['ID_CHARSET'] while ($this->Connector->CONFIG['NAME_LENGTH']--) {
[mt_rand(0, strlen($this->Connector->CONFIG['ID_CHARSET']))]; $NEW_NAME .= $this->Connector->CONFIG['ID_CHARSET'][mt_rand(0, $count - 1)];
} }
if (!empty($extension)) {
if (!is_null($extension)) {
$NEW_NAME .= '.' . $extension; $NEW_NAME .= '.' . $extension;
} }
} while ($this->Connector->dbCheckNameExists($NEW_NAME) > 0); } while ($this->Connector->dbCheckNameExists($NEW_NAME));
return $NEW_NAME; return $NEW_NAME;
} else {
return $a;
}
} }
} }

View File

@ -1,30 +0,0 @@
<?php
/**
* Uguu
*
* @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace Pomf\Uguu;
class GrillLoader extends Classes\CuteGrills
{
public function __construct()
{
$this->showGrills();
}
}

View File

@ -1,53 +0,0 @@
<?php
/**
* Uguu
*
* @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace Pomf\Uguu;
use Exception;
use Pomf\Uguu\Classes\Response;
class UploadGateway extends Classes\Upload
{
/**
* @throws Exception
*/
public function handleFile($output, $files)
{
$type = $output ?? 'json';
$response = (new Response($type));
if (isset($_FILES['files'])) {
$files = $this->reFiles($files);
try {
$this->fingerPrint(count($files));
foreach ($files as $ignored) {
$res[] = $this->uploadFile();
}
if (isset($res)) {
$response->send($res);
}
} catch (Exception $e) {
$response->error($e->getCode(), $e->getMessage());
}
} else {
$response->error(400, 'No input file(s)');
}
}
}

27
src/composer.json Normal file
View File

@ -0,0 +1,27 @@
{
"name": "pomf/uguu",
"description": "Uguu is a simple lightweight temporary file host with support for drop, paste, click and API uploading.",
"type": "library",
"license": "GPL-3.0",
"autoload": {
"psr-4": {
"Pomf\\Uguu\\": "./",
"Pomf\\Uguu\\Classes\\": "./Classes"
}
},
"authors": [
{
"name": "Go Johansson (neku)",
"email": "neku@pomf.se"
}
],
"minimum-stability": "stable",
"require": {
"ext-fileinfo": "*",
"ext-pdo": "*"
},
"config": {
"optimize-autoloader": true,
"classmap-authoritative": true
}
}

View File

@ -3,17 +3,18 @@
"allowErrors": false "allowErrors": false
}, },
"dest": "dist", "dest": "dist",
"pkgVersion": "1.6.0", "pkgVersion": "1.6.7",
"pages": [ "pages": [
"index.ejs", "index.ejs",
"faq.ejs", "faq.ejs",
"tools.ejs" "tools.ejs"
], ],
"max_upload_size": 128, "max_upload_size": 128,
"expireTime": "48H", "expireTime": "48",
"siteName": "Uguu", "siteName": "Uguu",
"subTitle": "wahooo", "subTitle": "wahooo",
"siteUrl": "https://domain.com", "DOMAIN": "domain.com",
"FILE_DOMAIN": "files.domain.com",
"abuseContact": "abuse@example.com", "abuseContact": "abuse@example.com",
"infoContact": "info@example.com", "infoContact": "info@example.com",
"ServerCountryLocation": "Sweden", "ServerCountryLocation": "Sweden",
@ -26,7 +27,7 @@
"kofiUrl": "", "kofiUrl": "",
"malwareBanner": false, "malwareBanner": false,
"DB_MODE": "sqlite", "DB_MODE": "sqlite",
"DB_PATH": "/var/www/db/uguu.sq3", "DB_PATH": "/var/www/db/uguuDB.sq3",
"DB_USER": "NULL", "DB_USER": "NULL",
"DB_PASS": "NULL", "DB_PASS": "NULL",
"LOG_IP": false, "LOG_IP": false,
@ -34,11 +35,19 @@
"BLACKLIST_DB": true, "BLACKLIST_DB": true,
"FILTER_MODE": true, "FILTER_MODE": true,
"RATE_LIMIT": false, "RATE_LIMIT": false,
"RATE_LIMIT_TIMEOUT": 60,
"RATE_LIMIT_FILES": 100,
"FILES_ROOT": "/var/www/files/", "FILES_ROOT": "/var/www/files/",
"FILES_RETRIES": 15, "FILES_RETRIES": 15,
"FILES_URL": "https://files.domain.com",
"NAME_LENGTH": 8, "NAME_LENGTH": 8,
"ID_CHARSET": "abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ", "ID_CHARSET": "abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ",
"DOUBLE_DOTS_EXTENSIONS": [
"tar.bz2",
"tar.gz",
"min.css",
"min.js",
"conf.bak"
],
"BLOCKED_EXTENSIONS": [ "BLOCKED_EXTENSIONS": [
"exe", "exe",
"scr", "scr",

View File

@ -24,3 +24,12 @@ CREATE TABLE `blacklist` (
`time` int(15) DEFAULT NULL, `time` int(15) DEFAULT NULL,
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
); );
CREATE TABLE `ratelimit`
(
`id` int(20) unsigned NOT NULL auto_increment,
`iphash` char(40) DEFAULT NULL,
`files` int(15) default NULL,
`time` int(15) DEFAULT NULL,
PRIMARY KEY (`id`)
);

View File

@ -1,6 +1,6 @@
<?php <?php
/** /**
* Uguu * Uguu
* *
* @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se> * @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se>
@ -18,9 +18,8 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../vendor/autoload.php'; use Pomf\Uguu\Classes\GrillLoader;
use Pomf\Uguu\GrillLoader; new GrillLoader();
new GrillLoader();

View File

@ -1,6 +1,6 @@
<?php <?php
/** /**
* Uguu * Uguu
* *
* @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se> * @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se>
@ -19,15 +19,55 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
error_reporting(0); require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../vendor/autoload.php'; use Pomf\Uguu\Classes\Upload;
use Pomf\Uguu\Classes\Response;
use Pomf\Uguu\UploadGateway; /**
* It takes a string and an array as arguments, creates a new Upload object,
$Upload = new UploadGateway(); * calls the reFiles method on the Upload object, calls the fingerPrint method on
try { * the Upload object, calls the uploadFile method on the Upload object,
$Upload->handleFile($_GET['output'], $_FILES['files']); * calls the send method on the Upload object, and calls the error method on the
} catch (Exception $e) { * Upload object
throw new Exception($e->getMessage(), 500); *
* @param $outputFormat string The format of the output, json or xml
* @param $files array The file to be uploaded, which is an array.
*
* @throws \Exception
*/
function handleFile(string $outputFormat, array $files): void
{
$fCount = count($files['size']);
$upload = new Upload($outputFormat);
$files = $upload->reFiles($files);
try {
$upload->fingerPrint($fCount);
$res = [];
foreach ($files as $ignored) {
$res[] = $upload->uploadFile();
}
if (!empty($res)) {
$upload->send($res);
}
} catch (Exception $e) {
$upload->error(500, $e->getMessage());
}
}
$response = new Response('json');
if (!isset($_FILES['files']) or empty($_FILES['files'])) {
$response->error(400, 'No input file(s)');
}
if (isset($_GET['output']) and !empty($_GET['output'])) {
$resType = strtolower(preg_replace('/[^a-zA-Z]/', '', $_GET['output']));
} else {
$resType = 'json';
}
try {
handleFile($resType, $_FILES['files']);
} catch (Exception $e) {
$response->error($e->getCode(), $e->getMessage());
} }

View File

@ -1 +1 @@
sqlite3 /path/to/db/uguu.sq3 "DELETE FROM files WHERE date <= strftime('%s', datetime('now', '-1 day'));" sqlite3 /path/to/db/uguu.sq3 "DELETE FROM files WHERE date <= strftime('%s', datetime('now', '-XXX hours'));"

View File

@ -1,2 +1,3 @@
#! /bin/sh #!/bin/sh
find /path/to/files/ -mmin +1440 -exec rm -f {} \; hours=$((XXX*60))
find /path/to/files/ -mmin +$hours -exec rm -f {} \;

View File

@ -1,6 +1,6 @@
<h1><%= siteName %>~</h1> <h1><%= siteName %>~</h1>
<p class="lead"> <p class="lead">
Max upload size is <%= max_upload_size %>&nbsp;MiB & files expire after <%= expireTime %>, read the Max upload size is <%= max_upload_size %>&nbsp;MiB & files expire after <%= expireTime %>H, read the
<a href="faq.html"> <a href="faq.html">
<abbr title="Frequently asked questions">FAQ</abbr> <abbr title="Frequently asked questions">FAQ</abbr>
</a> </a>

View File

@ -8,9 +8,9 @@
<h3> <h3>
To upload using curl or make a tool you can post using: To upload using curl or make a tool you can post using:
<br> <br>
curl -i -F files[]=@yourfile.jpeg <%= siteUrl %>/upload.php (JSON Response) curl -i -F files[]=@yourfile.jpeg <%= DOMAIN %>/upload.php (JSON Response)
</h3> </h3>
<p>To upload using curl or make a tool you can post using: curl -i -F files[]=@yourfile.jpeg <%= siteUrl %>/upload.php (JSON Response)</p> <p>To upload using curl or make a tool you can post using: curl -i -F files[]=@yourfile.jpeg <%= DOMAIN %>/upload.php (JSON Response)</p>
<section> <section>
<h2>ShareX</h2> <h2>ShareX</h2>
<dl> <dl>