mirror of
https://github.com/HaschekSolutions/pictshare.git
synced 2025-11-12 03:06:22 +00:00
Compare commits
168 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de764c105c | ||
|
|
bc1eca4014 | ||
|
|
fc9a7d3072 | ||
|
|
67fba8e66b | ||
|
|
7f18a193d4 | ||
|
|
090f452585 | ||
|
|
819c25374c | ||
|
|
2388f68d5d | ||
|
|
3fe84a63a4 | ||
|
|
774c8e0112 | ||
|
|
63132e1cfb | ||
|
|
d349ee8fc6 | ||
|
|
5c3ee9e159 | ||
|
|
cea501d854 | ||
|
|
a1bc5b5fa5 | ||
|
|
d3d5d1c385 | ||
|
|
f000404d5a | ||
|
|
c777cd62ab | ||
|
|
06a524ca2f | ||
|
|
2712946287 | ||
|
|
fffcb13ac9 | ||
|
|
307243a5bf | ||
|
|
61acb5420b | ||
|
|
cccb80de03 | ||
|
|
d0deb6f6c9 | ||
|
|
e551ef2bb2 | ||
|
|
21aa1fbb7d | ||
|
|
4c1124da07 | ||
|
|
19d7cb060e | ||
|
|
c90a716d45 | ||
|
|
4fbf8b31a2 | ||
|
|
422c17eb65 | ||
|
|
70cdcf5dcf | ||
|
|
7b4b27098d | ||
|
|
2362af1e8c | ||
|
|
28f5677247 | ||
|
|
8c2702be96 | ||
|
|
452db23c57 | ||
|
|
c6baa6edfa | ||
|
|
74ccf9f626 | ||
|
|
56f89d5610 | ||
|
|
0ea4b3c160 | ||
|
|
93d78d44fe | ||
|
|
7dac978d16 | ||
|
|
312f18ade2 | ||
|
|
cb2d17411c | ||
|
|
9a4a20fb41 | ||
|
|
72394f17ba | ||
|
|
240c7359a8 | ||
|
|
9d8d5f3d36 | ||
|
|
25b39bbd86 | ||
|
|
4964967524 | ||
|
|
bfb43ab856 | ||
|
|
03464199ee | ||
|
|
96e73e2baf | ||
|
|
6ad94f2063 | ||
|
|
45b2e11729 | ||
|
|
c7f8f37c62 | ||
|
|
6f540ad27e | ||
|
|
ef1a1ecaff | ||
|
|
be5f601866 | ||
|
|
45d933ebf0 | ||
|
|
a145ca88cc | ||
|
|
cf0effd0e8 | ||
|
|
8ee26e6922 | ||
|
|
9d1a4db4d2 | ||
|
|
adb1c468cd | ||
|
|
d6f3b7b5df | ||
|
|
33b08f378d | ||
|
|
31d2712733 | ||
|
|
9200d3d93c | ||
|
|
b669e69511 | ||
|
|
f0cfdeb650 | ||
|
|
99d9542b80 | ||
|
|
b65186f77f | ||
|
|
3ee37fe3a2 | ||
|
|
5ecf80bce1 | ||
|
|
5a5c4a0334 | ||
|
|
7c7358af9c | ||
|
|
ebc4ebda03 | ||
|
|
a4dc4366ba | ||
|
|
ea40ffbc46 | ||
|
|
9a7fadb231 | ||
|
|
199f162fdf | ||
|
|
70c06cfa5c | ||
|
|
520099be96 | ||
|
|
5cf9b86868 | ||
|
|
03d4875e92 | ||
|
|
726c77effb | ||
|
|
68de83b46a | ||
|
|
77d4023f00 | ||
|
|
0119368376 | ||
|
|
166ff1da1b | ||
|
|
7727fc9ea4 | ||
|
|
8de53d1ea7 | ||
|
|
0250b6a577 | ||
|
|
e13f4816fb | ||
|
|
75784174fa | ||
|
|
6d504f3a48 | ||
|
|
3a6c987347 | ||
|
|
5861e73848 | ||
|
|
a2b7feb6f9 | ||
|
|
dce00906ec | ||
|
|
ceffa04b6e | ||
|
|
984246912c | ||
|
|
3a8ac33dd1 | ||
|
|
7e28024d32 | ||
|
|
b149fe88cb | ||
|
|
3a27592d58 | ||
|
|
80e210af86 | ||
|
|
220a3103fa | ||
|
|
6ec765024e | ||
|
|
6c556e921f | ||
|
|
3a90787091 | ||
|
|
c4087c1e84 | ||
|
|
76c0f6cec1 | ||
|
|
f01b685820 | ||
|
|
e022227617 | ||
|
|
2fc56d438d | ||
|
|
fb50f23e19 | ||
|
|
40c53173ca | ||
|
|
30c78ab7da | ||
|
|
5e59d7e80d | ||
|
|
dd0d274ab1 | ||
|
|
3c7e8bea7c | ||
|
|
1a46209bfe | ||
|
|
ad04aeb5b6 | ||
|
|
bbb0b5b111 | ||
|
|
a19d0a3d48 | ||
|
|
63fe1b3edd | ||
|
|
b9e42d0f31 | ||
|
|
021fbad811 | ||
|
|
6cab15f8e7 | ||
|
|
f863045fcf | ||
|
|
87fa55eae8 | ||
|
|
6806e4a15a | ||
|
|
82f97479ff | ||
|
|
75124102cb | ||
|
|
515e034d91 | ||
|
|
2a628e7d7a | ||
|
|
8e3cf866a6 | ||
|
|
6adb557693 | ||
|
|
c178f3af44 | ||
|
|
8da3573ffb | ||
|
|
5f5b836a22 | ||
|
|
17974088bc | ||
|
|
4d5d57df8a | ||
|
|
71df6a54c4 | ||
|
|
4a5e29e7c1 | ||
|
|
9d2bf6a931 | ||
|
|
66eebd1445 | ||
|
|
38f6b1c22f | ||
|
|
263f0b762b | ||
|
|
9f9449a58d | ||
|
|
1cb8e28be7 | ||
|
|
37e0ae26f8 | ||
|
|
d41e06efa0 | ||
|
|
772860e186 | ||
|
|
5cc690bbee | ||
|
|
b2a7c778d8 | ||
|
|
771ec3305e | ||
|
|
f4cea4ce08 | ||
|
|
66570c54d8 | ||
|
|
ee0000bf59 | ||
|
|
5d6b57cabc | ||
|
|
b3e777baa3 | ||
|
|
fb25657d2f | ||
|
|
3e6cfdfed6 |
7
.dockerignore
Normal file
7
.dockerignore
Normal file
@@ -0,0 +1,7 @@
|
||||
tmp/*
|
||||
inc/config.inc.php
|
||||
data/*
|
||||
lib/vendor
|
||||
bin
|
||||
.git
|
||||
.github
|
||||
59
.github/workflows/build-docker.yml
vendored
Normal file
59
.github/workflows/build-docker.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.*.*"
|
||||
pull_request:
|
||||
branches:
|
||||
- "master"
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
# list of Docker images to use as base name for tags
|
||||
images: |
|
||||
hascheksolutions/pictshare
|
||||
ghcr.io/hascheksolutions/pictshare
|
||||
# generate Docker tags based on the following events/attributes
|
||||
tags: |
|
||||
type=schedule
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to Docker Hub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Login to GHCR
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: docker/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1 +1,4 @@
|
||||
test.php
|
||||
test.php
|
||||
notice.txt
|
||||
lib/vendor
|
||||
.vscode
|
||||
3
CHANGELOG.md
Normal file
3
CHANGELOG.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# V2.0.0 (Nov 2023)
|
||||
- Pushed current release to version 2.0
|
||||
- Updated CI to include versions
|
||||
78
README.md
78
README.md
@@ -1,42 +1,78 @@
|
||||
# PictShare v2
|
||||
---
|
||||
[](https://github.com/HaschekSolutions/pictshare/blob/master/LICENSE)
|
||||
<p align="center">
|
||||
<a href="" rel="noopener">
|
||||
<img height=200px src="./css/imgs/logo/logo.svg" alt="PictShare logo">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
# This is the development branch for Version 2 do not use in production
|
||||
Test site: https://dev.pictshare.net/ (only sometimes on)
|
||||
<h1 align="center">PictShare</h1>
|
||||
|
||||
<h4 align="center">https://pictshare.net</h4>
|
||||
|
||||
<div align="center">
|
||||
|
||||
|
||||

|
||||
[](https://hub.docker.com/r/hascheksolutions/pictshare)
|
||||
]
|
||||
[](https://github.com/HaschekSolutions/pictshare/blob/master/LICENSE)
|
||||
[](https://hits.seeyoufarm.com)
|
||||
[](https://github.com/HaschekSolutions/pictshare)
|
||||
|
||||
#### Host your own `images` `gifs` `mp4s` `text bins` and stay in control
|
||||
|
||||
</div>
|
||||
|
||||
-----------------------------------------
|
||||
<center>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://www.pictshare.net/39928d8239.gif" alt="PictShare demo">
|
||||
</p>
|
||||
|
||||
Table of contents
|
||||
=================
|
||||
* [Quick Start](#quickstart)
|
||||
* [Features](#features)
|
||||
* [Installation](/rtfm/INSTALL.md)
|
||||
* [Configuration](/rtfm/CONFIG.md)
|
||||
* [Docker](/rtfm/DOCKER.md)
|
||||
* [API](/rtfm/API.md)
|
||||
* [Addons/Integration](/rtfm/INTEGRATIONS.md)
|
||||
* [Development status](#development-status)
|
||||
* [Addons and integration](/rtfm/INTEGRATIONS.md)
|
||||
* [Development roadmap](#development-roadmap)
|
||||
|
||||
---
|
||||
|
||||
## New Features in v2
|
||||
## Quickstart
|
||||
|
||||
```bash
|
||||
docker run -d -p 8080:80 --name=pictshare ghcr.io/hascheksolutions/pictshare
|
||||
```
|
||||
|
||||
Then open http://localhost:8080 in your browser
|
||||
|
||||
## New Features
|
||||
|
||||
- Generate identicons based on strings in the URL [example1](https://pictshare.net/identicon/example1) [example2](https://pictshare.net/identicon/example2)
|
||||
- Generate placeholder images by specifying the size in the URL. [example](https://pictshare.net/placeholder/555x250/color-white-blue)
|
||||
- Added support for external storage
|
||||
- [Encryption of files in external storage](/rtfm/ENCRYPTION.md)
|
||||
- Added text hosting (like pastebin)
|
||||
- Added URL shortening
|
||||
- Added WebP to images (and conversion from jpg,png to webp)
|
||||
- Added WebP to images (and automatic conversion from jpg, png to webp if the requesting browser supports it)
|
||||
- Massive code rework. Actually we designed it from the ground up to be more modular and easier to debug
|
||||
|
||||
## Breaking changes in v2
|
||||
|
||||
- [New API system](/rtfm/API.md). Only single file uploads now via /api/upload.php (POST var name is "file"). [read more..](/rtfm/API.md)
|
||||
- Data directory renamed from ```upload``` to ```data```
|
||||
- Backblaze support dropped for now because we didn't need it anymore as ALT_FOLDER is more flexible. If soneone needs it, it can easily be implemented via adding a new storage controller. We're happy to accept pull requests
|
||||
|
||||
# Features
|
||||
|
||||
- Selfhostable
|
||||
- [Simple upload API](/rtfm/API.md)
|
||||
- 100% file based - no database needed
|
||||
- [Scalable hosting](/rtfm/SCALING.md)
|
||||
- Many [Filters](/rtfm/IMAGEFILTERS.md) for images
|
||||
- GIF to MP4 conversion
|
||||
- JPG, PNG to WEBP conversion
|
||||
- MP4 resizing
|
||||
- PictShare removes all exif data so you can upload photos from your phone and all GPS tags and camera model info get wiped
|
||||
- Change and resize your uploads just by editing the URL
|
||||
- Change and resize your images and videos just by editing the URL
|
||||
- Duplicates don't take up space. If the exact same file is uploaded twice, the second upload will link to the first
|
||||
- Many [configuration options](/rtfm/CONFIG.md)
|
||||
- Full control over your data. Delete images with individual and global delete codes
|
||||
@@ -67,17 +103,17 @@ Read [here](/rtfm/CONFIG.md) what those options do
|
||||
- [x] MASTER_DELETE_CODE
|
||||
- [x] MASTER_DELETE_IP
|
||||
- [x] UPLOAD_FORM_LOCATION
|
||||
- [x] S3 Backend
|
||||
- [x] UPLOAD_CODE
|
||||
- [ ] UPLOAD_QUOTA
|
||||
- [ ] UPLOAD_CODE
|
||||
- [ ] LOW_PROFILE
|
||||
- [ ] IMAGE_CHANGE_CODE
|
||||
- [ ] MAX_RESIZED_IMAGES
|
||||
- [ ] ALLOW_BLOATING
|
||||
- [ ] BACKBLAZE
|
||||
|
||||
### Image hosting
|
||||
- [X] Resizing
|
||||
- [ ] Filters
|
||||
- [x] Resizing
|
||||
- [x] Filters
|
||||
- [x] Gif to mp4 conversion
|
||||
- [x] Upload of images
|
||||
|
||||
@@ -102,4 +138,4 @@ Read [here](/rtfm/CONFIG.md) what those options do
|
||||
|
||||
This is a [HASCHEK SOLUTIONS](https://haschek.solutions) project
|
||||
|
||||
[](https://haschek.solutions)
|
||||
[](https://haschek.solutions)
|
||||
|
||||
11
SECURITY.md
Normal file
11
SECURITY.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
PictShare uses rolling releases so the latest version is the only supported one.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Reports can be made as issues in this repo or in confidence via christian@haschek.at
|
||||
|
||||
Pull requests welcome
|
||||
@@ -3,6 +3,8 @@
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
define('ROOT', dirname(__FILE__).'/..');
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
//loading default settings if exist
|
||||
if(!file_exists(ROOT.DS.'inc'.DS.'config.inc.php'))
|
||||
exit('Rename /inc/example.config.inc.php to /inc/config.inc.php first!');
|
||||
@@ -10,13 +12,16 @@ include_once(ROOT.DS.'inc'.DS.'config.inc.php');
|
||||
|
||||
//loading core and controllers
|
||||
include_once(ROOT . DS . 'inc' . DS. 'core.php');
|
||||
require_once(ROOT . DS . 'content-controllers' . DS. 'image'. DS . 'image.controller.php');
|
||||
require_once(ROOT . DS . 'content-controllers' . DS. 'text'. DS . 'text.controller.php');
|
||||
require_once(ROOT . DS . 'content-controllers' . DS. 'url'. DS . 'url.controller.php');
|
||||
require_once(ROOT . DS . 'content-controllers' . DS. 'video'. DS . 'video.controller.php');
|
||||
//load external things if existing
|
||||
if(file_exists(ROOT.'/lib/vendor/autoload.php'))
|
||||
require ROOT.'/lib/vendor/autoload.php';
|
||||
loadAllContentControllers();
|
||||
|
||||
// check if client has permission to upload
|
||||
executeUploadPermission();
|
||||
|
||||
// check write permissions first
|
||||
if(!isFolderWritable(ROOT.DS.'data'))
|
||||
if(!isFolderWritable(getDataDir()))
|
||||
exit(json_encode(array('status'=>'err','reason'=>'Data directory not writable')));
|
||||
else if(!isFolderWritable(ROOT.DS.'tmp'))
|
||||
exit(json_encode(array('status'=>'err','reason'=>'Temp directory not writable')));
|
||||
@@ -33,14 +38,15 @@ if($_REQUEST['base64'])
|
||||
|
||||
base64ToFile($data, $tmpfile);
|
||||
|
||||
|
||||
//get the file type
|
||||
$type = getTypeOfFile($tmpfile);
|
||||
|
||||
//check for duplicates
|
||||
$sha1 = sha1_file($tmpfile);
|
||||
$ehash = sha1Exists($sha1);
|
||||
if($ehash && file_exists(ROOT.DS.'data'.DS.$ehash.DS.$ehash))
|
||||
exit(json_encode(array('status'=>'ok','hash'=>$ehash,'filetype'=>$type,'url'=>URL.$ehash)));
|
||||
if($ehash && file_exists(getDataDir().DS.$ehash.DS.$ehash))
|
||||
exit(json_encode(array('status'=>'ok','hash'=>$ehash,'filetype'=>$type,'url'=>getURL().$ehash)));
|
||||
|
||||
|
||||
|
||||
@@ -74,16 +80,10 @@ if($_REQUEST['base64'])
|
||||
if(getDeleteCodeOfHash($answer['hash']))
|
||||
{
|
||||
$answer['delete_code'] = getDeleteCodeOfHash($answer['hash']);
|
||||
$answer['delete_url'] = URL.'delete_'.getDeleteCodeOfHash($answer['hash']).'/'.$answer['hash'];
|
||||
$answer['delete_url'] = getURL().'delete_'.getDeleteCodeOfHash($answer['hash']).'/'.$answer['hash'];
|
||||
}
|
||||
|
||||
// Lets' check all storage controllers and tell them that a new file was uploaded
|
||||
$sc = getStorageControllers();
|
||||
foreach($sc as $contr)
|
||||
{
|
||||
if((new $contr())->isEnabled()===true)
|
||||
(new $contr())->pushFile($answer['hash']);
|
||||
}
|
||||
storageControllerUpload($answer['hash']);
|
||||
}
|
||||
|
||||
echo json_encode($answer);
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
define('ROOT', dirname(__FILE__).'/..');
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
//loading default settings if exist
|
||||
if(!file_exists(ROOT.DS.'inc'.DS.'config.inc.php'))
|
||||
exit('Rename /inc/example.config.inc.php to /inc/config.inc.php first!');
|
||||
@@ -10,13 +12,16 @@ include_once(ROOT.DS.'inc'.DS.'config.inc.php');
|
||||
|
||||
//loading core and controllers
|
||||
include_once(ROOT . DS . 'inc' . DS. 'core.php');
|
||||
require_once(ROOT . DS . 'content-controllers' . DS. 'image'. DS . 'image.controller.php');
|
||||
require_once(ROOT . DS . 'content-controllers' . DS. 'text'. DS . 'text.controller.php');
|
||||
require_once(ROOT . DS . 'content-controllers' . DS. 'url'. DS . 'url.controller.php');
|
||||
require_once(ROOT . DS . 'content-controllers' . DS. 'video'. DS . 'video.controller.php');
|
||||
//load external things if existing
|
||||
if(file_exists(ROOT.'/lib/vendor/autoload.php'))
|
||||
require ROOT.'/lib/vendor/autoload.php';
|
||||
loadAllContentControllers();
|
||||
|
||||
// check if client has permission to upload
|
||||
executeUploadPermission();
|
||||
|
||||
// check write permissions first
|
||||
if(!isFolderWritable(ROOT.DS.'data'))
|
||||
if(!isFolderWritable(getDataDir()))
|
||||
exit(json_encode(array('status'=>'err','reason'=>'Data directory not writable')));
|
||||
else if(!isFolderWritable(ROOT.DS.'tmp'))
|
||||
exit(json_encode(array('status'=>'err','reason'=>'Temp directory not writable')));
|
||||
@@ -25,6 +30,9 @@ $hash = sanatizeString(trim($_REQUEST['hash']))?sanatizeString(trim($_REQUEST['h
|
||||
|
||||
$url = trim($_REQUEST['url']);
|
||||
|
||||
if(checkURLForPrivateIPRange($url))
|
||||
exit(json_encode(array('status'=>'err','reason'=>'Private IP range')));
|
||||
|
||||
if(!$url || !startsWith($url, 'http'))
|
||||
exit(json_encode(array('status'=>'err','reason'=>'Invalid URL')));
|
||||
|
||||
@@ -34,15 +42,23 @@ else if(remote_filesize($url)*0.000001 > 20)
|
||||
|
||||
$name = basename($url);
|
||||
$tmpfile = ROOT.DS.'tmp'.DS.$name;
|
||||
file_put_contents($tmpfile,file_get_contents($url));
|
||||
|
||||
$context = stream_context_create(
|
||||
array(
|
||||
"http" => array(
|
||||
"follow_location" => false,
|
||||
),
|
||||
)
|
||||
);
|
||||
file_put_contents($tmpfile,file_get_contents($url, false, $context));
|
||||
|
||||
$type = getTypeOfFile($tmpfile);
|
||||
|
||||
//check for duplicates
|
||||
$sha1 = sha1_file($tmpfile);
|
||||
$ehash = sha1Exists($sha1);
|
||||
if($ehash && file_exists(ROOT.DS.'data'.DS.$ehash.DS.$ehash))
|
||||
exit(json_encode(array('status'=>'ok','hash'=>$ehash,'filetype'=>$type,'url'=>URL.$ehash)));
|
||||
if($ehash && file_exists(getDataDir().DS.$ehash.DS.$ehash))
|
||||
exit(json_encode(array('status'=>'ok','hash'=>$ehash,'filetype'=>$type,'url'=>getURL().$ehash)));
|
||||
|
||||
//cross check filetype for controllers
|
||||
//
|
||||
@@ -74,17 +90,11 @@ if($answer['hash'] && $answer['status']=='ok')
|
||||
if(getDeleteCodeOfHash($answer['hash']))
|
||||
{
|
||||
$answer['delete_code'] = getDeleteCodeOfHash($answer['hash']);
|
||||
$answer['delete_url'] = URL.'delete_'.getDeleteCodeOfHash($answer['hash']).'/'.$answer['hash'];
|
||||
$answer['delete_url'] = getURL().'delete_'.getDeleteCodeOfHash($answer['hash']).'/'.$answer['hash'];
|
||||
}
|
||||
|
||||
|
||||
// Lets' check all storage controllers and tell them that a new file was uploaded
|
||||
$sc = getStorageControllers();
|
||||
foreach($sc as $contr)
|
||||
{
|
||||
if((new $contr())->isEnabled()===true)
|
||||
(new $contr())->pushFile($answer['hash']);
|
||||
}
|
||||
storageControllerUpload($answer['hash']);
|
||||
}
|
||||
|
||||
if($answer['hash'] && $answer['status']=='ok')
|
||||
@@ -95,16 +105,10 @@ if($answer['hash'] && $answer['status']=='ok')
|
||||
if(getDeleteCodeOfHash($answer['hash']))
|
||||
{
|
||||
$answer['delete_code'] = getDeleteCodeOfHash($answer['hash']);
|
||||
$answer['delete_url'] = URL.'delete_'.getDeleteCodeOfHash($answer['hash']).'/'.$answer['hash'];
|
||||
$answer['delete_url'] = getURL().'delete_'.getDeleteCodeOfHash($answer['hash']).'/'.$answer['hash'];
|
||||
}
|
||||
|
||||
// Lets' check all storage controllers and tell them that a new file was uploaded
|
||||
$sc = getStorageControllers();
|
||||
foreach($sc as $contr)
|
||||
{
|
||||
if((new $contr())->isEnabled()===true)
|
||||
(new $contr())->pushFile($answer['hash']);
|
||||
}
|
||||
storageControllerUpload($answer['hash']);
|
||||
}
|
||||
|
||||
echo json_encode($answer);
|
||||
|
||||
53
api/info.php
Normal file
53
api/info.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
// basic path definitions
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
define('ROOT', dirname(__FILE__).'/..');
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
//loading default settings if exist
|
||||
if(!file_exists(ROOT.DS.'inc'.DS.'config.inc.php'))
|
||||
exit('Rename /inc/example.config.inc.php to /inc/config.inc.php first!');
|
||||
include_once(ROOT.DS.'inc'.DS.'config.inc.php');
|
||||
|
||||
//loading core and controllers
|
||||
include_once(ROOT . DS . 'inc' . DS. 'core.php');
|
||||
//load external things if existing
|
||||
if(file_exists(ROOT.'/lib/vendor/autoload.php'))
|
||||
require ROOT.'/lib/vendor/autoload.php';
|
||||
|
||||
if($_REQUEST['ip']=='pls') exit(getUserIP());
|
||||
|
||||
loadAllContentControllers();
|
||||
|
||||
$hash = $_REQUEST['hash'];
|
||||
|
||||
if(!isExistingHash($hash))
|
||||
{
|
||||
exit(json_encode(array('status'=>'err','reason'=>'File not found')));
|
||||
}
|
||||
else
|
||||
{
|
||||
$answer = getInfoAboutHash($hash);
|
||||
$answer['status'] = 'ok';
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
|
||||
|
||||
function getInfoAboutHash($hash)
|
||||
{
|
||||
$file = getDataDir().DS.$hash.DS.$hash;
|
||||
if(!file_exists($file))
|
||||
return array('status'=>'err','reason'=>'File not found');
|
||||
$size = filesize($file);
|
||||
$size_hr = renderSize($size);
|
||||
$content_type = exec("file -bi " . escapeshellarg($file));
|
||||
if($content_type && strpos($content_type,'/')!==false && strpos($content_type,';')!==false)
|
||||
{
|
||||
$type = $content_type;
|
||||
$c = explode(';',$type);
|
||||
$type = $c[0];
|
||||
}
|
||||
|
||||
return array('hash'=>$hash,'size_bytes'=>$size,'size_interpreted'=>$size_hr,'type'=>$type,'type_interpreted'=>getTypeOfFile($file));
|
||||
}
|
||||
@@ -10,10 +10,18 @@ include_once(ROOT.DS.'inc'.DS.'config.inc.php');
|
||||
|
||||
//loading core and controllers
|
||||
include_once(ROOT . DS . 'inc' . DS. 'core.php');
|
||||
require_once(ROOT . DS . 'content-controllers' . DS. 'text'. DS . 'text.controller.php');
|
||||
//load external things if existing
|
||||
if(file_exists(ROOT.'/lib/vendor/autoload.php'))
|
||||
require ROOT.'/lib/vendor/autoload.php';
|
||||
$controllers = loadAllContentControllers();
|
||||
if(!in_array('TextController',$controllers))
|
||||
exit(json_encode(array('status'=>'err','reason'=>'Text controller not enabled')));
|
||||
|
||||
// check if client has permission to upload
|
||||
executeUploadPermission();
|
||||
|
||||
// check write permissions first
|
||||
if(!isFolderWritable(ROOT.DS.'data'))
|
||||
if(!isFolderWritable(getDataDir()))
|
||||
exit(json_encode(array('status'=>'err','reason'=>'Data directory not writable')));
|
||||
else if(!isFolderWritable(ROOT.DS.'tmp'))
|
||||
exit(json_encode(array('status'=>'err','reason'=>'Temp directory not writable')));
|
||||
@@ -29,11 +37,11 @@ if($_REQUEST['api_paste_code'])
|
||||
$sha1 = sha1_file($tmpfile);
|
||||
$sha_hash = sha1Exists($sha1);
|
||||
if($sha_hash)
|
||||
exit(URL.$sha_hash);
|
||||
exit(getURL().$sha_hash);
|
||||
|
||||
$answer = (new TextController())->handleUpload($tmpfile,$hash);
|
||||
if($answer['hash'] && $answer['status']=='ok')
|
||||
addSha1($answer['hash'],$sha1);
|
||||
|
||||
echo URL.$hash;
|
||||
echo getURL().$hash;
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
// basic path definitions
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
define('ROOT', dirname(__FILE__).'/..');
|
||||
define('ROOT', dirname(__FILE__).DS.'..');
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
//loading default settings if exist
|
||||
if(!file_exists(ROOT.DS.'inc'.DS.'config.inc.php'))
|
||||
@@ -10,18 +12,24 @@ include_once(ROOT.DS.'inc'.DS.'config.inc.php');
|
||||
|
||||
//loading core and controllers
|
||||
include_once(ROOT . DS . 'inc' . DS. 'core.php');
|
||||
require_once(ROOT . DS . 'content-controllers' . DS. 'image'. DS . 'image.controller.php');
|
||||
require_once(ROOT . DS . 'content-controllers' . DS. 'text'. DS . 'text.controller.php');
|
||||
require_once(ROOT . DS . 'content-controllers' . DS. 'url'. DS . 'url.controller.php');
|
||||
require_once(ROOT . DS . 'content-controllers' . DS. 'video'. DS . 'video.controller.php');
|
||||
//load external things if existing
|
||||
if(file_exists(ROOT.'/lib/vendor/autoload.php'))
|
||||
require ROOT.'/lib/vendor/autoload.php';
|
||||
$allowedcontentcontrollers = loadAllContentControllers();
|
||||
|
||||
// check write permissions first
|
||||
if(!isFolderWritable(ROOT.DS.'data'))
|
||||
if(!isFolderWritable(getDataDir()))
|
||||
exit(json_encode(array('status'=>'err','reason'=>'Data directory not writable')));
|
||||
else if(!isFolderWritable(ROOT.DS.'tmp'))
|
||||
exit(json_encode(array('status'=>'err','reason'=>'Temp directory not writable')));
|
||||
|
||||
$hash = sanatizeString(trim($_REQUEST['hash']))?sanatizeString(trim($_REQUEST['hash'])):false;
|
||||
// check if client has permission to upload
|
||||
executeUploadPermission();
|
||||
|
||||
if(isset($_REQUEST['hash']))
|
||||
$hash = sanatizeString(trim($_REQUEST['hash']));
|
||||
else
|
||||
$hash = false;
|
||||
|
||||
// check for POST upload
|
||||
if ($_FILES['file']["error"] == UPLOAD_ERR_OK)
|
||||
@@ -32,16 +40,28 @@ if ($_FILES['file']["error"] == UPLOAD_ERR_OK)
|
||||
//check for duplicates
|
||||
$sha1 = sha1_file($_FILES['file']["tmp_name"]);
|
||||
$ehash = sha1Exists($sha1);
|
||||
if($ehash && file_exists(ROOT.DS.'data'.DS.$ehash.DS.$ehash))
|
||||
exit(json_encode(array('status'=>'ok','hash'=>$ehash,'filetype'=>$type,'url'=>URL.$ehash)));
|
||||
if($ehash && file_exists(getDataDir().DS.$ehash.DS.$ehash))
|
||||
exit(json_encode(array('status'=>'ok','hash'=>$ehash,'filetype'=>$type,'url'=>getURL().$ehash)));
|
||||
|
||||
//cross check filetype for controllers
|
||||
//
|
||||
//image?
|
||||
|
||||
foreach($allowedcontentcontrollers as $cc)
|
||||
{
|
||||
if(in_array($type,(new $cc)->getRegisteredExtensions()))
|
||||
{
|
||||
$answer = (new $cc())->handleUpload($_FILES['file']['tmp_name'],$hash);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if(in_array($type,(new ImageController)->getRegisteredExtensions()))
|
||||
{
|
||||
$answer = (new ImageController())->handleUpload($_FILES['file']['tmp_name'],$hash);
|
||||
}
|
||||
|
||||
//or, a text
|
||||
else if($type=='text')
|
||||
{
|
||||
@@ -52,7 +72,7 @@ if ($_FILES['file']["error"] == UPLOAD_ERR_OK)
|
||||
{
|
||||
$answer = (new VideoController())->handleUpload($_FILES['file']['tmp_name'],$hash);
|
||||
}
|
||||
|
||||
*/
|
||||
if(!$answer)
|
||||
$answer = array('status'=>'err','reason'=>'Unsupported filetype: '.$type,'filetype'=>$type);
|
||||
|
||||
@@ -65,17 +85,11 @@ if ($_FILES['file']["error"] == UPLOAD_ERR_OK)
|
||||
if(getDeleteCodeOfHash($answer['hash']))
|
||||
{
|
||||
$answer['delete_code'] = getDeleteCodeOfHash($answer['hash']);
|
||||
$answer['delete_url'] = URL.'delete_'.getDeleteCodeOfHash($answer['hash']).'/'.$answer['hash'];
|
||||
$answer['delete_url'] = getURL().'delete_'.getDeleteCodeOfHash($answer['hash']).'/'.$answer['hash'];
|
||||
}
|
||||
|
||||
|
||||
// Lets' check all storage controllers and tell them that a new file was uploaded
|
||||
$sc = getStorageControllers();
|
||||
foreach($sc as $contr)
|
||||
{
|
||||
if((new $contr())->isEnabled()===true)
|
||||
(new $contr())->pushFile($answer['hash']);
|
||||
}
|
||||
storageControllerUpload($answer['hash']);
|
||||
}
|
||||
|
||||
echo json_encode($answer);
|
||||
|
||||
BIN
bin/ffmpeg
BIN
bin/ffmpeg
Binary file not shown.
38
content-controllers/identicon/identicon.controller.php
Normal file
38
content-controllers/identicon/identicon.controller.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Bitverse\Identicon\Identicon;
|
||||
use Bitverse\Identicon\Color\Color;
|
||||
use Bitverse\Identicon\Generator\RingsGenerator;
|
||||
use Bitverse\Identicon\Preprocessor\MD5Preprocessor;
|
||||
|
||||
class IdenticonController implements ContentController
|
||||
{
|
||||
public const ctype = 'dynamic';
|
||||
|
||||
//returns all extensions registered by this type of content
|
||||
public function getRegisteredExtensions(){return array('identicon');}
|
||||
|
||||
public function handleHash($hash,$url)
|
||||
{
|
||||
unset($url[array_search('identicon',$url)]);
|
||||
$url = array_values($url);
|
||||
|
||||
|
||||
$generator = new RingsGenerator();
|
||||
$generator->setBackgroundColor(Color::parseHex('#EEEEEE'));
|
||||
|
||||
$identicon = new Identicon(new MD5Preprocessor(), $generator);
|
||||
|
||||
$icon = $identicon->getIcon($url[0]);
|
||||
|
||||
header('Content-type: image/svg+xml');
|
||||
echo $icon;
|
||||
}
|
||||
|
||||
public function handleUpload($tmpfile,$hash=false)
|
||||
{
|
||||
return array('status'=>'err','hash'=>$hash,'reason'=>'Cannot upload to Identicons');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
<?php
|
||||
|
||||
function getFilters()
|
||||
{
|
||||
return get_class_methods('Filter');
|
||||
}
|
||||
|
||||
class Filter {
|
||||
public function sepia($im,$val) {
|
||||
|
||||
imagefilter($im, IMG_FILTER_GRAYSCALE);
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 100, 50, 0);
|
||||
|
||||
return $im;
|
||||
}
|
||||
public function sepia2($im,$val) {
|
||||
imagefilter($im, IMG_FILTER_GRAYSCALE);
|
||||
imagefilter($im, IMG_FILTER_BRIGHTNESS, -10);
|
||||
imagefilter($im, IMG_FILTER_CONTRAST, -20);
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 60, 30, -15);
|
||||
return $im;
|
||||
}
|
||||
public function sharpen($im,$val) {
|
||||
|
||||
$gaussian = array(
|
||||
array(1.0, 1.0, 1.0),
|
||||
array(1.0, -7.0, 1.0),
|
||||
array(1.0, 1.0, 1.0)
|
||||
);
|
||||
imageconvolution($im, $gaussian, 1, 4);
|
||||
|
||||
return $im;
|
||||
}
|
||||
public function emboss($im,$val) {
|
||||
|
||||
$gaussian = array(
|
||||
array(-2.0, -1.0, 0.0),
|
||||
array(-1.0, 1.0, 1.0),
|
||||
array(0.0, 1.0, 2.0)
|
||||
);
|
||||
|
||||
imageconvolution($im, $gaussian, 1, 5);
|
||||
|
||||
return $im;
|
||||
}
|
||||
public function cool($im,$val) {
|
||||
|
||||
imagefilter($im, IMG_FILTER_MEAN_REMOVAL);
|
||||
imagefilter($im, IMG_FILTER_CONTRAST, -50);
|
||||
|
||||
return $im;
|
||||
}
|
||||
public function light($im,$val) {
|
||||
|
||||
imagefilter($im, IMG_FILTER_BRIGHTNESS, 10);
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 100, 50, 0, 10);
|
||||
|
||||
return $im;
|
||||
}
|
||||
public function aqua($im,$val) {
|
||||
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 0, 70, 0, 30);
|
||||
|
||||
return $im;
|
||||
}
|
||||
public function fuzzy($im,$val) {
|
||||
|
||||
$gaussian = array(
|
||||
array(1.0, 1.0, 1.0),
|
||||
array(1.0, 1.0, 1.0),
|
||||
array(1.0, 1.0, 1.0)
|
||||
);
|
||||
imageconvolution($im, $gaussian, 9, 20);
|
||||
|
||||
return $im;
|
||||
}
|
||||
public function boost($im,$val) {
|
||||
|
||||
imagefilter($im, IMG_FILTER_CONTRAST, -35);
|
||||
imagefilter($im, IMG_FILTER_BRIGHTNESS, 10);
|
||||
|
||||
return $im;
|
||||
}
|
||||
public function boost2($im,$val) {
|
||||
imagefilter( $im, IMG_FILTER_CONTRAST, -35);
|
||||
imagefilter( $im, IMG_FILTER_COLORIZE, 25, 25, 25);
|
||||
return $im;
|
||||
}
|
||||
public function gray($im,$val) {
|
||||
|
||||
imagefilter($im, IMG_FILTER_CONTRAST, -60);
|
||||
imagefilter($im, IMG_FILTER_GRAYSCALE);
|
||||
|
||||
return $im;
|
||||
}
|
||||
public function antique($im,$val) {
|
||||
imagefilter($im, IMG_FILTER_BRIGHTNESS, 0);
|
||||
imagefilter($im, IMG_FILTER_CONTRAST, -30);
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 75, 50, 25);
|
||||
return $im;
|
||||
}
|
||||
public function blackwhite($im,$val) {
|
||||
imagefilter($im, IMG_FILTER_GRAYSCALE);
|
||||
imagefilter($im, IMG_FILTER_BRIGHTNESS, 10);
|
||||
imagefilter($im, IMG_FILTER_CONTRAST, -20);
|
||||
return $im;
|
||||
}
|
||||
public function vintage($im,$val) {
|
||||
imagefilter($im, IMG_FILTER_BRIGHTNESS, 10);
|
||||
imagefilter($im, IMG_FILTER_GRAYSCALE);
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 40, 10, -15);
|
||||
return $im;
|
||||
}
|
||||
|
||||
public function concentrate($im,$val) {
|
||||
imagefilter($im, IMG_FILTER_GAUSSIAN_BLUR);
|
||||
imagefilter($im, IMG_FILTER_SMOOTH, -10);
|
||||
return $im;
|
||||
}
|
||||
|
||||
public function hermajesty($im,$val) {
|
||||
imagefilter($im, IMG_FILTER_BRIGHTNESS, -10);
|
||||
imagefilter($im, IMG_FILTER_CONTRAST, -5);
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 80, 0, 60);
|
||||
return $im;
|
||||
}
|
||||
public function everglow($im,$val) {
|
||||
imagefilter($im, IMG_FILTER_BRIGHTNESS, -30);
|
||||
imagefilter($im, IMG_FILTER_CONTRAST, -5);
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 30, 30, 0);
|
||||
return $im;
|
||||
}
|
||||
public function freshblue($im,$val) {
|
||||
imagefilter($im, IMG_FILTER_CONTRAST, -5);
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 20, 0, 80, 60);
|
||||
return $im;
|
||||
}
|
||||
public function tender($im,$val) {
|
||||
imagefilter($im, IMG_FILTER_CONTRAST, 5);
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 80, 20, 40, 50);
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 0, 40, 40, 100);
|
||||
imagefilter($im, IMG_FILTER_SELECTIVE_BLUR);
|
||||
return $im;
|
||||
}
|
||||
public function dream($im,$val) {
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 150, 0, 0, 50);
|
||||
imagefilter($im, IMG_FILTER_NEGATE);
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 0, 50, 0, 50);
|
||||
imagefilter($im, IMG_FILTER_NEGATE);
|
||||
imagefilter($im, IMG_FILTER_GAUSSIAN_BLUR);
|
||||
return $im;
|
||||
}
|
||||
public function frozen($im,$val) {
|
||||
imagefilter($im, IMG_FILTER_BRIGHTNESS, -15);
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 0, 0, 100, 50);
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 0, 0, 100, 50);
|
||||
imagefilter($im, IMG_FILTER_GAUSSIAN_BLUR);
|
||||
return $im;
|
||||
}
|
||||
public function forest($im,$val) {
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 0, 0, 150, 50);
|
||||
imagefilter($im, IMG_FILTER_NEGATE);
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 0, 0, 150, 50);
|
||||
imagefilter($im, IMG_FILTER_NEGATE);
|
||||
imagefilter($im, IMG_FILTER_SMOOTH, 10);
|
||||
return $im;
|
||||
}
|
||||
public function rain($im,$val) {
|
||||
imagefilter($im, IMG_FILTER_GAUSSIAN_BLUR);
|
||||
imagefilter($im, IMG_FILTER_MEAN_REMOVAL);
|
||||
imagefilter($im, IMG_FILTER_NEGATE);
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 0, 80, 50, 50);
|
||||
imagefilter($im, IMG_FILTER_NEGATE);
|
||||
imagefilter($im, IMG_FILTER_SMOOTH, 10);
|
||||
return $im;
|
||||
}
|
||||
public function orangepeel($im,$val) {
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 100, 20, -50, 20);
|
||||
imagefilter($im, IMG_FILTER_SMOOTH, 10);
|
||||
imagefilter($im, IMG_FILTER_BRIGHTNESS, -10);
|
||||
imagefilter($im, IMG_FILTER_CONTRAST, 10);
|
||||
imagegammacorrect($im, 1, 1.2 );
|
||||
return $im;
|
||||
}
|
||||
public function darken($im,$val) {
|
||||
imagefilter($im, IMG_FILTER_GRAYSCALE);
|
||||
imagefilter($im, IMG_FILTER_BRIGHTNESS, -50);
|
||||
return $im;
|
||||
}
|
||||
public function summer($im,$val) {
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 0, 150, 0, 50);
|
||||
imagefilter($im, IMG_FILTER_NEGATE);
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 25, 50, 0, 50);
|
||||
imagefilter($im, IMG_FILTER_NEGATE);
|
||||
return $im;
|
||||
}
|
||||
public function retro($im,$val) {
|
||||
imagefilter($im, IMG_FILTER_GRAYSCALE);
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 100, 25, 25, 50);
|
||||
return $im;
|
||||
}
|
||||
public function country($im,$val) {
|
||||
imagefilter($im, IMG_FILTER_BRIGHTNESS, -30);
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, 50, 50, 50, 50);
|
||||
imagegammacorrect($im, 1, 0.3);
|
||||
return $im;
|
||||
}
|
||||
public function washed($im,$val) {
|
||||
imagefilter($im, IMG_FILTER_BRIGHTNESS, 30);
|
||||
imagefilter($im, IMG_FILTER_NEGATE);
|
||||
imagefilter($im, IMG_FILTER_COLORIZE, -50, 0, 20, 50);
|
||||
imagefilter($im, IMG_FILTER_NEGATE );
|
||||
imagefilter($im, IMG_FILTER_BRIGHTNESS, 10);
|
||||
imagegammacorrect($im, 1, 1.2);
|
||||
return $im;
|
||||
}
|
||||
|
||||
public function pixelate($im,$val) {
|
||||
if($val==null) $val = 10;
|
||||
imagefilter($im,IMG_FILTER_PIXELATE,$val);
|
||||
return $im;
|
||||
}
|
||||
|
||||
public function blur($im,$blurFactor)
|
||||
{
|
||||
if(!$blurFactor)
|
||||
$blurFactor = 3;
|
||||
if($blurFactor>6)
|
||||
$blurFactor = 6;
|
||||
else if($blurFactor<0)
|
||||
$blurFactor = 0;
|
||||
// blurFactor has to be an integer
|
||||
$blurFactor = round($blurFactor);
|
||||
|
||||
$originalWidth = imagesx($im);
|
||||
$originalHeight = imagesy($im);
|
||||
|
||||
$smallestWidth = ceil($originalWidth * pow(0.5, $blurFactor));
|
||||
$smallestHeight = ceil($originalHeight * pow(0.5, $blurFactor));
|
||||
|
||||
// for the first run, the previous image is the original input
|
||||
$prevImage = $im;
|
||||
$prevWidth = $originalWidth;
|
||||
$prevHeight = $originalHeight;
|
||||
|
||||
// scale way down and gradually scale back up, blurring all the way
|
||||
for($i = 0; $i < $blurFactor; $i += 1)
|
||||
{
|
||||
// determine dimensions of next image
|
||||
$nextWidth = $smallestWidth * pow(2, $i);
|
||||
$nextHeight = $smallestHeight * pow(2, $i);
|
||||
|
||||
// resize previous image to next size
|
||||
$nextImage = imagecreatetruecolor($nextWidth, $nextHeight);
|
||||
imagecopyresized($nextImage, $prevImage, 0, 0, 0, 0,
|
||||
$nextWidth, $nextHeight, $prevWidth, $prevHeight);
|
||||
|
||||
// apply blur filter
|
||||
imagefilter($nextImage, IMG_FILTER_GAUSSIAN_BLUR);
|
||||
|
||||
// now the new image becomes the previous image for the next step
|
||||
$prevImage = $nextImage;
|
||||
$prevWidth = $nextWidth;
|
||||
$prevHeight = $nextHeight;
|
||||
}
|
||||
|
||||
// scale back to original size and blur one more time
|
||||
imagecopyresized($im, $nextImage,
|
||||
0, 0, 0, 0, $originalWidth, $originalHeight, $nextWidth, $nextHeight);
|
||||
imagefilter($im, IMG_FILTER_GAUSSIAN_BLUR);
|
||||
|
||||
// clean up
|
||||
imagedestroy($prevImage);
|
||||
|
||||
// return result
|
||||
return $im;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
class ImageController implements ContentController
|
||||
{
|
||||
public const ctype = 'static';
|
||||
//returns all extensions registered by this type of content
|
||||
public function getRegisteredExtensions(){return array('png','bmp','gif','jpg','jpeg','x-png','webp');}
|
||||
|
||||
@@ -24,8 +25,43 @@ class ImageController implements ContentController
|
||||
case 17: $ext = 'ico';break; // ico
|
||||
case 18: $ext = 'webp';break; // webp
|
||||
|
||||
case 2: //we clean up exif data of JPGs so GPS and other data is removed
|
||||
case 2:
|
||||
//we clean up exif data of JPGs so GPS and other data is removed
|
||||
$res = imagecreatefromjpeg($tmpfile);
|
||||
|
||||
// rotate based on EXIF Orientation
|
||||
$exif = exif_read_data($tmpfile);
|
||||
if (!empty($exif['Orientation'])) {
|
||||
switch ($exif['Orientation']) {
|
||||
case 2:
|
||||
imageflip($res, IMG_FLIP_HORIZONTAL);
|
||||
case 1:
|
||||
// Nothing to do
|
||||
break;
|
||||
|
||||
case 4:
|
||||
imageflip($res, IMG_FLIP_HORIZONTAL);
|
||||
// Also rotate
|
||||
case 3:
|
||||
$res = imagerotate($res, 180, 0);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
imageflip($res, IMG_FLIP_VERTICAL);
|
||||
// Also rotate
|
||||
case 6:
|
||||
$res = imagerotate($res, -90, 0);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
imageflip($res, IMG_FLIP_VERTICAL);
|
||||
// Also rotate
|
||||
case 8:
|
||||
$res = imagerotate($res, 90, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
imagejpeg($res, $tmpfile, (defined('JPEG_COMPRESSION')?JPEG_COMPRESSION:90));
|
||||
$ext = 'jpg';
|
||||
break;
|
||||
@@ -48,12 +84,12 @@ class ImageController implements ContentController
|
||||
|
||||
storeFile($tmpfile,$hash,true);
|
||||
|
||||
return array('status'=>'ok','hash'=>$hash,'url'=>URL.$hash);
|
||||
return array('status'=>'ok','hash'=>$hash,'url'=>getURL().$hash);
|
||||
}
|
||||
|
||||
public function handleHash($hash,$url)
|
||||
{
|
||||
$path = ROOT.DS.'data'.DS.$hash.DS.$hash;
|
||||
$path = getDataDir().DS.$hash.DS.$hash;
|
||||
$type = getExtensionOfFilename($hash);
|
||||
|
||||
//get all our sub files where all the good functions lie
|
||||
@@ -64,14 +100,31 @@ class ImageController implements ContentController
|
||||
//don't do this if it's a gif because PHP can't handle animated gifs
|
||||
if($type!='gif')
|
||||
{
|
||||
$filters = getFilters();
|
||||
foreach($url as $u)
|
||||
{
|
||||
if(isSize($u))
|
||||
$modifiers['size'] = $u;
|
||||
else if(isRotation($u))
|
||||
$modifiers['rotation'] = $u;
|
||||
else // check for filters
|
||||
{
|
||||
foreach($filters as $filter)
|
||||
{
|
||||
if(startsWith($u,$filter) && ($u==$filter || startsWith($u,$filter.'_')))
|
||||
{
|
||||
$a = explode('_',$u);
|
||||
$value = $a[1];
|
||||
if(is_numeric($value))
|
||||
$modifiers['filters'][] = array('filter'=>$filter,'value'=>$value);
|
||||
else
|
||||
$modifiers['filters'][] = array('filter'=>$filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(in_array('webp',$url) && $type!='webp')
|
||||
|
||||
if( (in_array('webp',$url) && $type!='webp') || ( $this->shouldAlwaysBeWebp() && ($type=='jpg' || $type=='png') ) )
|
||||
$modifiers['webp'] = true;
|
||||
if(in_array('forcesize',$url) && $modifiers['size'])
|
||||
$modifiers['forcesize'] = true;
|
||||
@@ -89,8 +142,9 @@ class ImageController implements ContentController
|
||||
//so if we take all parameters in key=>value form and hash it
|
||||
//we get one nice little hash for every eventuality
|
||||
$modhash = md5(http_build_query($modifiers,'',','));
|
||||
$newpath = ROOT.DS.'data'.DS.$hash.DS.$modhash.'_'.$hash;
|
||||
$newpath = getDataDir().DS.$hash.DS.$modhash.'_'.$hash;
|
||||
$im = $this->getObjOfImage($path);
|
||||
$f = new Filter();
|
||||
|
||||
if(!file_exists($newpath))
|
||||
{
|
||||
@@ -98,6 +152,15 @@ class ImageController implements ContentController
|
||||
{
|
||||
switch($mod)
|
||||
{
|
||||
case 'filters':
|
||||
foreach($val as $fd)
|
||||
{
|
||||
$filter = $fd['filter'];
|
||||
$value = $fd['value'];
|
||||
$im = $f->$filter($im,$value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'size':
|
||||
($modifiers['forcesize']?forceResize($im,$val):resize($im,$val));
|
||||
break;
|
||||
@@ -111,7 +174,7 @@ class ImageController implements ContentController
|
||||
break;
|
||||
|
||||
case 'mp4':
|
||||
$mp4path = ROOT.DS.'data'.DS.$hash.DS.$hash.'mp4';
|
||||
$mp4path = getDataDir().DS.$hash.DS.$hash.'mp4';
|
||||
if(!file_exists($mp4path))
|
||||
$this->gifToMP4($path,$mp4path);
|
||||
$path = $mp4path;
|
||||
@@ -128,7 +191,7 @@ class ImageController implements ContentController
|
||||
|
||||
header ("Content-type: image/jpeg");
|
||||
readfile($preview);
|
||||
|
||||
exit;
|
||||
}
|
||||
else if(in_array('download',$url))
|
||||
{
|
||||
@@ -148,6 +211,7 @@ class ImageController implements ContentController
|
||||
{
|
||||
$data = array('url'=>implode('/',$url),'hash'=>$hash,'filesize'=>renderSize(filesize($path)));
|
||||
renderTemplate('video',$data);
|
||||
exit;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -155,31 +219,46 @@ class ImageController implements ContentController
|
||||
|
||||
$this->saveObjOfImage($im,$newpath,$type);
|
||||
}
|
||||
else if($modifiers['webp'])
|
||||
{
|
||||
$type = 'webp';
|
||||
}
|
||||
$path = $newpath;
|
||||
|
||||
}
|
||||
|
||||
|
||||
switch($type)
|
||||
{
|
||||
case 'jpeg':
|
||||
case 'jpg':
|
||||
header ("Content-type: image/jpeg");
|
||||
header ("Last-Modified: ".gmdate('D, d M Y H:i:s ', filemtime($path)) . 'GMT');
|
||||
header ("ETag: $hash");
|
||||
header('Cache-control: public, max-age=31536000');
|
||||
readfile($path);
|
||||
break;
|
||||
|
||||
case 'png':
|
||||
header ("Content-type: image/png");
|
||||
header ("Last-Modified: ".gmdate('D, d M Y H:i:s ', filemtime($path)) . 'GMT');
|
||||
header ("ETag: $hash");
|
||||
header('Cache-control: public, max-age=31536000');
|
||||
readfile($path);
|
||||
break;
|
||||
|
||||
case 'gif':
|
||||
header ("Content-type: image/gif");
|
||||
header ("Last-Modified: ".gmdate('D, d M Y H:i:s ', filemtime($path)) . 'GMT');
|
||||
header ("ETag: $hash");
|
||||
header('Cache-control: public, max-age=31536000');
|
||||
readfile($path);
|
||||
break;
|
||||
|
||||
case 'webp':
|
||||
header ("Content-type: image/webp");
|
||||
header ("Last-Modified: ".gmdate('D, d M Y H:i:s ', filemtime($path)) . 'GMT');
|
||||
header ("ETag: $hash");
|
||||
header('Cache-control: public, max-age=31536000');
|
||||
readfile($path);
|
||||
break;
|
||||
}
|
||||
@@ -206,22 +285,46 @@ class ImageController implements ContentController
|
||||
|
||||
function saveObjOfImage($im,$path,$type)
|
||||
{
|
||||
$tmppath = '/tmp/'.getNewHash($type,12);
|
||||
switch($type)
|
||||
{
|
||||
case 'jpeg':
|
||||
case 'jpg':
|
||||
imagejpeg($im,$path,(defined('JPEG_COMPRESSION')?JPEG_COMPRESSION:90));
|
||||
imagejpeg($im,$tmppath,(defined('JPEG_COMPRESSION')?JPEG_COMPRESSION:90));
|
||||
break;
|
||||
|
||||
case 'png':
|
||||
imagepng($im,$path,(defined('PNG_COMPRESSION')?PNG_COMPRESSION:6));
|
||||
imagepng($im,$tmppath,(defined('PNG_COMPRESSION')?PNG_COMPRESSION:6));
|
||||
break;
|
||||
|
||||
case 'webp':
|
||||
imagewebp($im,$path,(defined('WEBP_COMPRESSION')?WEBP_COMPRESSION:80));
|
||||
imagepalettetotruecolor($im);
|
||||
imagealphablending($im, true);
|
||||
imagewebp($im,$tmppath,(defined('WEBP_COMPRESSION')?WEBP_COMPRESSION:80));
|
||||
break;
|
||||
}
|
||||
|
||||
return $im;
|
||||
if(file_exists($tmppath) && filesize($tmppath)>0)
|
||||
{
|
||||
rename($tmppath,$path);
|
||||
return $im;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function shouldAlwaysBeWebp()
|
||||
{
|
||||
//sanity check
|
||||
if(!$_SERVER['HTTP_ACCEPT']) return false;
|
||||
|
||||
if(defined('ALWAYS_WEBP') && ALWAYS_WEBP && strpos( $_SERVER['HTTP_ACCEPT'], 'image/webp' ) !== false )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -91,14 +91,14 @@ function rotate(&$im,$direction)
|
||||
if ($height > $width)
|
||||
{
|
||||
$ratio = $maxheight / $height;
|
||||
$newheight = $maxheight;
|
||||
$newwidth = $width * $ratio;
|
||||
$newheight = intval($maxheight);
|
||||
$newwidth = intval($width * $ratio) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
$ratio = $maxwidth / $width;
|
||||
$newwidth = $maxwidth;
|
||||
$newheight = $height * $ratio;
|
||||
$newwidth = intval($maxwidth) ;
|
||||
$newheight = intval($height * $ratio);
|
||||
}
|
||||
|
||||
$newimg = imagecreatetruecolor($newwidth,$newheight);
|
||||
|
||||
Binary file not shown.
52
content-controllers/placeholder/placeholder.controller.php
Normal file
52
content-controllers/placeholder/placeholder.controller.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
class PlaceholderController implements ContentController
|
||||
{
|
||||
public const ctype = 'dynamic';
|
||||
|
||||
//returns all extensions registered by this type of content
|
||||
public function getRegisteredExtensions(){return array('placeholder');}
|
||||
|
||||
public function handleHash($hash,$url)
|
||||
{
|
||||
$path = getDataDir().DS.$hash.DS.$hash;
|
||||
|
||||
include_once(dirname(__FILE__).DS.'placeholdergenerator.php');
|
||||
$pg = new PlaceholderGenerator();
|
||||
|
||||
foreach($url as $u)
|
||||
{
|
||||
if(isSize($u))
|
||||
$modifiers['size'] = $u;
|
||||
if(startsWith($u,'color-'))
|
||||
{
|
||||
$u = substr($u,6);
|
||||
$colors = explode('-',$u);
|
||||
foreach($colors as $c)
|
||||
if(isColor($c))
|
||||
$modifiers['colors'][] = (ctype_xdigit($c)?$c:color_name_to_hex($c));
|
||||
|
||||
if(count($modifiers['colors'])>4)
|
||||
$modifiers['colors'] = array_slice($modifiers['colors'],0,4);
|
||||
}
|
||||
}
|
||||
|
||||
$img = $pg->generateImage($modifiers);
|
||||
|
||||
$img = $pg->gradient($img, $modifiers['colors']);
|
||||
$img = $pg->addSizeText($img,$modifiers);
|
||||
|
||||
header ("Content-type: image/jpeg");
|
||||
header ("ETag: $hash");
|
||||
header('Cache-control: public, max-age=31536000');
|
||||
|
||||
imagejpeg($img,null,(defined('JPEG_COMPRESSION')?JPEG_COMPRESSION:90));
|
||||
}
|
||||
|
||||
public function handleUpload($tmpfile,$hash=false)
|
||||
{
|
||||
return array('status'=>'err','hash'=>$hash,'reason'=>'Cannot upload to placeholder image');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
88
content-controllers/placeholder/placeholdergenerator.php
Normal file
88
content-controllers/placeholder/placeholdergenerator.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
class PlaceholderGenerator {
|
||||
|
||||
function generateImage($modifiers)
|
||||
{
|
||||
$size = ($modifiers['size']?:'800x600');
|
||||
|
||||
$sd = sizeStringToWidthHeight($size);
|
||||
$width = $sd['width'];
|
||||
$height = $sd['height'];
|
||||
|
||||
$im = imagecreatetruecolor($width, $height);
|
||||
|
||||
return $im;
|
||||
}
|
||||
|
||||
function addSizeText($im,$modifiers)
|
||||
{
|
||||
$size = imagesx($im).'x'.imagesy($im);
|
||||
$text = $size;
|
||||
//add the size as text in the center of the image
|
||||
$textcolor = imagecolorallocate($im, 0, 0, 0);
|
||||
$font = dirname(__FILE__).DS.'fonts/RonysiswadiArchitect5-1GErv.ttf';
|
||||
//calculate the size of the text to make sure it will alway be visible
|
||||
$fontsize = 20;
|
||||
$textsize = imagettfbbox($fontsize, 0, $font, $text);
|
||||
$scaleX = imagesx($im) / ($textsize[2] - $textsize[0] + 25);
|
||||
$scaleY = imagesy($im) / ($textsize[1] - $textsize[7] + 25);
|
||||
|
||||
$scale = min($scaleX,$scaleY);
|
||||
|
||||
$fontsize = 20 * $scale;
|
||||
$textsize = imagettfbbox($fontsize, 0, $font, $text);
|
||||
$textwidth = $textsize[2] - $textsize[0];
|
||||
$textheight = $textsize[1] - $textsize[7];
|
||||
|
||||
if($textwidth > imagesx($im) || $textheight > imagesy($im))
|
||||
return $im;
|
||||
|
||||
$x = intval((imagesx($im) - $textwidth) / 2);
|
||||
$y = intval((imagesy($im) - $textheight) / 2 + $textheight);
|
||||
imagettftext($im, $fontsize, 0, $x, $y, $textcolor, $font, $text);
|
||||
|
||||
return $im;
|
||||
}
|
||||
|
||||
function gradient($im, $c) {
|
||||
|
||||
$w = imagesx($im);
|
||||
$h = imagesy($im);
|
||||
|
||||
if(!$c[0]) $c = ['ffffff','ffffff','ffffff','ffffff'];
|
||||
else if(!$c[1]) $c = [$c[0],$c[0],$c[0],$c[0]];
|
||||
else if(!$c[2]) $c = [$c[0],$c[0],$c[1],$c[1]];
|
||||
else if(!$c[3]) $c = [$c[0],$c[1],$c[2],$c[0]];
|
||||
|
||||
for($i=0;$i<=3;$i++) {
|
||||
$c[$i]=$this->hex2rgb($c[$i]);
|
||||
}
|
||||
|
||||
$rgb=$c[0]; // start with top left color
|
||||
for($x=0;$x<=$w;$x++) { // loop columns
|
||||
for($y=0;$y<=$h;$y++) { // loop rows
|
||||
// set pixel color
|
||||
$col=imagecolorallocate($im,intval($rgb[0]),intval($rgb[1]),intval($rgb[2]));
|
||||
imagesetpixel($im,$x-1,$y-1,$col);
|
||||
// calculate new color
|
||||
for($i=0;$i<=2;$i++) {
|
||||
$rgb[$i]=
|
||||
$c[0][$i]*(($w-$x)*($h-$y)/($w*$h)) +
|
||||
$c[1][$i]*($x *($h-$y)/($w*$h)) +
|
||||
$c[2][$i]*(($w-$x)*$y /($w*$h)) +
|
||||
$c[3][$i]*($x *$y /($w*$h));
|
||||
}
|
||||
}
|
||||
}
|
||||
return $im;
|
||||
}
|
||||
|
||||
function hex2rgb($hex)
|
||||
{
|
||||
$rgb[0]=hexdec(substr($hex,0,2));
|
||||
$rgb[1]=hexdec(substr($hex,2,2));
|
||||
$rgb[2]=hexdec(substr($hex,4,2));
|
||||
return($rgb);
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
class TextController implements ContentController
|
||||
{
|
||||
public const ctype = 'static';
|
||||
|
||||
//returns all extensions registered by this type of content
|
||||
public function getRegisteredExtensions(){return array('txt');}
|
||||
public function getRegisteredExtensions(){return array('txt','text','csv');}
|
||||
|
||||
public function handleHash($hash,$url)
|
||||
{
|
||||
$path = ROOT.DS.'data'.DS.$hash.DS.$hash;
|
||||
$path = getDataDir().DS.$hash.DS.$hash;
|
||||
|
||||
if(in_array('raw',$url))
|
||||
{
|
||||
@@ -48,11 +50,11 @@ class TextController implements ContentController
|
||||
|
||||
storeFile($tmpfile,$hash,true);
|
||||
|
||||
return array('status'=>'ok','hash'=>$hash,'url'=>URL.$hash);
|
||||
return array('status'=>'ok','hash'=>$hash,'url'=>getURL().$hash);
|
||||
}
|
||||
|
||||
function getTypeOfText($hash)
|
||||
{
|
||||
return file_get_contents(ROOT.DS.'data'.DS.$hash.DS.'type');
|
||||
return file_get_contents(getDataDir().DS.$hash.DS.'type');
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
class UrlController implements ContentController
|
||||
{
|
||||
public const ctype = 'static';
|
||||
|
||||
//returns all extensions registered by this type of content
|
||||
public function getRegisteredExtensions(){return array('url');}
|
||||
public function handleHash($hash,$url){}
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
class VideoController implements ContentController
|
||||
{
|
||||
public const ctype = 'static';
|
||||
|
||||
//returns all extensions registered by this type of content
|
||||
public function getRegisteredExtensions(){return array('mp4');}
|
||||
|
||||
public function handleHash($hash,$url)
|
||||
{
|
||||
$path = ROOT.DS.'data'.DS.$hash.DS.$hash;
|
||||
$path = getDataDir().DS.$hash.DS.$hash;
|
||||
|
||||
//@todo: - resize by changing $path
|
||||
|
||||
@@ -19,7 +21,7 @@ class VideoController implements ContentController
|
||||
{
|
||||
$s = sizeStringToWidthHeight($size);
|
||||
$width = $s['width'];
|
||||
$newpath = ROOT.DS.'data'.DS.$hash.DS.$width.'_'.$hash;
|
||||
$newpath = getDataDir().DS.$hash.DS.$width.'_'.$hash;
|
||||
if(!file_exists($newpath))
|
||||
$this->resize($path,$newpath,$width);
|
||||
$path = $newpath;
|
||||
@@ -72,12 +74,12 @@ class VideoController implements ContentController
|
||||
return array('status'=>'err','hash'=>$hash,'reason'=>'Custom hash already exists');
|
||||
}
|
||||
|
||||
storeFile($tmpfile,$hash,true);
|
||||
$file = storeFile($tmpfile,$hash,true);
|
||||
|
||||
if(!$this->rightEncodedMP4($file))
|
||||
system("nohup php ".ROOT.DS.'tools'.DS.'re-encode_mp4.php force '.$hash." > /dev/null 2> /dev/null &");
|
||||
|
||||
return array('status'=>'ok','hash'=>$hash,'url'=>URL.$hash);
|
||||
return array('status'=>'ok','hash'=>$hash,'url'=>getURL().$hash);
|
||||
}
|
||||
|
||||
|
||||
@@ -91,6 +93,7 @@ class VideoController implements ContentController
|
||||
$start = 0;
|
||||
$end = $size - 1;
|
||||
header('Content-type: video/mp4');
|
||||
header('Cache-control: public, max-age=31536000');
|
||||
header("Accept-Ranges: 0-$length");
|
||||
if (isset($_SERVER['HTTP_RANGE'])) {
|
||||
$c_start = $start;
|
||||
|
||||
1
css/imgs/logo/logo.svg
Normal file
1
css/imgs/logo/logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2" viewBox="0 0 318.8 194"><circle cx="97" cy="97" r="97" style="fill:url(#a)"/><path d="M97.3 6.8c50 0 90.5 40.3 90.5 90s-40.5 90-90.5 90-90.5-40.3-90.5-90 40.5-90 90.5-90Zm69.8 140.7a85.2 85.2 0 0 0 16.6-50.6A86.2 86.2 0 0 0 97.3 11 86.2 86.2 0 0 0 11 96.9c0 17 5 32.8 13.5 46.2 36.3-67.4 36.2-74.7 45.3-74.7 8.8 0 10.3 7.6 43.3 71.4 16.7-25.9 18.9-30.4 24.3-30.4 6.7 0 8 5 29.7 38Zm-29.3-68.2a10.6 10.6 0 1 1 0 21.2 10.6 10.6 0 0 1 0-21.2Z" style="fill:url(#b)"/><path d="M253 37.1a33 33 0 1 1 10.4 27l-69.7 26.1-6.4-28.5L253 37.1Z" style="fill:url(#c)"/><path d="M263.4 129.3a32.9 32.9 0 0 1 55.4 24.2 33 33 0 0 1-65.8 3.2l-65.6-24.8 6.4-28.9 69.6 26.3Z" style="fill:url(#d)"/><defs><radialGradient id="a" cx="0" cy="0" r="1" gradientTransform="matrix(79 0 0 -73 97.3 77.6)" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#867278;stop-opacity:1"/><stop offset=".5" style="stop-color:#5c4955;stop-opacity:1"/><stop offset="1" style="stop-color:#433545;stop-opacity:1"/></radialGradient><radialGradient id="c" cx="0" cy="0" r="1" gradientTransform="matrix(55 0 0 -55 253 48.6)" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#867278;stop-opacity:1"/><stop offset=".5" style="stop-color:#5c4955;stop-opacity:1"/><stop offset="1" style="stop-color:#433545;stop-opacity:1"/></radialGradient><radialGradient id="d" cx="0" cy="0" r="1" gradientTransform="matrix(55 0 0 -56 253.1 144.8)" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#867278;stop-opacity:1"/><stop offset=".5" style="stop-color:#5c4955;stop-opacity:1"/><stop offset="1" style="stop-color:#433545;stop-opacity:1"/></radialGradient><linearGradient id="b" x1="0" x2="1" y1="0" y2="0" gradientTransform="matrix(97 0 0 97 53.6 97)" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#40a1d4;stop-opacity:1"/><stop offset="1" style="stop-color:#2971c2;stop-opacity:1"/></linearGradient></defs></svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
69
docker/Dockerfile
Normal file
69
docker/Dockerfile
Normal file
@@ -0,0 +1,69 @@
|
||||
FROM alpine:3.18
|
||||
|
||||
RUN apk add --no-cache bash socat wget curl nginx file ffmpeg unzip zlib redis \
|
||||
php82-fileinfo \
|
||||
php82-session \
|
||||
php \
|
||||
php-curl \
|
||||
php-openssl \
|
||||
php-mbstring \
|
||||
php-json \
|
||||
php-gd \
|
||||
php-dom \
|
||||
php-fpm \
|
||||
php82 \
|
||||
php82-pdo \
|
||||
php82-exif \
|
||||
php82-curl \
|
||||
php82-gd \
|
||||
php82-json \
|
||||
php82-phar \
|
||||
php82-fpm \
|
||||
php82-openssl \
|
||||
php82-ctype \
|
||||
php82-opcache \
|
||||
php82-mbstring \
|
||||
php82-sodium \
|
||||
php82-xml \
|
||||
php82-ftp \
|
||||
php82-simplexml \
|
||||
php82-session \
|
||||
php82-fileinfo \
|
||||
php82-pcntl \
|
||||
php82-pecl-redis
|
||||
|
||||
RUN ln -s /usr/bin/php82 /usr/bin/php
|
||||
|
||||
RUN curl -sS https://getcomposer.org/installer | /usr/bin/php -- --install-dir=/usr/bin --filename=composer
|
||||
RUN mkdir -p /var/www
|
||||
WORKDIR /var/www
|
||||
|
||||
ADD . /var/www/.
|
||||
|
||||
ADD docker/rootfs/start.sh /etc/start.sh
|
||||
RUN chmod +x /etc/start.sh
|
||||
|
||||
# Composer intall
|
||||
WORKDIR /var/www/lib
|
||||
RUN composer install --no-dev --no-interaction --no-progress --optimize-autoloader
|
||||
|
||||
# nginx stuff
|
||||
WORKDIR /var/www
|
||||
ADD docker/rootfs/nginx.conf /etc/nginx/http.d/default.conf
|
||||
RUN mkdir -p /run/nginx
|
||||
RUN mkdir -p /var/log/nginx
|
||||
RUN sed -i 's/nobody/nginx/g' /etc/php82/php-fpm.d/www.conf
|
||||
|
||||
# Since requests can trigger conversion, let's give the server enough time to respond
|
||||
RUN sed -i "/max_execution_time/c\max_execution_time=3600" /etc/php82/php.ini
|
||||
RUN sed -i "/max_input_time/c\max_input_time=3600" /etc/php82/php.ini
|
||||
|
||||
WORKDIR /var/www/
|
||||
|
||||
# Volumes to mount
|
||||
#VOLUME /var/lib/influxdb
|
||||
VOLUME /var/www/data
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
ENTRYPOINT ["/etc/start.sh"]
|
||||
73
docker/rootfs/nginx.conf
Normal file
73
docker/rootfs/nginx.conf
Normal file
@@ -0,0 +1,73 @@
|
||||
server {
|
||||
listen 80 default_server;
|
||||
|
||||
set $base /var/www;
|
||||
root /var/www/;
|
||||
|
||||
index index.php;
|
||||
|
||||
client_max_body_size 50M;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?url=$request_uri;
|
||||
}
|
||||
|
||||
location ~ /(data|tmp|bin|content-controllers|inc|interfaces|storage-controllers|templates|tools|docker) {
|
||||
deny all;
|
||||
return 404;
|
||||
}
|
||||
|
||||
# logging
|
||||
access_log /var/log/nginx/pictshare/access.log;
|
||||
error_log /var/log/nginx/pictshare/error.log warn;
|
||||
|
||||
location ~ \.php$ {
|
||||
# 404
|
||||
try_files $fastcgi_script_name =404;
|
||||
|
||||
# default fastcgi_params
|
||||
include fastcgi_params;
|
||||
|
||||
# fastcgi settings
|
||||
fastcgi_pass 127.0.0.1:9000;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_buffers 8 16k;
|
||||
fastcgi_buffer_size 32k;
|
||||
fastcgi_max_temp_file_size 0; # caching files to disk while uploading. this will help low-memory devices
|
||||
fastcgi_read_timeout 1d; # we set the timeout to 1 day so big uploads and have enough time to be delivered
|
||||
fastcgi_send_timeout 1d; # it's especially important if you want to use storage controllers like S3 or FTP
|
||||
fastcgi_request_buffering off; # disabbling buffering which will send the uploaded data right to PHP
|
||||
|
||||
# fastcgi params
|
||||
fastcgi_param DOCUMENT_ROOT $realpath_root;
|
||||
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
|
||||
fastcgi_param PHP_ADMIN_VALUE "open_basedir=$base/:/usr/lib/php/:/tmp/";
|
||||
}
|
||||
|
||||
location /favicon.ico {
|
||||
log_not_found off;
|
||||
}
|
||||
|
||||
# security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||
add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
||||
|
||||
# svg, fonts
|
||||
location ~* \.(?:svgz?|ttf|ttc|otf|eot|woff2?)$ {
|
||||
add_header Access-Control-Allow-Origin "*";
|
||||
expires 7d;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# gzip
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss application/atom+xml image/svg+xml;
|
||||
|
||||
}
|
||||
96
docker/rootfs/start.sh
Normal file
96
docker/rootfs/start.sh
Normal file
@@ -0,0 +1,96 @@
|
||||
#!/bin/bash
|
||||
|
||||
######### functions
|
||||
|
||||
_maxUploadSize() {
|
||||
echo "[i] Setting uploadsize to ${MAX_UPLOAD_SIZE}M"
|
||||
|
||||
sed -i "/post_max_size/c\post_max_size=${MAX_UPLOAD_SIZE}M" /etc/php82/php.ini
|
||||
sed -i "/upload_max_filesize/c\upload_max_filesize=${MAX_UPLOAD_SIZE}M" /etc/php82/php.ini
|
||||
|
||||
# set error reporting no notices, no warnings
|
||||
sed -i "/^error_reporting/c\error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_WARNING & ~E_NOTICE" /etc/php82/php.ini
|
||||
|
||||
sed -i -e "s/50M/${MAX_UPLOAD_SIZE}M/g" /etc/nginx/http.d/default.conf
|
||||
|
||||
MAX_RAM=$((MAX_UPLOAD_SIZE + 30)) # 30megs more than the upload size
|
||||
echo "[i] Also changing memory limit of PHP to ${MAX_RAM}M"
|
||||
sed -i -e "s/128M/${MAX_RAM}M/g" /etc/php82/php.ini
|
||||
sed -i "/memory_limit/c\memory_limit=${MAX_RAM}M" /etc/php82/php.ini
|
||||
}
|
||||
|
||||
_filePermissions() {
|
||||
chown -R nginx:nginx /var/www
|
||||
touch data/sha1.csv
|
||||
chown nginx:nginx data/sha1.csv
|
||||
}
|
||||
|
||||
_buildConfig() {
|
||||
echo "<?php"
|
||||
echo "define('URL', '${URL:-}');"
|
||||
echo "define('TITLE', '${TITLE:-PictShare}');"
|
||||
echo "define('ALLOWED_SUBNET', '${ALLOWED_SUBNET:-}');"
|
||||
echo "define('CONTENTCONTROLLERS', '${CONTENTCONTROLLERS:-}');"
|
||||
echo "define('MASTER_DELETE_CODE', '${MASTER_DELETE_CODE:-}');"
|
||||
echo "define('MASTER_DELETE_IP', '${MASTER_DELETE_IP:-}');"
|
||||
echo "define('UPLOAD_FORM_LOCATION', '${UPLOAD_FORM_LOCATION:-}');"
|
||||
echo "define('UPLOAD_CODE', '${UPLOAD_CODE:-}');"
|
||||
echo "define('LOG_UPLOADER', ${LOG_UPLOADER:-false});"
|
||||
echo "define('MAX_RESIZED_IMAGES',${MAX_RESIZED_IMAGES:--1});"
|
||||
echo "define('ALLOW_BLOATING', ${ALLOW_BLOATING:-false});"
|
||||
echo "define('SHOW_ERRORS', ${SHOW_ERRORS:-false});"
|
||||
echo "define('JPEG_COMPRESSION', ${JPEG_COMPRESSION:-90});"
|
||||
echo "define('PNG_COMPRESSION', ${PNG_COMPRESSION:-6});"
|
||||
echo "define('ALT_FOLDER', '${ALT_FOLDER:-}');"
|
||||
echo "define('S3_BUCKET', '${S3_BUCKET:-}');"
|
||||
echo "define('S3_ACCESS_KEY', '${S3_ACCESS_KEY:-}');"
|
||||
echo "define('S3_SECRET_KEY', '${S3_SECRET_KEY:-}');"
|
||||
echo "define('S3_ENDPOINT', '${S3_ENDPOINT:-}');"
|
||||
echo "define('S3_REGION', '${S3_REGION:-}');"
|
||||
echo "define('FTP_SERVER', '${FTP_SERVER:-}');"
|
||||
echo "define('FTP_PORT', ${FTP_PORT:-21});"
|
||||
echo "define('FTP_USER', '${FTP_USER:-}');"
|
||||
echo "define('FTP_PASS', '${FTP_PASS:-}');"
|
||||
echo "define('FTP_PASSIVEMODE', ${FTP_PASSIVEMODE:-true});"
|
||||
echo "define('FTP_SSL', ${FTP_SSL:-false});"
|
||||
echo "define('FTP_BASEDIR', '${FTP_BASEDIR:-}');"
|
||||
echo "define('ENCRYPTION_KEY', '${ENCRYPTION_KEY:-}');"
|
||||
echo "define('FFMPEG_BINARY', '${FFMPEG_BINARY:-/usr/bin/ffmpeg}');"
|
||||
echo "define('ALWAYS_WEBP', ${ALWAYS_WEBP:-false});"
|
||||
echo "define('ALLOWED_DOMAINS', '${ALLOWED_DOMAINS:-}');"
|
||||
echo "define('SPLIT_DATA_DIR', ${SPLIT_DATA_DIR:-false});"
|
||||
}
|
||||
|
||||
|
||||
|
||||
######### main
|
||||
|
||||
echo 'Starting Pictshare'
|
||||
|
||||
cd /var/www/
|
||||
|
||||
if [[ ${MAX_UPLOAD_SIZE:=100} =~ ^[0-9]+$ ]]; then
|
||||
_maxUploadSize
|
||||
fi
|
||||
|
||||
# run _filePermissions function unless SKIP_FILEPERMISSIONS is set to true
|
||||
if [[ ${SKIP_FILEPERMISSIONS:=false} != true ]]; then
|
||||
_filePermissions
|
||||
fi
|
||||
|
||||
echo ' [+] Starting php'
|
||||
php-fpm82
|
||||
|
||||
echo ' [+] Creating config'
|
||||
|
||||
_buildConfig > inc/config.inc.php
|
||||
|
||||
echo ' [+] Starting nginx'
|
||||
|
||||
mkdir -p /var/log/nginx/pictshare
|
||||
touch /var/log/nginx/pictshare/access.log
|
||||
touch /var/log/nginx/pictshare/error.log
|
||||
|
||||
nginx
|
||||
|
||||
tail -f /var/log/nginx/pictshare/*.log
|
||||
628
inc/core.php
628
inc/core.php
@@ -4,7 +4,7 @@ spl_autoload_register('autoload');
|
||||
//disable output buffering
|
||||
if (ob_get_level()) ob_end_clean();
|
||||
|
||||
error_reporting(E_ALL & ~E_NOTICE);
|
||||
error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
|
||||
|
||||
if(!defined('FFMPEG_BINARY'))
|
||||
define('FFMPEG_BINARY',ROOT.DS.'bin'.DS.'ffmpeg');
|
||||
@@ -24,7 +24,13 @@ function architect($url)
|
||||
//just show the site
|
||||
if( ( (!defined('UPLOAD_FORM_LOCATION') || (defined('UPLOAD_FORM_LOCATION') && !UPLOAD_FORM_LOCATION)) && count($u)==0) || (defined('UPLOAD_FORM_LOCATION') && UPLOAD_FORM_LOCATION && '/'.implode('/',$u)==UPLOAD_FORM_LOCATION) )
|
||||
{
|
||||
renderTemplate('main');
|
||||
// check if client address is allowed
|
||||
$forbidden = false;
|
||||
if(defined('ALLOWED_SUBNET') && ALLOWED_SUBNET != '' && !isIPInRange( getUserIP(), ALLOWED_SUBNET ))
|
||||
{
|
||||
$forbidden = true;
|
||||
}
|
||||
renderTemplate('main',array('forbidden'=>$forbidden));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -43,18 +49,51 @@ function architect($url)
|
||||
if(!$sc)
|
||||
$sc = getStorageControllers();
|
||||
foreach($sc as $contr)
|
||||
{
|
||||
{
|
||||
$c = new $contr();
|
||||
if($c->isEnabled()===true && $c->hashExists($el))
|
||||
|
||||
if($c->isEnabled()===true && $c->hashExists($el))
|
||||
{
|
||||
$c->pullFile($el);
|
||||
$hash = $el;
|
||||
break; // we brake here because we already have the file. no need to check other storage controllers
|
||||
$c->pullFile($hash,ROOT.DS.'tmp'.DS.$hash);
|
||||
if(!file_exists(ROOT.DS.'tmp'.DS.$hash)) continue;
|
||||
storeFile(ROOT.DS.'tmp'.DS.$hash,$hash,true);
|
||||
|
||||
break; // we break here because we already have the file. no need to check other storage controllers
|
||||
}
|
||||
else if($c->isEnabled()===true && defined('ENCRYPTION_KEY') && ENCRYPTION_KEY !='' && $c->hashExists($el.'.enc')) //this is an encrypted file. Let's decrypt it
|
||||
{
|
||||
$hash = $el.'.enc';
|
||||
$c->pullFile($hash,ROOT.DS.'tmp'.DS.$hash);
|
||||
if(!file_exists(ROOT.DS.'tmp'.DS.$hash)) continue;
|
||||
$enc = new Encryption;
|
||||
$hash = substr($hash,0,-4);
|
||||
$enc->decryptFile(ROOT.DS.'tmp'.DS.$el.'.enc', ROOT.DS.'tmp'.DS.$hash,base64_decode(ENCRYPTION_KEY));
|
||||
|
||||
storeFile(ROOT.DS.'tmp'.DS.$hash,$hash,true);
|
||||
unlink(ROOT.DS.'tmp'.DS.$el.'.enc');
|
||||
|
||||
break; // we break here because we already have the file. no need to check other storage controllers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
// if it's still false, we only have one hope: Maybe it's from a dynamic controller and the cache hasn't been created yet
|
||||
else if($hash===false)
|
||||
{
|
||||
foreach(loadAllContentControllers(true) as $cc)
|
||||
{
|
||||
if((new $cc)::ctype=='dynamic' && in_array((new $cc)->getRegisteredExtensions()[0],$u) )
|
||||
{
|
||||
$hash = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//we didn't find a hash. send error 404
|
||||
if($hash===false)
|
||||
{
|
||||
@@ -85,38 +124,82 @@ function architect($url)
|
||||
$extension = pathinfo($hash, PATHINFO_EXTENSION);
|
||||
|
||||
|
||||
//First, check if URL is an image
|
||||
if(in_array($extension,(new ImageController)->getRegisteredExtensions()))
|
||||
foreach(loadAllContentControllers(true) as $cc)
|
||||
{
|
||||
(new ImageController())->handleHash($hash,$u);
|
||||
}
|
||||
//or, a url
|
||||
else if(in_array($extension,(new UrlController)->getRegisteredExtensions()))
|
||||
{
|
||||
var_dump("Url");
|
||||
}
|
||||
//or, a text
|
||||
else if(in_array($extension,(new TextController)->getRegisteredExtensions()))
|
||||
{
|
||||
(new TextController())->handleHash($hash,$u);
|
||||
}
|
||||
//or, a video
|
||||
else if(in_array($extension,(new VideoController)->getRegisteredExtensions()))
|
||||
{
|
||||
(new VideoController())->handleHash($hash,$u);
|
||||
}
|
||||
//very odd. We know it's a valid hash but no controller says it's one of their kids
|
||||
//oh well
|
||||
else
|
||||
{
|
||||
var_dump("odd err");
|
||||
if(
|
||||
((new $cc)::ctype=='dynamic' && in_array((new $cc)->getRegisteredExtensions()[0],$u)) ||
|
||||
((new $cc)::ctype=='static' && in_array($extension,(new $cc)->getRegisteredExtensions()))
|
||||
)
|
||||
{
|
||||
(new $cc())->handleHash($hash,$u);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
http_response_code(404);
|
||||
die("404");
|
||||
}
|
||||
|
||||
//var_dump($u);
|
||||
}
|
||||
|
||||
function storageControllerUpload($hash)
|
||||
{
|
||||
// Lets' check all storage controllers and tell them that a new file was uploaded
|
||||
$sc = getStorageControllers();
|
||||
$allgood = true;
|
||||
$uploadedhash =$hash;
|
||||
foreach($sc as $contr)
|
||||
{
|
||||
$controller = new $contr();
|
||||
if($controller->isEnabled()===true)
|
||||
{
|
||||
$source = getDataDir().DS.$hash.DS.$hash;
|
||||
if(defined('ENCRYPTION_KEY') && ENCRYPTION_KEY) //ok so we got an encryption key which means we'll store only the encrypted file
|
||||
{
|
||||
$enc = new Encryption;
|
||||
$encoded_file = ROOT.DS.'tmp'.DS.$hash.'.enc';
|
||||
$enc->encryptFile($source,$encoded_file,base64_decode(ENCRYPTION_KEY));
|
||||
$controller->pushFile($encoded_file,$hash.'.enc');
|
||||
unlink($encoded_file);
|
||||
$uploadedhash = $hash.'.enc';
|
||||
}
|
||||
else // not encrypted
|
||||
$controller->pushFile($source,$hash);
|
||||
|
||||
//let's check if the file is really there. If not, queue it for later
|
||||
if(!$controller->hashExists($uploadedhash))
|
||||
{
|
||||
$allgood = false;
|
||||
$queuefile=ROOT.DS.'tmp'.DS.'controllerqueue.txt';
|
||||
if(!file_exists($queuefile) || !stringInFile($hash,$queuefile))
|
||||
{
|
||||
$fp=fopen($queuefile,'a');
|
||||
if($fp)
|
||||
{
|
||||
fwrite($fp,$hash."\n");
|
||||
fclose($fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $allgood;
|
||||
}
|
||||
|
||||
function stringInFile($string,$file)
|
||||
{
|
||||
$handle = fopen($file, 'r');
|
||||
while (($line = fgets($handle)) !== false) {
|
||||
$line=trim($line);
|
||||
if($line==$string) return true;
|
||||
}
|
||||
fclose($handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
function getNewHash($type,$length=10)
|
||||
{
|
||||
while(1)
|
||||
@@ -129,7 +212,8 @@ function getNewHash($type,$length=10)
|
||||
|
||||
function isExistingHash($hash)
|
||||
{
|
||||
return is_dir(ROOT.DS.'data'.DS.$hash);
|
||||
if(!trim($hash)) return false;
|
||||
return is_dir(getDataDir().DS.$hash);
|
||||
}
|
||||
|
||||
function mightBeAHash($string)
|
||||
@@ -146,10 +230,10 @@ function mightBeAHash($string)
|
||||
|
||||
function autoload($className)
|
||||
{
|
||||
if (file_exists(ROOT . DS . 'content-controllers' . DS . strtolower($className) . '.php'))
|
||||
require_once(ROOT . DS . 'content-controllers' . DS . strtolower($className) . '.php');
|
||||
if (file_exists(ROOT . DS . 'interfaces' . DS . strtolower($className) . '.interface.php'))
|
||||
require_once(ROOT . DS . 'interfaces' . DS . strtolower($className) . '.interface.php');
|
||||
require_once(ROOT . DS . 'interfaces' . DS . strtolower($className) . '.interface.php');
|
||||
if ($className=='Encryption')
|
||||
require_once(ROOT . DS . 'inc' . DS . 'encryption.php');
|
||||
}
|
||||
|
||||
function renderTemplate($template,$vars=false)
|
||||
@@ -275,16 +359,19 @@ function renderSize($byte)
|
||||
|
||||
function getTypeOfFile($url)
|
||||
{
|
||||
$fi = new finfo(FILEINFO_MIME);
|
||||
$type = $fi->buffer(file_get_contents($url, false, null, -1, 1024));
|
||||
|
||||
// on linux use the "file" command or it will handle everything as octet-stream
|
||||
if(strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' && startsWith($type,'application/octet-stream'))
|
||||
if(strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')
|
||||
{
|
||||
$content_type = exec("file -bi " . escapeshellarg($url));
|
||||
if($content_type && $content_type!=$type && strpos($content_type,'/')!==false && strpos($content_type,';')!==false)
|
||||
if($content_type && strpos($content_type,'/')!==false && strpos($content_type,';')!==false)
|
||||
$type = $content_type;
|
||||
}
|
||||
else
|
||||
{
|
||||
//for windows we'll use mime_content_type. Make sure you have enabled the "exif" extension in php.ini
|
||||
$type = mime_content_type($url);
|
||||
}
|
||||
if(!$type) return false;
|
||||
if(startsWith($type,'text')) return 'text';
|
||||
$arr = explode(';', trim($type));
|
||||
if(count($arr)>1)
|
||||
@@ -298,9 +385,11 @@ function getTypeOfFile($url)
|
||||
$type = $a2[1];
|
||||
}
|
||||
|
||||
if($type=='octet-stream' && (new VideoController())->isProperMP4($url)) return 'mp4';
|
||||
if($type=='mp4' && !(new VideoController())->isProperMP4($url))
|
||||
return false;
|
||||
if($type=='octet-stream')
|
||||
if((new VideoController())->isProperMP4($url)) return 'mp4';
|
||||
if($type=='mp4')
|
||||
if(!(new VideoController())->isProperMP4($url))
|
||||
return false;
|
||||
|
||||
return $type;
|
||||
}
|
||||
@@ -330,6 +419,8 @@ function endswith($string, $test) {
|
||||
|
||||
function getUserIP()
|
||||
{
|
||||
if(isCloudflare() || $_SERVER['HTTP_CF_CONNECTING_IP'])
|
||||
return $_SERVER['HTTP_CF_CONNECTING_IP'];
|
||||
$client = @$_SERVER['HTTP_CLIENT_IP'];
|
||||
$forward = @$_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||
$remote = $_SERVER['REMOTE_ADDR'];
|
||||
@@ -357,10 +448,13 @@ function getUserIP()
|
||||
// checks the list of uploaded files for this hash
|
||||
function sha1Exists($sha1)
|
||||
{
|
||||
$handle = fopen(ROOT.DS.'data'.DS.'sha1.csv', "r");
|
||||
$shafile = getDataDir().DS.'sha1.csv';
|
||||
|
||||
if(!file_exists($shafile)) touch($shafile);
|
||||
$handle = fopen($shafile, "r");
|
||||
if ($handle) {
|
||||
while (($line = fgets($handle)) !== false) {
|
||||
if(substr($line,0,40)==$sha1) return trim(substr($line,41));
|
||||
if(substr($line,0,40)===$sha1) return trim(substr($line,41));
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
@@ -372,7 +466,7 @@ function sha1Exists($sha1)
|
||||
function addSha1($hash,$sha1)
|
||||
{
|
||||
if(sha1Exists($sha1)) return;
|
||||
$fp = fopen(ROOT.DS.'data'.DS.'sha1.csv','a');
|
||||
$fp = fopen(getDataDir().DS.'sha1.csv','a');
|
||||
fwrite($fp,"$sha1;$hash\n");
|
||||
fclose($fp);
|
||||
return true;
|
||||
@@ -387,6 +481,176 @@ function isSize($var)
|
||||
return true;
|
||||
}
|
||||
|
||||
function isColor($var)
|
||||
{
|
||||
if(strlen($var)==6 && ctype_xdigit($var)) return true;
|
||||
else
|
||||
{
|
||||
$col = color_name_to_hex($var);
|
||||
if($col) return true;
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
|
||||
function color_name_to_hex($color_name)
|
||||
{
|
||||
// standard 147 HTML color names
|
||||
$colors = array(
|
||||
'aliceblue'=>'F0F8FF',
|
||||
'antiquewhite'=>'FAEBD7',
|
||||
'aqua'=>'00FFFF',
|
||||
'aquamarine'=>'7FFFD4',
|
||||
'azure'=>'F0FFFF',
|
||||
'beige'=>'F5F5DC',
|
||||
'bisque'=>'FFE4C4',
|
||||
'black'=>'000000',
|
||||
'blanchedalmond '=>'FFEBCD',
|
||||
'blue'=>'0000FF',
|
||||
'blueviolet'=>'8A2BE2',
|
||||
'brown'=>'A52A2A',
|
||||
'burlywood'=>'DEB887',
|
||||
'cadetblue'=>'5F9EA0',
|
||||
'chartreuse'=>'7FFF00',
|
||||
'chocolate'=>'D2691E',
|
||||
'coral'=>'FF7F50',
|
||||
'cornflowerblue'=>'6495ED',
|
||||
'cornsilk'=>'FFF8DC',
|
||||
'crimson'=>'DC143C',
|
||||
'cyan'=>'00FFFF',
|
||||
'darkblue'=>'00008B',
|
||||
'darkcyan'=>'008B8B',
|
||||
'darkgoldenrod'=>'B8860B',
|
||||
'darkgray'=>'A9A9A9',
|
||||
'darkgreen'=>'006400',
|
||||
'darkgrey'=>'A9A9A9',
|
||||
'darkkhaki'=>'BDB76B',
|
||||
'darkmagenta'=>'8B008B',
|
||||
'darkolivegreen'=>'556B2F',
|
||||
'darkorange'=>'FF8C00',
|
||||
'darkorchid'=>'9932CC',
|
||||
'darkred'=>'8B0000',
|
||||
'darksalmon'=>'E9967A',
|
||||
'darkseagreen'=>'8FBC8F',
|
||||
'darkslateblue'=>'483D8B',
|
||||
'darkslategray'=>'2F4F4F',
|
||||
'darkslategrey'=>'2F4F4F',
|
||||
'darkturquoise'=>'00CED1',
|
||||
'darkviolet'=>'9400D3',
|
||||
'deeppink'=>'FF1493',
|
||||
'deepskyblue'=>'00BFFF',
|
||||
'dimgray'=>'696969',
|
||||
'dimgrey'=>'696969',
|
||||
'dodgerblue'=>'1E90FF',
|
||||
'firebrick'=>'B22222',
|
||||
'floralwhite'=>'FFFAF0',
|
||||
'forestgreen'=>'228B22',
|
||||
'fuchsia'=>'FF00FF',
|
||||
'gainsboro'=>'DCDCDC',
|
||||
'ghostwhite'=>'F8F8FF',
|
||||
'gold'=>'FFD700',
|
||||
'goldenrod'=>'DAA520',
|
||||
'gray'=>'808080',
|
||||
'green'=>'008000',
|
||||
'greenyellow'=>'ADFF2F',
|
||||
'grey'=>'808080',
|
||||
'honeydew'=>'F0FFF0',
|
||||
'hotpink'=>'FF69B4',
|
||||
'indianred'=>'CD5C5C',
|
||||
'indigo'=>'4B0082',
|
||||
'ivory'=>'FFFFF0',
|
||||
'khaki'=>'F0E68C',
|
||||
'lavender'=>'E6E6FA',
|
||||
'lavenderblush'=>'FFF0F5',
|
||||
'lawngreen'=>'7CFC00',
|
||||
'lemonchiffon'=>'FFFACD',
|
||||
'lightblue'=>'ADD8E6',
|
||||
'lightcoral'=>'F08080',
|
||||
'lightcyan'=>'E0FFFF',
|
||||
'lightgoldenrodyellow'=>'FAFAD2',
|
||||
'lightgray'=>'D3D3D3',
|
||||
'lightgreen'=>'90EE90',
|
||||
'lightgrey'=>'D3D3D3',
|
||||
'lightpink'=>'FFB6C1',
|
||||
'lightsalmon'=>'FFA07A',
|
||||
'lightseagreen'=>'20B2AA',
|
||||
'lightskyblue'=>'87CEFA',
|
||||
'lightslategray'=>'778899',
|
||||
'lightslategrey'=>'778899',
|
||||
'lightsteelblue'=>'B0C4DE',
|
||||
'lightyellow'=>'FFFFE0',
|
||||
'lime'=>'00FF00',
|
||||
'limegreen'=>'32CD32',
|
||||
'linen'=>'FAF0E6',
|
||||
'magenta'=>'FF00FF',
|
||||
'maroon'=>'800000',
|
||||
'mediumaquamarine'=>'66CDAA',
|
||||
'mediumblue'=>'0000CD',
|
||||
'mediumorchid'=>'BA55D3',
|
||||
'mediumpurple'=>'9370D0',
|
||||
'mediumseagreen'=>'3CB371',
|
||||
'mediumslateblue'=>'7B68EE',
|
||||
'mediumspringgreen'=>'00FA9A',
|
||||
'mediumturquoise'=>'48D1CC',
|
||||
'mediumvioletred'=>'C71585',
|
||||
'midnightblue'=>'191970',
|
||||
'mintcream'=>'F5FFFA',
|
||||
'mistyrose'=>'FFE4E1',
|
||||
'moccasin'=>'FFE4B5',
|
||||
'navajowhite'=>'FFDEAD',
|
||||
'navy'=>'000080',
|
||||
'oldlace'=>'FDF5E6',
|
||||
'olive'=>'808000',
|
||||
'olivedrab'=>'6B8E23',
|
||||
'orange'=>'FFA500',
|
||||
'orangered'=>'FF4500',
|
||||
'orchid'=>'DA70D6',
|
||||
'palegoldenrod'=>'EEE8AA',
|
||||
'palegreen'=>'98FB98',
|
||||
'paleturquoise'=>'AFEEEE',
|
||||
'palevioletred'=>'DB7093',
|
||||
'papayawhip'=>'FFEFD5',
|
||||
'peachpuff'=>'FFDAB9',
|
||||
'peru'=>'CD853F',
|
||||
'pink'=>'FFC0CB',
|
||||
'plum'=>'DDA0DD',
|
||||
'powderblue'=>'B0E0E6',
|
||||
'purple'=>'800080',
|
||||
'red'=>'FF0000',
|
||||
'rosybrown'=>'BC8F8F',
|
||||
'royalblue'=>'4169E1',
|
||||
'saddlebrown'=>'8B4513',
|
||||
'salmon'=>'FA8072',
|
||||
'sandybrown'=>'F4A460',
|
||||
'seagreen'=>'2E8B57',
|
||||
'seashell'=>'FFF5EE',
|
||||
'sienna'=>'A0522D',
|
||||
'silver'=>'C0C0C0',
|
||||
'skyblue'=>'87CEEB',
|
||||
'slateblue'=>'6A5ACD',
|
||||
'slategray'=>'708090',
|
||||
'slategrey'=>'708090',
|
||||
'snow'=>'FFFAFA',
|
||||
'springgreen'=>'00FF7F',
|
||||
'steelblue'=>'4682B4',
|
||||
'tan'=>'D2B48C',
|
||||
'teal'=>'008080',
|
||||
'thistle'=>'D8BFD8',
|
||||
'tomato'=>'FF6347',
|
||||
'turquoise'=>'40E0D0',
|
||||
'violet'=>'EE82EE',
|
||||
'wheat'=>'F5DEB3',
|
||||
'white'=>'FFFFFF',
|
||||
'whitesmoke'=>'F5F5F5',
|
||||
'yellow'=>'FFFF00',
|
||||
'yellowgreen'=>'9ACD32');
|
||||
|
||||
$color_name = strtolower($color_name);
|
||||
if (isset($colors[$color_name]))
|
||||
return $colors[$color_name];
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
function getStorageControllers()
|
||||
{
|
||||
$controllers = array();
|
||||
@@ -406,13 +670,18 @@ function getStorageControllers()
|
||||
return $controllers;
|
||||
}
|
||||
|
||||
function getAllContentControllers()
|
||||
function loadAllContentControllers($all=false)
|
||||
{
|
||||
$allowedcontrollers = false;
|
||||
if(defined('CONTENTCONTROLLERS') && CONTENTCONTROLLERS != '' && $all!==true)
|
||||
{
|
||||
$allowedcontrollers = array_map('strtolower', explode(',',CONTENTCONTROLLERS));
|
||||
}
|
||||
$controllers = array();
|
||||
if ($handle = opendir(ROOT.DS.'content-controllers')) {
|
||||
while (false !== ($entry = readdir($handle))) {
|
||||
if ($entry != "." && $entry != "..") {
|
||||
if(is_dir(ROOT.DS.'content-controllers'.DS.$entry) && file_exists(ROOT.DS.'content-controllers'.DS.$entry.DS."$entry.controller.php"))
|
||||
if(is_dir(ROOT.DS.'content-controllers'.DS.$entry) && file_exists(ROOT.DS.'content-controllers'.DS.$entry.DS."$entry.controller.php") && ( ($allowedcontrollers!==false && in_array($entry,$allowedcontrollers) ) || $allowedcontrollers===false))
|
||||
{
|
||||
$controllers[] = ucfirst($entry).'Controller';
|
||||
include_once(ROOT.DS.'content-controllers'.DS.$entry.DS."$entry.controller.php");
|
||||
@@ -428,16 +697,19 @@ function getAllContentControllers()
|
||||
function getAllContentFiletypes()
|
||||
{
|
||||
$types = array();
|
||||
$controllers = getAllContentControllers(true);
|
||||
$controllers = loadAllContentControllers();
|
||||
foreach($controllers as $c)
|
||||
{
|
||||
$types = array_merge($types,(new $c)->getRegisteredExtensions());
|
||||
$instance = new $c;
|
||||
if($instance::ctype=='static')
|
||||
$types = array_merge($types,(new $instance)->getRegisteredExtensions());
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
function rrmdir($dir) {
|
||||
chmod($dir, 0777);
|
||||
if (is_dir($dir)) {
|
||||
$objects = scandir($dir);
|
||||
foreach ($objects as $object) {
|
||||
@@ -445,7 +717,9 @@ function rrmdir($dir) {
|
||||
if (is_dir($dir."/".$object))
|
||||
rrmdir($dir."/".$object);
|
||||
else
|
||||
{
|
||||
unlink($dir."/".$object);
|
||||
}
|
||||
}
|
||||
}
|
||||
rmdir($dir);
|
||||
@@ -454,9 +728,9 @@ function rrmdir($dir) {
|
||||
|
||||
function storeFile($srcfile,$hash,$deleteoriginal=false)
|
||||
{
|
||||
if(is_dir(ROOT.DS.'data'.DS.$hash) && file_exists(ROOT.DS.'data'.DS.$hash.DS.$hash)) return;
|
||||
mkdir(ROOT.DS.'data'.DS.$hash);
|
||||
$file = ROOT.DS.'data'.DS.$hash.DS.$hash;
|
||||
if(is_dir(getDataDir().DS.$hash) && file_exists(getDataDir().DS.$hash.DS.$hash)) return;
|
||||
mkdir(getDataDir().DS.$hash);
|
||||
$file = getDataDir().DS.$hash.DS.$hash;
|
||||
|
||||
copy($srcfile, $file);
|
||||
if($deleteoriginal===true)
|
||||
@@ -466,36 +740,266 @@ function storeFile($srcfile,$hash,$deleteoriginal=false)
|
||||
|
||||
//creating a delete code
|
||||
$deletecode = getRandomString(32);
|
||||
$fh = fopen(ROOT.DS.'data'.DS.$hash.DS.'deletecode', 'w');
|
||||
$fh = fopen(getDataDir().DS.$hash.DS.'deletecode', 'w');
|
||||
fwrite($fh, $deletecode);
|
||||
fclose($fh);
|
||||
|
||||
if(defined('LOG_UPLOADER') && LOG_UPLOADER)
|
||||
{
|
||||
$fh = fopen(ROOT.DS.'data'.DS.'uploads.csv', 'a');
|
||||
fwrite($fh, time().';'.$url.';'.$hash.';'.getUserIP()."\n");
|
||||
$fh = fopen(getDataDir().DS.'uploads.csv', 'a');
|
||||
fwrite($fh, time().';;'.$hash.';'.getUserIP()."\n");
|
||||
fclose($fh);
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
function getDeleteCodeOfHash($hash)
|
||||
{
|
||||
return file_get_contents(ROOT.DS.'data'.DS.$hash.DS.'deletecode');
|
||||
if(file_exists(getDataDir().DS.$hash.DS.'deletecode'))
|
||||
return file_get_contents(getDataDir().DS.$hash.DS.'deletecode');
|
||||
return false;
|
||||
}
|
||||
|
||||
function deleteHash($hash)
|
||||
{
|
||||
//delete all local images
|
||||
rrmdir(ROOT.DS.'data'.DS.$hash);
|
||||
//@todo: add hash to deleted list. also on all controllers
|
||||
|
||||
//delete all files in directory
|
||||
rrmdir(getDataDir().DS.$hash);
|
||||
|
||||
//tell every storage controller to delete theirs as well
|
||||
$sc = getStorageControllers();
|
||||
foreach($sc as $contr)
|
||||
{
|
||||
$c = new $contr();
|
||||
if($c->isEnabled()===true && $c->hashExists($el))
|
||||
if($c->isEnabled()===true && $c->hashExists($hash))
|
||||
{
|
||||
$c->deleteFile($el);
|
||||
$c->deleteFile($hash);
|
||||
}
|
||||
//delete encrypted file if it exists
|
||||
if($c->isEnabled()===true && defined('ENCRYPTION_KEY') && ENCRYPTION_KEY !='' && $c->hashExists($hash.'.enc'))
|
||||
{
|
||||
$c->deleteFile($hash.'.enc');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given IPv4 or IPv6 is in a network
|
||||
* @param string $ip IP to check in IPV4 format eg. 127.0.0.1
|
||||
* @param string $range IP/CIDR netmask eg. 127.0.0.0/24, or 2001:db8::8a2e:370:7334/128
|
||||
* @return boolean true if the ip is in this range / false if not.
|
||||
* via https://stackoverflow.com/a/56050595/1174516
|
||||
*/
|
||||
function isIPInRange( $ip, $range ) {
|
||||
|
||||
if(strpos($range,',')!==false)
|
||||
{
|
||||
// we got a list of ranges. splitting
|
||||
$ranges = array_map('trim',explode(',',$range));
|
||||
foreach($ranges as $range)
|
||||
if(isIPInRange($ip,$range)) return true;
|
||||
return false;
|
||||
}
|
||||
// Get mask bits
|
||||
list($net, $maskBits) = explode('/', $range);
|
||||
|
||||
// Size
|
||||
$size = (strpos($ip, ':') === false) ? 4 : 16;
|
||||
|
||||
// Convert to binary
|
||||
$ip = inet_pton($ip);
|
||||
$net = inet_pton($net);
|
||||
if (!$ip || !$net) {
|
||||
throw new InvalidArgumentException('Invalid IP address');
|
||||
}
|
||||
|
||||
// Build mask
|
||||
$solid = floor($maskBits / 8);
|
||||
$solidBits = $solid * 8;
|
||||
$mask = str_repeat(chr(255), $solid);
|
||||
for ($i = $solidBits; $i < $maskBits; $i += 8) {
|
||||
$bits = max(0, min(8, $maskBits - $i));
|
||||
$mask .= chr((pow(2, $bits) - 1) << (8 - $bits));
|
||||
}
|
||||
$mask = str_pad($mask, $size, chr(0));
|
||||
|
||||
// Compare the mask
|
||||
return ($ip & $mask) === ($net & $mask);
|
||||
}
|
||||
|
||||
function loadContentControllers()
|
||||
{
|
||||
if(defined('CONTENTCONTROLLERS') && CONTENTCONTROLLERS != '')
|
||||
{
|
||||
$controllers = explode(',',CONTENTCONTROLLERS);
|
||||
foreach($controllers as $controller)
|
||||
{
|
||||
$controller = strtolower($controller);
|
||||
if(@file_exists(ROOT . DS . 'content-controllers' . DS. $controller. DS . $controller.'.controller.php'))
|
||||
require_once(ROOT . DS . 'content-controllers' . DS. $controller. DS . $controller.'.controller.php');
|
||||
}
|
||||
}
|
||||
else
|
||||
loadAllContentControllers();
|
||||
}
|
||||
|
||||
function ip_in_range($ip, $range) {
|
||||
if (strpos($range, '/') == false)
|
||||
$range .= '/32';
|
||||
|
||||
// $range is in IP/CIDR format eg 127.0.0.1/24
|
||||
list($range, $netmask) = explode('/', $range, 2);
|
||||
$range_decimal = ip2long($range);
|
||||
$ip_decimal = ip2long($ip);
|
||||
$wildcard_decimal = pow(2, (32 - $netmask)) - 1;
|
||||
$netmask_decimal = ~ $wildcard_decimal;
|
||||
return (($ip_decimal & $netmask_decimal) == ($range_decimal & $netmask_decimal));
|
||||
}
|
||||
|
||||
|
||||
// from https://www.cloudflare.com/ips-v4
|
||||
// and https://www.cloudflare.com/ips-v6
|
||||
function _cloudflare_CheckIP($ip) {
|
||||
$cf_ips = array_filter(array_map('trim',explode("\n","
|
||||
173.245.48.0/20
|
||||
103.21.244.0/22
|
||||
103.22.200.0/22
|
||||
103.31.4.0/22
|
||||
141.101.64.0/18
|
||||
108.162.192.0/18
|
||||
190.93.240.0/20
|
||||
188.114.96.0/20
|
||||
197.234.240.0/22
|
||||
198.41.128.0/17
|
||||
162.158.0.0/15
|
||||
104.16.0.0/13
|
||||
104.24.0.0/14
|
||||
172.64.0.0/13
|
||||
131.0.72.0/22
|
||||
2400:cb00::/32
|
||||
2606:4700::/32
|
||||
2803:f800::/32
|
||||
2405:b500::/32
|
||||
2405:8100::/32
|
||||
2a06:98c0::/29
|
||||
2c0f:f248::/32
|
||||
")));
|
||||
|
||||
$is_cf_ip = false;
|
||||
foreach ($cf_ips as $cf_ip) {
|
||||
if (ip_in_range($ip, $cf_ip)) {
|
||||
$is_cf_ip = true;
|
||||
break;
|
||||
}
|
||||
} return $is_cf_ip;
|
||||
}
|
||||
|
||||
function _cloudflare_Requests_Check() {
|
||||
$flag = true;
|
||||
|
||||
if(!isset($_SERVER['HTTP_CF_CONNECTING_IP'])) $flag = false;
|
||||
if(!isset($_SERVER['HTTP_CF_IPCOUNTRY'])) $flag = false;
|
||||
if(!isset($_SERVER['HTTP_CF_RAY'])) $flag = false;
|
||||
if(!isset($_SERVER['HTTP_CF_VISITOR'])) $flag = false;
|
||||
return $flag;
|
||||
}
|
||||
|
||||
function isCloudflare() {
|
||||
$ipCheck = _cloudflare_CheckIP($_SERVER['REMOTE_ADDR']);
|
||||
$requestCheck = _cloudflare_Requests_Check();
|
||||
return ($ipCheck && $requestCheck);
|
||||
}
|
||||
|
||||
function executeUploadPermission()
|
||||
{
|
||||
if(defined('ALLOWED_SUBNET') && ALLOWED_SUBNET != '' && !isIPInRange( getUserIP(), ALLOWED_SUBNET ))
|
||||
{
|
||||
http_response_code(403);
|
||||
exit(json_encode(array('status'=>'err','reason'=> 'Access denied')));
|
||||
}
|
||||
else if(defined('UPLOAD_CODE') && UPLOAD_CODE!='')
|
||||
{
|
||||
if(!isset($_REQUEST['uploadcode']) || $_REQUEST['uploadcode']!=UPLOAD_CODE)
|
||||
{
|
||||
http_response_code(403);
|
||||
exit(json_encode(array('status'=>'err','reason'=> 'Incorrect upload code specified - Access denied')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a URL is valid
|
||||
* @param string $url
|
||||
* @return boolean (true if valid, false if not)
|
||||
*/
|
||||
function checkURLForPrivateIPRange($url)
|
||||
{
|
||||
$host = getHost($url);
|
||||
$ip = gethostbyname($host);
|
||||
if(is_public_ipv4($ip) || is_public_ipv6($ip)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function getHost($url){
|
||||
$URIs = parse_url(trim($url));
|
||||
$host = !empty($URIs['host'])? $URIs['host'] : explode('/', $URIs['path'])[0];
|
||||
return $host;
|
||||
}
|
||||
|
||||
function is_public_ipv4($ip=NULL)
|
||||
{
|
||||
return filter_var(
|
||||
$ip,
|
||||
FILTER_VALIDATE_IP,
|
||||
FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
|
||||
) === $ip ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
function is_public_ipv6($ip=NULL)
|
||||
{
|
||||
return filter_var(
|
||||
$ip,
|
||||
FILTER_VALIDATE_IP,
|
||||
FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
|
||||
) === $ip ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
function getDataDir()
|
||||
{
|
||||
if(defined('SPLIT_DATA_DIR') && SPLIT_DATA_DIR===true && getDomain() && in_array(getDomain(),explode(',',ALLOWED_DOMAINS)))
|
||||
{
|
||||
$dir = ROOT.DS.'data'.DS.getDomain();
|
||||
if(!is_dir($dir)) mkdir($dir);
|
||||
return $dir;
|
||||
}
|
||||
return ROOT.DS.'data';
|
||||
}
|
||||
|
||||
function getDomain($stripport=true)
|
||||
{
|
||||
$host = $_SERVER['HTTP_HOST'];
|
||||
//strip port
|
||||
if(strpos($host,':')!==false)
|
||||
$strippedhost = substr($host,0,strpos($host,':'));
|
||||
else $strippedhost = $host;
|
||||
|
||||
//check if it's in ALLOWED_DOMAINS
|
||||
if(defined('ALLOWED_DOMAINS') && ALLOWED_DOMAINS!='')
|
||||
{
|
||||
$domains = explode(',',ALLOWED_DOMAINS);
|
||||
if(!in_array($strippedhost,$domains)) //always check without port
|
||||
return false;
|
||||
else return ($stripport ? $strippedhost : $host);
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
function getURL()
|
||||
{
|
||||
if(defined('URL') && URL !='')
|
||||
return URL;
|
||||
$protocol = strpos(strtolower($_SERVER['SERVER_PROTOCOL']), 'https') === FALSE ? 'http' : 'https';
|
||||
return $protocol . '://' . getDomain(false).'/';
|
||||
}
|
||||
|
||||
85
inc/encryption.php
Normal file
85
inc/encryption.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
class Encryption{
|
||||
|
||||
/**
|
||||
* $key must have been generated at some point with: random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES)
|
||||
*/
|
||||
function encryptText($text,$key)
|
||||
{
|
||||
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
|
||||
$ciphertext = sodium_crypto_secretbox($text, $nonce, $key);
|
||||
$encoded = base64_encode($nonce . $ciphertext);
|
||||
return $encoded;
|
||||
}
|
||||
|
||||
/**
|
||||
* $key must have been generated at some point with: random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES)
|
||||
*/
|
||||
function decryptText($encoded,$key)
|
||||
{
|
||||
$decoded = base64_decode($encoded);
|
||||
$nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
|
||||
|
||||
|
||||
$ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
|
||||
$plaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
|
||||
return $plaintext;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* $key must have been generated at some point with: sodium_crypto_secretstream_xchacha20poly1305_keygen();
|
||||
*/
|
||||
function encryptFile($infile,$enc_outfile,$key)
|
||||
{
|
||||
$chunk_size = 4096;
|
||||
|
||||
$fd_in = fopen($infile, 'rb');
|
||||
$fd_out = fopen($enc_outfile, 'wb');
|
||||
|
||||
list($stream, $header) = sodium_crypto_secretstream_xchacha20poly1305_init_push($key);
|
||||
|
||||
fwrite($fd_out, $header);
|
||||
|
||||
$tag = SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_MESSAGE;
|
||||
do {
|
||||
$chunk = fread($fd_in, $chunk_size);
|
||||
if (feof($fd_in)) {
|
||||
$tag = SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL;
|
||||
}
|
||||
$encrypted_chunk = sodium_crypto_secretstream_xchacha20poly1305_push($stream, $chunk, '', $tag);
|
||||
fwrite($fd_out, $encrypted_chunk);
|
||||
} while ($tag !== SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL);
|
||||
|
||||
fclose($fd_out);
|
||||
fclose($fd_in);
|
||||
}
|
||||
|
||||
/**
|
||||
* $key must have been generated at some point with: sodium_crypto_secretstream_xchacha20poly1305_keygen();
|
||||
*/
|
||||
function decryptFile($enc_infile,$outfile,$key)
|
||||
{
|
||||
$fd_in = fopen($enc_infile, 'rb');
|
||||
$fd_out = fopen($outfile, 'wb');
|
||||
$chunk_size = 4096;
|
||||
|
||||
$header = fread($fd_in, SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES);
|
||||
|
||||
$stream = sodium_crypto_secretstream_xchacha20poly1305_init_pull($header, $key);
|
||||
do {
|
||||
$chunk = fread($fd_in, $chunk_size + SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES);
|
||||
list($decrypted_chunk, $tag) = sodium_crypto_secretstream_xchacha20poly1305_pull($stream, $chunk);
|
||||
fwrite($fd_out, $decrypted_chunk);
|
||||
} while (!feof($fd_in) && $tag !== SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL);
|
||||
$ok = feof($fd_in);
|
||||
|
||||
fclose($fd_out);
|
||||
fclose($fd_in);
|
||||
|
||||
if (!$ok) {
|
||||
die('Invalid/corrupted input');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,9 @@
|
||||
/**
|
||||
* All settings that are uncommented are mandatory
|
||||
* Others optional
|
||||
*
|
||||
* For all possible config options, check https://github.com/HaschekSolutions/pictshare/blob/master/rtfm/CONFIG.md
|
||||
* or /rtfm/CONFIG.md in your installation
|
||||
*/
|
||||
|
||||
//Use a specific domain for links presented to the user
|
||||
@@ -11,4 +14,12 @@ define('URL','https://dev.pictshare.net/');
|
||||
|
||||
//define('JPEG_COMPRESSION', 90);
|
||||
//define('FFMPEG_BINARY','');
|
||||
//define('ALT_FOLDER','/ftp/pictshare');
|
||||
//define('ALT_FOLDER','/ftp/pictshare');
|
||||
//define('ALLOWED_SUBNET','192.168.0.0/24');
|
||||
|
||||
//S3 settings
|
||||
//
|
||||
//define('S3_BUCKET','bucketname');
|
||||
//define('S3_ACCESS_KEY','');
|
||||
//define('S3_SECRET_KEY','');
|
||||
//define('S3_ENDPOINT','http://localhost:9000'); //optional, only if you're using S3 compatible storage like Minio
|
||||
11
index.php
11
index.php
@@ -10,12 +10,13 @@ include_once(ROOT.DS.'inc'.DS.'config.inc.php');
|
||||
|
||||
//loading core and controllers
|
||||
include_once(ROOT.DS.'inc'.DS.'core.php');
|
||||
require_once(ROOT . DS . 'content-controllers' . DS. 'image'. DS . 'image.controller.php');
|
||||
require_once(ROOT . DS . 'content-controllers' . DS. 'text'. DS . 'text.controller.php');
|
||||
require_once(ROOT . DS . 'content-controllers' . DS. 'url'. DS . 'url.controller.php');
|
||||
require_once(ROOT . DS . 'content-controllers' . DS. 'video'. DS . 'video.controller.php');
|
||||
loadAllContentControllers();
|
||||
|
||||
//load external things if existing
|
||||
if(file_exists(ROOT.'/lib/vendor/autoload.php'))
|
||||
require ROOT.'/lib/vendor/autoload.php';
|
||||
|
||||
|
||||
//send the URL to the architect. It'll know what to do
|
||||
$url = $_GET['url'];
|
||||
$url = $_GET['url']?$_GET['url']:ltrim($_SERVER['REQUEST_URI'],'/');
|
||||
architect($url);
|
||||
@@ -6,6 +6,14 @@
|
||||
|
||||
interface ContentController
|
||||
{
|
||||
/**
|
||||
* When implementing a new content controller, you must specify a constant with the name of "ctype"
|
||||
* This constant will be used to distinguish static controllers (which have files on disk) from dynamic ones (which don't, like the placeholder controller)
|
||||
*
|
||||
* Possible values: 'static' or 'dynamic'
|
||||
*/
|
||||
//const ctype = 'static';
|
||||
|
||||
/** This method will return all file extensions that will be associated with this content type
|
||||
* for example 'pdf' but it could be anything really. You just need a way later to confirm that a type is what it says it is
|
||||
*
|
||||
|
||||
@@ -26,6 +26,14 @@ interface StorageController
|
||||
*/
|
||||
function hashExists($hash);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of all items in this storage controller
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function getItems();
|
||||
|
||||
/**
|
||||
* If a file does exist in this storage system, then this method should
|
||||
* get the file and put it in the default data directory
|
||||
@@ -34,20 +42,21 @@ interface StorageController
|
||||
* a folder that you might have to create first before putting the file in
|
||||
*
|
||||
* @param string $hash is the hash of the file that should be pulled from this storage system
|
||||
* @param string $location is the location where the downloaded file should be placed
|
||||
*
|
||||
* @return bool true if successful
|
||||
*/
|
||||
function pullFile($hash);
|
||||
function pullFile($hash,$location);
|
||||
|
||||
/**
|
||||
* Whenever a new file is uploaded this method will be called
|
||||
* You should then upload it or do whatever your storage system is meant to do with new files
|
||||
*
|
||||
* @param string $hash is the hash of the new file. The file path of this file is always ROOT.DS.'data'.DS.$hash.DS.$hash
|
||||
* @param string $hash is the hash of the new file. The file path of this file is always getDataDir().DS.$hash.DS.$hash
|
||||
*
|
||||
* @return bool true if successful
|
||||
*/
|
||||
function pushFile($hash);
|
||||
function pushFile($source,$hash);
|
||||
|
||||
/**
|
||||
* If deletion of a file is requested, this method is called
|
||||
|
||||
@@ -1,19 +1,40 @@
|
||||
Dropzone.autoDiscover = false;
|
||||
|
||||
$(function() {
|
||||
$(function () {
|
||||
var myDropzone = new Dropzone("#dropzone");
|
||||
myDropzone.on("success", function(file,response) {
|
||||
console.log("raw response: "+response);
|
||||
if(response==null || response =="null")
|
||||
$("#uploadinfo").append("<div class='alert alert-danger' role='alert'><strong>Error uploading "+file.name+"</strong><br/>Reason is unknown :(</div>")
|
||||
else
|
||||
{
|
||||
var o = JSON.parse(response);
|
||||
if(o.status=='ok')
|
||||
$("#uploadinfo").append("<div class='alert alert-success' role='alert'><strong>"+file.name+"</strong> uploaded as <a target='_blank' href='/"+o.hash+"'>"+o.hash+"</a><br/>URL: <a target='_blank' href='"+o.url+"'>"+o.url+"</a></div>")
|
||||
else if(o.status=='err')
|
||||
$("#uploadinfo").append("<div class='alert alert-danger' role='alert'><strong>Error uploading "+file.name+"</strong><br/>Reason: "+o.reason+"</div>")
|
||||
console.log(o)
|
||||
//console.log(myDropzone.options);
|
||||
if (maxUploadFileSize !== undefined)
|
||||
myDropzone.options.maxFilesize = maxUploadFileSize;
|
||||
myDropzone.options.timeout = 0,
|
||||
myDropzone.on("sending", function(file, xhr, formData) {
|
||||
if(document.getElementById("uploadcode"))
|
||||
formData.append("uploadcode", document.getElementById("uploadcode").value);
|
||||
});
|
||||
myDropzone.on('error', function(file, response) {
|
||||
alert("Error: "+response.reason);
|
||||
});
|
||||
myDropzone.on("success", function (file, response) {
|
||||
console.log("raw response: " + response);
|
||||
if (response == null || response == "null")
|
||||
$("#uploadinfo").append("<div class='alert alert-danger' role='alert'><strong>Error uploading " + file.name + "</strong><br/>Reason is unknown :(</div>")
|
||||
else {
|
||||
var o = response;
|
||||
if (o.status == 'ok')
|
||||
$("#uploadinfo").append("<div class='alert alert-success' role='alert'><strong>" + file.name + "</strong> uploaded as <a target='_blank' href='/" + o.hash + "'>" + o.hash + "</a><br/>URL: <a target='_blank' href='" + o.url + "'>" + o.url + "</a> <button class='btn btn-xs' onClick='navigator.clipboard.writeText(\"" + o.url + "\");'>Copy URL</button></div>")
|
||||
else if (o.status == 'err')
|
||||
$("#uploadinfo").append("<div class='alert alert-danger' role='alert'><strong>Error uploading " + file.name + "</strong><br/>Reason: " + o.reason + "</div>")
|
||||
console.log(o)
|
||||
}
|
||||
});
|
||||
|
||||
document.onpaste = function (event) {
|
||||
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
|
||||
for (index in items) {
|
||||
var item = items[index];
|
||||
if (item.kind === 'file') {
|
||||
// adds the file to your dropzone instance
|
||||
myDropzone.addFile(item.getAsFile())
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
})
|
||||
6
lib/composer.json
Normal file
6
lib/composer.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"require": {
|
||||
"aws/aws-sdk-php": "^3.33",
|
||||
"bitverse/identicon": "^1.1"
|
||||
}
|
||||
}
|
||||
939
lib/composer.lock
generated
Normal file
939
lib/composer.lock
generated
Normal file
@@ -0,0 +1,939 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "55774a7c38a82a511932ca4321acb7f8",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aws/aws-sdk-php",
|
||||
"version": "3.33.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||
"reference": "586a9c0e52664cfc4d2d91f9dcdf3ea48d143162"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/586a9c0e52664cfc4d2d91f9dcdf3ea48d143162",
|
||||
"reference": "586a9c0e52664cfc4d2d91f9dcdf3ea48d143162",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"guzzlehttp/guzzle": "^5.3.1|^6.2.1",
|
||||
"guzzlehttp/promises": "~1.0",
|
||||
"guzzlehttp/psr7": "^1.4.1",
|
||||
"mtdowling/jmespath.php": "~2.2",
|
||||
"php": ">=5.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"andrewsville/php-token-reflection": "^1.4",
|
||||
"aws/aws-php-sns-message-validator": "~1.0",
|
||||
"behat/behat": "~3.0",
|
||||
"doctrine/cache": "~1.4",
|
||||
"ext-dom": "*",
|
||||
"ext-json": "*",
|
||||
"ext-openssl": "*",
|
||||
"ext-pcre": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-spl": "*",
|
||||
"nette/neon": "^2.3",
|
||||
"phpunit/phpunit": "^4.8.35|^5.4.0",
|
||||
"psr/cache": "^1.0"
|
||||
},
|
||||
"suggest": {
|
||||
"aws/aws-php-sns-message-validator": "To validate incoming SNS notifications",
|
||||
"doctrine/cache": "To use the DoctrineCacheAdapter",
|
||||
"ext-curl": "To send requests using cURL",
|
||||
"ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Aws\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Amazon Web Services",
|
||||
"homepage": "http://aws.amazon.com"
|
||||
}
|
||||
],
|
||||
"description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project",
|
||||
"homepage": "http://aws.amazon.com/sdkforphp",
|
||||
"keywords": [
|
||||
"amazon",
|
||||
"aws",
|
||||
"cloud",
|
||||
"dynamodb",
|
||||
"ec2",
|
||||
"glacier",
|
||||
"s3",
|
||||
"sdk"
|
||||
],
|
||||
"support": {
|
||||
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
|
||||
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
||||
"source": "https://github.com/aws/aws-sdk-php/tree/master"
|
||||
},
|
||||
"time": "2017-08-21T20:34:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "bitverse/identicon",
|
||||
"version": "1.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/bitverseio/identicon.git",
|
||||
"reference": "65a50a5a8bd86b3591795937f9652b2e9075626c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/bitverseio/identicon/zipball/65a50a5a8bd86b3591795937f9652b2e9075626c",
|
||||
"reference": "65a50a5a8bd86b3591795937f9652b2e9075626c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Kuba Birecki",
|
||||
"email": "kuba.birecki@bitverse.io"
|
||||
}
|
||||
],
|
||||
"description": "A PHP library for generating identicons.",
|
||||
"support": {
|
||||
"issues": "https://github.com/bitverseio/identicon/issues",
|
||||
"source": "https://github.com/bitverseio/identicon/tree/master"
|
||||
},
|
||||
"time": "2015-11-01T21:19:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
"version": "6.5.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/guzzle.git",
|
||||
"reference": "a52f0440530b54fa079ce76e8c5d196a42cad981"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/a52f0440530b54fa079ce76e8c5d196a42cad981",
|
||||
"reference": "a52f0440530b54fa079ce76e8c5d196a42cad981",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/promises": "^1.0",
|
||||
"guzzlehttp/psr7": "^1.9",
|
||||
"php": ">=5.5",
|
||||
"symfony/polyfill-intl-idn": "^1.17"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-curl": "*",
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
|
||||
"psr/log": "^1.1"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log": "Required for using the Log middleware"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "6.5-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
},
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
},
|
||||
{
|
||||
"name": "Jeremy Lindblom",
|
||||
"email": "jeremeamia@gmail.com",
|
||||
"homepage": "https://github.com/jeremeamia"
|
||||
},
|
||||
{
|
||||
"name": "George Mponos",
|
||||
"email": "gmponos@gmail.com",
|
||||
"homepage": "https://github.com/gmponos"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com",
|
||||
"homepage": "https://github.com/Nyholm"
|
||||
},
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com",
|
||||
"homepage": "https://github.com/sagikazarmark"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Schultze",
|
||||
"email": "webmaster@tubo-world.de",
|
||||
"homepage": "https://github.com/Tobion"
|
||||
}
|
||||
],
|
||||
"description": "Guzzle is a PHP HTTP client library",
|
||||
"homepage": "http://guzzlephp.org/",
|
||||
"keywords": [
|
||||
"client",
|
||||
"curl",
|
||||
"framework",
|
||||
"http",
|
||||
"http client",
|
||||
"rest",
|
||||
"web service"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/guzzle/issues",
|
||||
"source": "https://github.com/guzzle/guzzle/tree/6.5.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/Nyholm",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-06-20T22:16:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/promises",
|
||||
"version": "1.5.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/promises.git",
|
||||
"reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/promises/zipball/67ab6e18aaa14d753cc148911d273f6e6cb6721e",
|
||||
"reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "^4.4 || ^5.1"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Promise\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
},
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com",
|
||||
"homepage": "https://github.com/Nyholm"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Schultze",
|
||||
"email": "webmaster@tubo-world.de",
|
||||
"homepage": "https://github.com/Tobion"
|
||||
}
|
||||
],
|
||||
"description": "Guzzle promises library",
|
||||
"keywords": [
|
||||
"promise"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/promises/issues",
|
||||
"source": "https://github.com/guzzle/promises/tree/1.5.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/Nyholm",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-05-21T12:31:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
"version": "1.9.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/psr7.git",
|
||||
"reference": "e4490cabc77465aaee90b20cfc9a770f8c04be6b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/e4490cabc77465aaee90b20cfc9a770f8c04be6b",
|
||||
"reference": "e4490cabc77465aaee90b20cfc9a770f8c04be6b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4.0",
|
||||
"psr/http-message": "~1.0",
|
||||
"ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-message-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-zlib": "*",
|
||||
"phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10"
|
||||
},
|
||||
"suggest": {
|
||||
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Psr7\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
},
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
},
|
||||
{
|
||||
"name": "George Mponos",
|
||||
"email": "gmponos@gmail.com",
|
||||
"homepage": "https://github.com/gmponos"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com",
|
||||
"homepage": "https://github.com/Nyholm"
|
||||
},
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com",
|
||||
"homepage": "https://github.com/sagikazarmark"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Schultze",
|
||||
"email": "webmaster@tubo-world.de",
|
||||
"homepage": "https://github.com/Tobion"
|
||||
}
|
||||
],
|
||||
"description": "PSR-7 message implementation that also provides common utility methods",
|
||||
"keywords": [
|
||||
"http",
|
||||
"message",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response",
|
||||
"stream",
|
||||
"uri",
|
||||
"url"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/psr7/issues",
|
||||
"source": "https://github.com/guzzle/psr7/tree/1.9.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/Nyholm",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-04-17T16:00:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mtdowling/jmespath.php",
|
||||
"version": "2.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/jmespath/jmespath.php.git",
|
||||
"reference": "bbb69a935c2cbb0c03d7f481a238027430f6440b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/bbb69a935c2cbb0c03d7f481a238027430f6440b",
|
||||
"reference": "bbb69a935c2cbb0c03d7f481a238027430f6440b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.5 || ^8.0",
|
||||
"symfony/polyfill-mbstring": "^1.17"
|
||||
},
|
||||
"require-dev": {
|
||||
"composer/xdebug-handler": "^3.0.3",
|
||||
"phpunit/phpunit": "^8.5.33"
|
||||
},
|
||||
"bin": [
|
||||
"bin/jp.php"
|
||||
],
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.7-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/JmesPath.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"JmesPath\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
},
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
}
|
||||
],
|
||||
"description": "Declaratively specify how to extract elements from a JSON document",
|
||||
"keywords": [
|
||||
"json",
|
||||
"jsonpath"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/jmespath/jmespath.php/issues",
|
||||
"source": "https://github.com/jmespath/jmespath.php/tree/2.7.0"
|
||||
},
|
||||
"time": "2023-08-25T10:54:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
"version": "1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-message.git",
|
||||
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
|
||||
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP messages",
|
||||
"homepage": "https://github.com/php-fig/http-message",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-message",
|
||||
"psr",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-message/tree/1.1"
|
||||
},
|
||||
"time": "2023-04-04T09:50:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ralouphie/getallheaders",
|
||||
"version": "3.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ralouphie/getallheaders.git",
|
||||
"reference": "120b605dfeb996808c31b6477290a714d356e822"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
|
||||
"reference": "120b605dfeb996808c31b6477290a714d356e822",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^2.1",
|
||||
"phpunit/phpunit": "^5 || ^6.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/getallheaders.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ralph Khattar",
|
||||
"email": "ralph.khattar@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "A polyfill for getallheaders.",
|
||||
"support": {
|
||||
"issues": "https://github.com/ralouphie/getallheaders/issues",
|
||||
"source": "https://github.com/ralouphie/getallheaders/tree/develop"
|
||||
},
|
||||
"time": "2019-03-08T08:55:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-idn",
|
||||
"version": "v1.28.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-idn.git",
|
||||
"reference": "ecaafce9f77234a6a449d29e49267ba10499116d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/ecaafce9f77234a6a449d29e49267ba10499116d",
|
||||
"reference": "ecaafce9f77234a6a449d29e49267ba10499116d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"symfony/polyfill-intl-normalizer": "^1.10",
|
||||
"symfony/polyfill-php72": "^1.10"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-intl": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.28-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Intl\\Idn\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Laurent Bassin",
|
||||
"email": "laurent@bassin.info"
|
||||
},
|
||||
{
|
||||
"name": "Trevor Rowbotham",
|
||||
"email": "trevor.rowbotham@pm.me"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"idn",
|
||||
"intl",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.28.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-01-26T09:30:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-normalizer",
|
||||
"version": "v1.28.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
||||
"reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
|
||||
"reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-intl": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.28-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
|
||||
},
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for intl's Normalizer class and related functions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"intl",
|
||||
"normalizer",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-01-26T09:26:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.28.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "42292d99c55abe617799667f454222c54c60e229"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229",
|
||||
"reference": "42292d99c55abe617799667f454222c54c60e229",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"provide": {
|
||||
"ext-mbstring": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.28-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for the Mbstring extension",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"mbstring",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-07-28T09:04:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php72",
|
||||
"version": "v1.28.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php72.git",
|
||||
"reference": "70f4aebd92afca2f865444d30a4d2151c13c3179"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/70f4aebd92afca2f865444d30a4d2151c13c3179",
|
||||
"reference": "70f4aebd92afca2f865444d30a4d2151c13c3179",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.28-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php72\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php72/tree/v1.28.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-01-26T09:26:14+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.2.0"
|
||||
}
|
||||
164
rtfm/API.md
164
rtfm/API.md
@@ -1,6 +1,6 @@
|
||||
# API
|
||||
|
||||
## upload.php
|
||||
# upload.php
|
||||
|
||||
- URL https://pictshare.net/api/upload.php
|
||||
- Method: POST file
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
If the upload was successful answer will look like this
|
||||
|
||||
```
|
||||
```json
|
||||
{
|
||||
"status":"ok",
|
||||
"hash":"y1b6hr.jpg",
|
||||
@@ -19,7 +19,7 @@ If the upload was successful answer will look like this
|
||||
|
||||
If there is an error the server will answer with status:err and a reason
|
||||
|
||||
```
|
||||
```json
|
||||
{
|
||||
"status":"err",
|
||||
"reason":"Unsupported filetype"
|
||||
@@ -33,18 +33,114 @@ If there is an error the server will answer with status:err and a reason
|
||||
```curl -F "file=@test.jpg" https://pictshare.net/api/upload.php```
|
||||
|
||||
Answer from the server:
|
||||
```{"status":"ok","hash":"y1b6hr.jpg","url":"https://pictshare.net/y1b6hr.jpg"}```
|
||||
```json
|
||||
{"status":"ok","hash":"y1b6hr.jpg","url":"https://pictshare.net/y1b6hr.jpg"}
|
||||
```
|
||||
|
||||
2. Uploading a file called test.jpg via curl and requesting a custom hash
|
||||
2. Uploading from the commandline using alias, requires `jq` package for json response decoding
|
||||
|
||||
```curl -F "file=@test.jpg" -F "hash=helloworld.jpg" https://pictshare.net/api/upload.php```
|
||||
Put this in your `.bashrc` or `.zshrc`:
|
||||
```
|
||||
pict () {
|
||||
curl -s -F "file=@${1:--}" https://pictshare.net/api/upload.php | jq -r '.url';
|
||||
}
|
||||
```
|
||||
|
||||
Usage:
|
||||
```
|
||||
$ cat path/to/image.jpg | pict
|
||||
```
|
||||
|
||||
Repsonse:
|
||||
```
|
||||
https://pictshare.net/y1b6hr.jpg
|
||||
```
|
||||
|
||||
# geturl.php
|
||||
|
||||
- URL https://pictshare.net/api/geturl.php
|
||||
- Method: GET
|
||||
- Var name: url
|
||||
- Answer type: JSON
|
||||
|
||||
Upload content by providing a link to the content. If the link points to a website, the HTML of the page is uploaded as a text bin.
|
||||
|
||||
```json
|
||||
{
|
||||
"status":"ok",
|
||||
"hash":"y1b6hr.jpg",
|
||||
"url":"https://pictshare.net/y1b6hr.jpg",
|
||||
"delete_code": "aqxqlv3kqokxd15xpkqp8zjljpqerveu",
|
||||
"delete_url": "https://pictshare.net/delete_aqxqlv3kqokxd15xpkqp8zjljpqerveu/2mr2va.txt"
|
||||
}
|
||||
```
|
||||
|
||||
If there is an error the server will answer with status:err and a reason
|
||||
|
||||
```json
|
||||
{
|
||||
"status":"err",
|
||||
"reason":"Unsupported filetype"
|
||||
}
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
1. Uploading the HTML of xkcd.com
|
||||
|
||||
```curl -s https://pictshare.net/api/geturl.php?url=https://xkcd.com```
|
||||
|
||||
Answer from the server:
|
||||
```{"status":"ok","hash":"helloworld.jpg","url":"https://pictshare.net/helloworld.jpg"}```
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"hash": "2mr2va.txt",
|
||||
"url": "https://pictshare.net/2mr2va.txt",
|
||||
"filetype": "text",
|
||||
"delete_code": "aqxqlv3kqokxd15xpkqp8zjljpqerveu",
|
||||
"delete_url": "https://pictshare.net/delete_aqxqlv3kqokxd15xpkqp8zjljpqerveu/2mr2va.txt"
|
||||
}
|
||||
```
|
||||
|
||||
2. Uploading a Video from Imgur
|
||||
|
||||
```curl https://pictshare.net/api/geturl.php?url=https://i.imgur.com/qQstLQt.mp4```
|
||||
|
||||
Answer from the server:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"hash": "u0ni1m.mp4",
|
||||
"url": "https://pictshare.net/u0ni1m.mp4",
|
||||
"filetype": "mp4",
|
||||
"delete_code": "aqxqlv3kqokxd15xpkqp8zjljpqerveu",
|
||||
"delete_url": "https://pictshare.net/delete_aqxqlv3kqokxd15xpkqp8zjljpqerveu/u0ni1m.mp4"
|
||||
}
|
||||
```
|
||||
|
||||
3. Uploading from the commandline using alias, requires `jq` package for json response decoding
|
||||
|
||||
Put this in your `.bashrc` or `.zshrc`:
|
||||
```
|
||||
pictget () {
|
||||
curl -s "hhttps://pictshare.net/api/geturl.php?url=$1" | jq -r '.url';
|
||||
}
|
||||
```
|
||||
|
||||
Usage:
|
||||
```
|
||||
$ pictget https://i.imgur.com/qQstLQt.mp4
|
||||
```
|
||||
|
||||
Repsonse:
|
||||
```
|
||||
https://pictshare.net/u0ni1m.mp4
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## pasetebin.php
|
||||
# pasetebin.php
|
||||
- URL https://pictshare.net/api/pastebin.php
|
||||
- Method: POST/GET text
|
||||
- Post var name: api_paste_code
|
||||
@@ -56,7 +152,55 @@ This API can be used to directly post text. Server responds with the URL to the
|
||||
|
||||
Creating a new text bin that ready "Hello World"
|
||||
|
||||
```url -F "api_paste_code=Hello World" https://pictshare.net/api/pastebin.php```
|
||||
```curl -F "api_paste_code=Hello World" https://pictshare.net/api/pastebin.php```
|
||||
|
||||
Answer from the server:
|
||||
```https://pictshare.net/vekjy4e5rr.txt```
|
||||
```https://pictshare.net/vekjy4e5rr.txt```
|
||||
|
||||
# info.php
|
||||
- URL https://pictshare.net/api/info.php
|
||||
- Method: POST/GET text
|
||||
- Query var name: hash
|
||||
- Answer: JSON
|
||||
|
||||
This API will get information about any given hash.
|
||||
|
||||
## Example
|
||||
|
||||
```curl https://pictshare.net/api/info.php?hash=9k3rbw.mp4```
|
||||
|
||||
Answer from the server:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"hash": "9k3rbw.mp4",
|
||||
"size_bytes": 2513225,
|
||||
"size_interpreted": "2.4 MB",
|
||||
"type": "video/mp4",
|
||||
"type_interpreted": "mp4"
|
||||
}
|
||||
```
|
||||
|
||||
# base64.php
|
||||
- URL https://pictshare.net/api/base64.php
|
||||
- Method: POST/GET
|
||||
- Query var name: base64
|
||||
- Answer: JSON
|
||||
|
||||
## Example
|
||||
|
||||
Upload local image "test.jpg" to pictshare
|
||||
|
||||
```(echo -n "base64="; echo -n "data:image/jpeg;base64,$(base64 -w 0 test.jpg)") | curl --data @- https://pictshare.net/api/base64.php```
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"hash": "lpl119.jpg",
|
||||
"url": "https://dev.pictshare.net/lpl119.jpg",
|
||||
"filetype": "jpeg",
|
||||
"delete_code": "z0e1mdo8szxnauspxp2f080e4wd4ycf2",
|
||||
"delete_url": "https://dev.pictshare.net/delete_z0e1mdo8szxnauspxp2f080e4wd4ycf2/lpl119.jpg"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
# Configuration
|
||||
|
||||
PictShare can be configured using a single file: `inc/config.inc.php`
|
||||
|
||||
In this file you can set the following options. For a simple working example config file check out [/inc/example.config.inc.php](/inc/example.config.inc.php)
|
||||
|
||||
# Config options
|
||||
|
||||
|Option | value type | What it does|
|
||||
|--- | --- | ---|
|
||||
| URL | string | Sets the URL that will be shown to users for each upload. Must be set and must have tailing slash. eg: http://pictshare.local/ |
|
||||
| ALT_FOLDER | string | All uploaded files will be copied to this location. This location can be a mounted network share (eg NFS or FTP, etc). If a file is not found in the normal upload direcotry, ALT_FOLDER will be checked. [more info about scaling PictShare](/rtfm/SCALING.md) |
|
||||
| LOG_UPLOADER | bool | If set to true, all IP addresses of uploaders will be stored in /data/uploads.csv |
|
||||
| FFMPEG_BINARY | string | If you installed ffmpeg on your machine, you can set the binary path here. This allows devices like the Raspberry Pi to be used with PictShare although I wouldn't recommend it because of the sloooooow conversion speed |
|
||||
| PNG_COMPRESSION | int | 0 (no compression) to 9 (best compression) Note that for PNGs the compression doesn't affect the quality of the image, just the en/decode speed and file size |
|
||||
@@ -12,6 +17,80 @@
|
||||
| MASTER_DELETE_CODE | string | If set, this code will be accepted to delete any image by adding "delete_yourmasterdeletecode" to any image |
|
||||
| MASTER_DELETE_IP | IP addr | If set, allows deletion of image no matter what delete code you provided if request is coming from this single IP |
|
||||
| UPLOAD_FORM_LOCATION | string | If set, will only show the upload form if this url is requested. eg if you set it to /secret/upload then you only see the form if you go to http://your.pictshare.server/secret/upload but bare in mind that the uploads [via API](/rtfm/API.md) will still work for anyone|
|
||||
| ALLOWED_SUBNET | IPv4 or IPv6 CIDR | If set, will limit uploads to IPs that match this CIDR |
|
||||
| ALWAYS_WEBP | bool | If set to `true`, JPGs will always be served as WebP, if the client supports it (if `image/webp` is in header `HTTP_ACCEPT`) |
|
||||
| UPLOAD_CODE | string | If set, all uploads require this code via GET or POST variable "uploadcode" to succeed |
|
||||
| REDIS_SERVER (NOT IMPLEMENTED) | IP | If you define a REDIS server IP here, it will enable you to use the FFMPEG Worker |
|
||||
| UPLOAD_QUOTA (NOT IMPLEMENTED) | int | Size in MB. If set, will only allow uploads if combined size of uploads on Server is smaller than this value. Does not account for ALT_FOLDER data and resized versions of original uploads won't be added to calculation |
|
||||
| UPLOAD_CODE (NOT IMPLEMENTED | string | If set, all uploads require this code via GET or POST variable "uploadcode" or upload will fail |
|
||||
| MAX_RESIZED_IMAGES (NOT IMPLEMENTED | string | If set, limits count of resized images/videos per file on server |
|
||||
| MAX_RESIZED_IMAGES (NOT IMPLEMENTED | string | If set, limits count of resized images/videos per file on server |
|
||||
|
||||
|
||||
# Content controllers
|
||||
PictShare is not limited to handling just images. Various content types including txt,mp4 and even url shortenings are supported.
|
||||
By default all of these are enabled but if you only need one or more, you can whitelist them and all others won't be accessible.
|
||||
|
||||
|Option | value type | What it does|
|
||||
|--- | --- | ---|
|
||||
| CONTENTCONTROLLERS | CSV string | If set, will whitelist content controllers for your instance. Must be uppercase and can be comma separated. Example: Only Pictures: `IMAGE`, Pictures and Videos: `IMAGE,VIDEO` |
|
||||
|
||||
Available values for the `CONTENTCONTROLLERS` setting are:
|
||||
|
||||
- IMAGE
|
||||
- TEXT
|
||||
- VIDEO
|
||||
- URL
|
||||
|
||||
# Storage controllers
|
||||
|
||||
PictShare has an extention system that allows handling of multiple storage solutions or backends. If a requested file is not found locally, PictShare will ask all configured storage controllers if they have it, then download and serve it to the user.
|
||||
|
||||
If you want data on your external storage to be **encrypted**, you can set the following config setting. En/decryption is done automatically on up/download.
|
||||
|
||||
|Option | value type | What it does|
|
||||
|--- | --- | ---|
|
||||
|ENCRYPTION_KEY | base64 string | The key used to encrypt/decrypt files stored in storage controllers. See [/rtfm/ENCRYPTION.md](/rtfm/ENCRYPTION.md) for setup guide |
|
||||
|
||||
|
||||
### Alternative Folder
|
||||
|
||||
The ALT_FOLDER option will copy every uploaded file from PictShare to a local path of your choice. This can be used to allow two instances of PictShare to serve the same data. Eg. you can mount a NFS share on your server and configure the ALT_FOLDER variable to point to that folder. All images are then stored on the NFS as well as your PictShare server.
|
||||
|
||||
|Option | value type | What it does|
|
||||
|--- | --- | ---|
|
||||
| ALT_FOLDER | string | All uploaded files will be copied to this location. This location can be a mounted network share (eg NFS or FTP, etc). If a file is not found in the normal upload direcotry, ALT_FOLDER will be checked. [more info about scaling PictShare](/rtfm/SCALING.md) |
|
||||
|
||||
|
||||
### S3 (compatible) storage
|
||||
|
||||
You can also store all uploaded files on S3 or S3 compatible storage like [Minio](https://min.io/). This can also be used to scale your PictShare instance and have multiple distributed servers to serve the same files.
|
||||
|
||||
|Option | value type | What it does|
|
||||
|--- | --- | ---|
|
||||
|S3_BUCKET | string | Name of your [S3 bucket](https://aws.amazon.com/s3/) |
|
||||
|S3_ACCESS_KEY | string | Access key for your bucket|
|
||||
|S3_SECRET_KEY | string | Secret key for your bucket |
|
||||
|S3_ENDPOINT | URL | Server URL. If you're using S3 compatible software like [Minio](https://min.io/) you can enter the URL here |
|
||||
|S3_REGION | string | Region of your bucket |
|
||||
|
||||
### FTP
|
||||
|
||||
Oldschool, insecure and not that fast. But if you use it in combination with [Encryption](/rtfm/ENCRYPTION.md) this could be OK I guess. I don't judge.
|
||||
This probably requires the php-ftp package but on some platforms it's included in the php-common package.
|
||||
|
||||
|Option | value type | What it does|
|
||||
|--- | --- | ---|
|
||||
|FTP_SERVER | string | IP or hostname of your FTP Server |
|
||||
|FTP_PORT | int | Port number of your FTP Server. Defaults to 21 |
|
||||
|FTP_SSL | bool | If your FTP server supports SSL-FTP (note: not sFTP! not the same), set it to true |
|
||||
|FTP_USER | string | FTP Username |
|
||||
|FTP_PASS | string | FTP Password |
|
||||
|FTP_BASEDIR | string | Base path where files will be stored. Must end with / eg `/web/pictshare/` |
|
||||
|FTP_PASSIVEMODE | bool | Wether to use passive mode or not. If you have troubles with uploading, switch this setting maybe |
|
||||
|
||||
# FFMPEG Worker
|
||||
|
||||
For faster video en/transcoding there is a php script called `/tools/ffmpeg_worker.php` which is a CLI application looping every second, checking the REDIS Queue for encoding tasks.
|
||||
|
||||
This way the rendering queue is detached from php-fpm since PHP can't have threaded workloads and it will make sure a encoding task like resizing of a video won't block the rest of the site from functioning.
|
||||
|
||||
For the FFMPEG Worker to be enabled, the config option `REDIS_SERVER` must be set to the IP Address of a redis server.
|
||||
@@ -1,32 +1,81 @@
|
||||
# Docker
|
||||
The fastest way to deploy PictShare is via the [official Docker repo](https://hub.docker.com/r/hascheksolutions/pictshare/)
|
||||
- [Source code & more examples](https://github.com/HaschekSolutions/PictShare-Docker)
|
||||
The fastest way to deploy PictShare is via the [official Docker package](https://github.com/HaschekSolutions/pictshare/pkgs/container/pictshare)
|
||||
|
||||
```bash
|
||||
docker run -d -p 80:80 -e "TITLE=My own PictShare" -e "URL=http://localhost/" hascheksolutions/pictshare
|
||||
docker run -d -p 80:80 -e "TITLE=My own PictShare" -e "URL=http://localhost/" hascheksolutions/pictshare:2
|
||||
```
|
||||
|
||||
[](https://www.pictshare.net/8a1dec0973.mp4)
|
||||
|
||||
### Docker Compose With Prebuild Image by hascheksolutions
|
||||
## Usage
|
||||
|
||||
Run container by docker-compose:
|
||||
- First, install docker compose:
|
||||
[Docker official docs](https://docs.docker.com/compose/install/)
|
||||
- Pull docker-compose file:
|
||||
### Building it
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/HaschekSolutions/pictshare/master/docker-compose.yml
|
||||
```
|
||||
- Edit docker-compose file:
|
||||
```bash
|
||||
vi docker-compose.yml
|
||||
```
|
||||
- Run container by docker-compose:
|
||||
```bash
|
||||
docker-compose up
|
||||
docker build -t pictshare -f docker/Dockerfile .
|
||||
```
|
||||
|
||||
By using this compose file, you should know that:
|
||||
- Will make a directory "volumes" in the same directory where compose file is.
|
||||
- Change `AUTOUPDATE` to false from true by defalt.
|
||||
- And...it is highly recommended to build your own image.
|
||||
### Quick start
|
||||
```bash
|
||||
docker run -d -p 80:80 --name=pictshare hascheksolutions/pictshare:2
|
||||
```
|
||||
|
||||
### Persistent data
|
||||
```bash
|
||||
mkdir /data/pictshareuploads
|
||||
chown 1000 -R /data/pictshareuploads
|
||||
docker run -d -v /data/pictshareuploads:/var/www/data -p 80:80 --name=pictshare hascheksolutions/pictshare:2
|
||||
```
|
||||
|
||||
### Persistent data with increased max upload size
|
||||
```bash
|
||||
mkdir /data/pictshareuploads
|
||||
chown 1000 -R /data/pictshareuploads
|
||||
docker run -d -e "MAX_UPLOAD_SIZE=1024" -v /data/pictshareuploads:/var/www/data -p 80:80 --name=pictshare hascheksolutions/pictshare:2
|
||||
```
|
||||
|
||||
### Development
|
||||
Using these commands it will mount the current directory in the docker container so you can develop locally without building after each change.
|
||||
|
||||
```bash
|
||||
docker build -t pictshare -f docker/Dockerfile .
|
||||
docker run -it --rm --name pictshare-dev -p 8080:80 -v $(pwd):/var/www -v $(pwd)/data:/var/www/data -e "URL=http://localhost:8080/" -e "SKIP_FILEPERMISSIONS=true" pictshare
|
||||
```
|
||||
|
||||
## ENV Variables
|
||||
There are some ENV variables that only apply to the Docker image
|
||||
- MAX_UPLOAD_SIZE (int | size in MB that will be used for nginx. default 50)
|
||||
|
||||
Every other variable can be referenced against the [default PictShare configuration file](https://github.com/HaschekSolutions/pictshare/blob/master/inc/example.config.inc.php).
|
||||
- TITLE (string | Title of the page)
|
||||
- URL (string | URL that will be linked to new uploads)
|
||||
- PNG_COMPRESSION (int | 0-9 how much compression is used. note that this never affects quality. default: 6)
|
||||
- JPEG_COMPRESSION (int | 0-100 how high should the quality be? More is better. default: 90)
|
||||
- MASTER_DELETE_CODE (string | code if added to any url, will delete the image)
|
||||
- MASTER_DELETE_IP (string | ip which can delete any image)
|
||||
- ALLOWED_SUBNET (CIDR IP range (can be comma separated) | IP subnets which are allowed to upload files)
|
||||
- ALLOW_BLOATING (true/false | can images be bloated to higher resolutions than the originals)
|
||||
- UPLOAD_CODE (string | Code that has to be supplied via POST or GET, to upload an image)
|
||||
- UPLOAD_FORM_LOCATION (string | absolute path where upload gui will be shown)
|
||||
- LOW_PROFILE (string | won't display error messages on failed uploads)
|
||||
- IMAGE_CHANGE_CODE (string | code if provided, needs to be added to image to apply filter/rotation/etc)
|
||||
- LOG_UPLOADER (true/false | log IPs of uploaders)
|
||||
- MAX_RESIZED_IMAGES (int | how many versions of a single image may exist? -1 for infinite)
|
||||
- SHOW_ERRORS (true/false | show upload/size/server errors?)
|
||||
- SKIP_FILEPERMISSIONS (true/false | enables/disables fixing file permissions on start. default is false)
|
||||
- ALWAYS_WEBP (true/false | Always tries to server JPGs as WEBp if the client supports it. Default is false)
|
||||
- ALT_FOLDER (path to a folder where all hashes will be copied to and looked for offsite backup via nfs for example)
|
||||
- S3_BUCKET (string | Name of your S3 bucket)
|
||||
- S3_ACCESS_KEY (string | Access Key for your Bucket)
|
||||
- S3_SECRET_KEY (string | Secrety Key)
|
||||
- S3_REGION (string | S3 bucket region)
|
||||
- S3_ENDPOINT (url | If you are using a selfhosted version of S3 like Minio, put your URL here)
|
||||
- ENCRYPTION_KEY (string | If you want to use encryption for storage controllers, put your encryption key here. [Read more](https://github.com/HaschekSolutions/pictshare/blob/master/rtfm/ENCRYPTION.md))
|
||||
|
||||
- FTP_SERVER (string | IP or hostname of your FTP Server )
|
||||
- FTP_PORT (int | Port of your FTP server (defaults to 21) )
|
||||
- FTP_SSL (true/false | If FTP server supports SSL-FTP (not sFTP, thats not the same!))
|
||||
- FTP_USER (string | FTP Username)
|
||||
- FTP_PASS (string | FTP Password)
|
||||
- FTP_BASEDIR (string | Base path where files will be stored. Must end with / eg `/web/pictshare/`)
|
||||
|
||||
- CONTENTCONTROLLERS (CSV string | If set, will whitelist content controllers for your instance. Must be uppercase and can be comma separated. Example: Only Pictures: `IMAGE`, Pictures and Videos: `IMAGE,VIDEO`)
|
||||
|
||||
37
rtfm/ENCRYPTION.md
Normal file
37
rtfm/ENCRYPTION.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Encryption
|
||||
|
||||
As of Jan. 2020 you can set up an encryption key in your config which will encrypt all images stored on [external storage](/rtfm/CONFIG.md#storage-controllers)
|
||||
|
||||
The files on the PictShare server are not encrypted, only the ones on external storage providers, eg if you want to use S3 as storage.
|
||||
|
||||
## Dependencies
|
||||
|
||||
To be able to use encryption you'll need the following extensions:
|
||||
|
||||
- mb-string (`apt-get install php-mbstring`)
|
||||
- libsodium (`apt-get install php-libsodium`)
|
||||
|
||||
Since only files on [storage controllers](/rtfm/CONFIG.md#storage-controllers) are encrypted, you'll need to configure at least one.
|
||||
|
||||
## Preparation
|
||||
|
||||
First you'll need to generate a key and encode it in base64.
|
||||
The easiest way to get it would be to run this command:
|
||||
|
||||
`php -r "echo base64_encode(sodium_crypto_secretstream_xchacha20poly1305_keygen());"`
|
||||
|
||||
This will output something like `SSdoJvp10ZvOY5v+vAcprxQKjNX1AzD52cAnFwr6yXc=`
|
||||
|
||||
Now put this output in your /inc/config.inc.php like this:
|
||||
|
||||
`define('ENCRYPTION_KEY','SSdoJvp10ZvOY5v+vAcprxQKjNX1AzD52cAnFwr6yXc=');`
|
||||
|
||||
**Warning: If you change or lose the ENCRYPTION_KEY, all encrypted data will be unrecoverably lost**
|
||||
|
||||
# How it works
|
||||
|
||||
If you have everything running you can upload a new image and it will get encrypted and uploaded to your storage container(s). This means you could even host on untrusted servers/buckets since nobody without the key will be able to decrypt it.
|
||||
|
||||
If you have uploaded a few files and see them on your storage container (eg S3) you'll notice the file has the '.enc' extension.
|
||||
|
||||
When you now wipe your PictShare instances local data folder and request the file again via the URL, the storage controller will pull the encrypted file, decrypt it and save it locally (unencrypted)
|
||||
39
rtfm/IMAGEFILTERS.md
Normal file
39
rtfm/IMAGEFILTERS.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Filters for images
|
||||
|
||||
If the value field is defined you can use it like this: ```filtername_value```. eg: https://pictshare.net/pixelate_10/4qylps.jpg
|
||||
|
||||
Click on the images to see them in full size
|
||||
|
||||
| Filter | Value | Example image |
|
||||
| --- | --- | --- |
|
||||
| pixelate | 1-100 | [](https://pictshare.net/pixelate/4qylps.jpg) |
|
||||
| blur | 1-6 | [](https://pictshare.net/blur/4qylps.jpg) |
|
||||
| sepia | | [](https://pictshare.net/sepia/4qylps.jpg) |
|
||||
| sepia2 | | [](https://pictshare.net/sepia2/4qylps.jpg) |
|
||||
| sharpen | | [](https://pictshare.net/sharpen/4qylps.jpg) |
|
||||
| emboss | | [](https://pictshare.net/emboss/4qylps.jpg) |
|
||||
| cool | | [](https://pictshare.net/cool/4qylps.jpg) |
|
||||
| light | | [](https://pictshare.net/light/4qylps.jpg) |
|
||||
| aqua | | [](https://pictshare.net/aqua/4qylps.jpg) |
|
||||
| fuzzy | | [](https://pictshare.net/fuzzy/4qylps.jpg) |
|
||||
| boost | | [](https://pictshare.net/boost/4qylps.jpg) |
|
||||
| boost2 | | [](https://pictshare.net/boost2/4qylps.jpg) |
|
||||
| gray | | [](https://pictshare.net/gray/4qylps.jpg) |
|
||||
| antique | | [](https://pictshare.net/antique/4qylps.jpg) |
|
||||
| blackwhite | | [](https://pictshare.net/blackwhite/4qylps.jpg) |
|
||||
| vintage | | [](https://pictshare.net/vintage/4qylps.jpg) |
|
||||
| concentrate| | [](https://pictshare.net/concentrate/4qylps.jpg) |
|
||||
| hermajesty | | [](https://pictshare.net/hermajesty/4qylps.jpg) |
|
||||
| everglow | | [](https://pictshare.net/everglow/4qylps.jpg) |
|
||||
| freshblue | | [](https://pictshare.net/freshblue/4qylps.jpg) |
|
||||
| tender | | [](https://pictshare.net/tender/4qylps.jpg) |
|
||||
| dream | | [](https://pictshare.net/dream/4qylps.jpg) |
|
||||
| frozen | | [](https://pictshare.net/frozen/4qylps.jpg) |
|
||||
| forest | | [](https://pictshare.net/forest/4qylps.jpg) |
|
||||
| rain | | [](https://pictshare.net/rain/4qylps.jpg) |
|
||||
| orangepeel | | [](https://pictshare.net/orangepeel/4qylps.jpg) |
|
||||
| darken | | [](https://pictshare.net/darken/4qylps.jpg) |
|
||||
| summer | | [](https://pictshare.net/summer/4qylps.jpg) |
|
||||
| retro | | [](https://pictshare.net/retro/4qylps.jpg) |
|
||||
| country | | [](https://pictshare.net/country/4qylps.jpg) |
|
||||
| washed | | [](https://pictshare.net/washed/4qylps.jpg) |
|
||||
@@ -2,24 +2,29 @@
|
||||
|
||||
PictShare is written to be run on a linux server with PHP 7 and nginx. We tried to support Windows for some time but ever since we started integrating ffmpeg for MP4 hosting we ditched Windows.
|
||||
|
||||
- Make sure you have PHP7 GD libraries installed: ```apt-get install php-gd```
|
||||
- Unpack the [PictShare zip](https://github.com/chrisiaut/pictshare/archive/master.zip)
|
||||
It's highly recommended t hat you use [the official Docker container](https://github.com/HaschekSolutions/pictshare/pkgs/container/pictshare) so you don't have to do any manual setup. But if you know what you're doing, you can set it up yourself.
|
||||
|
||||
- Make sure you have all PHP7 libraries installed (note on some systems the packages are not called php7-* but just php-* also on some systems php7-mbstring is called php7-mb): ```apt-get install php7-exif php7-gd php7-json php7-openssl php7-fileinfo php7-mbstring php7-mcrypt```
|
||||
- Run `composer install` from the `/lib` directory
|
||||
- If you are not using windows, make sure your os have the ```file``` command working: ```apt-get install file```
|
||||
- Unpack the [PictShare zip](https://github.com/hascheksolutions/pictshare/archive/master.zip)
|
||||
- Rename /inc/example.config.inc.php to /inc/config.inc.php
|
||||
- ```chmod +x bin/ffmpeg``` if you want to be able to use mp4 uploads
|
||||
- The provided ffmpeg binary (bin/ffmpeg) is from [here](http://johnvansickle.com/ffmpeg/) and it's a 64bit linux executable. If you need a different one, load yours and overwrite the one provided or if you have ffmpeg installed on the server you can use the config var ```FFMPEG_BINARY``` to tell PictShare where to look for the binary
|
||||
- If you want to be able to use mp4 uploads you need to supply your own FFMPEG binary or use the installed one on your distro useing the config var ```FFMPEG_BINARY```. For example it should point to `/usr/bin/ffmpeg` if you installed ffmpeg through your package manager
|
||||
- Since default upload sizes will be 2M in PHP you should edit your php.ini and change ```upload_max_filesize``` and ```post_max_size``` to a larger value
|
||||
- (optional) You can and should put a [nginx](https://www.nginx.com/) proxy before the Apache server. That thing is just insanely fast with static content like images.
|
||||
- (optional) You can and should use [nginx](https://www.nginx.com/) as your web server. Check [/docker/rootfs/nginx.conf] for an example on how the nginx config should look like
|
||||
- (optional) To secure your traffic I'd highly recommend getting an [SSL Cert](https://letsencrypt.org/) for your server if you don't already have one.
|
||||
|
||||
|
||||
## Upgrading
|
||||
- Just re-download the [PictShare zip](https://github.com/chrisiaut/pictshare/archive/master.zip) file and extract and overwrite existing pictshare files. Uploads and config won't be affected.
|
||||
- Check if your ```/inc/config.inc.php``` file has all settings required by the ```/inc/example.config.inc.php``` since new options might get added in new versions
|
||||
- On docker just `docker pull hascheksolutions/pictshare:2` and run the newer image
|
||||
- Manual upgrade:
|
||||
- Just re-download the [PictShare zip](https://github.com/hascheksolutions/pictshare/archive/master.zip) file and extract and overwrite existing pictshare files. Uploads and config won't be affected.
|
||||
- Check if your ```/inc/config.inc.php``` file has all settings required by the ```/inc/example.config.inc.php``` since new options might get added in new versions
|
||||
|
||||
|
||||
```bash
|
||||
# to be run from the directory where your pictshare directory sits in
|
||||
git clone https://github.com/chrisiaut/pictshare.git temp
|
||||
git clone https://github.com/hascheksolutions/pictshare.git temp
|
||||
cp -r temp/* pictshare/.
|
||||
rm -rf temp
|
||||
```
|
||||
@@ -59,4 +64,4 @@ server {
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
@@ -2,5 +2,141 @@
|
||||
|
||||
- [Pastebinit](/rtfm/PASTEBINIT.md)
|
||||
- Chrome Browser extension: https://chrome.google.com/webstore/detail/pictshare-1-click-imagesc/mgomffcdpnohakmlhhjmiemlolonpafc
|
||||
- Source: https://github.com/chrisiaut/PictShare-Chrome-extension
|
||||
- Plugin to upload images with ShareX: https://github.com/ShareX/CustomUploaders/blob/master/pictshare.net.sxcu
|
||||
- Source: https://github.com/hascheksolutions/PictShare-Chrome-extension
|
||||
- Plugin to upload images with ShareX: https://github.com/ShareX/CustomUploaders/blob/master/pictshare.net.sxcu
|
||||
|
||||
# Upload from CLI
|
||||
|
||||
Requirements:
|
||||
- curl (apt-get install curl)
|
||||
- jq (apt-get install jq)
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# filename: pictshare.sh
|
||||
# usage: ./pictshare.sh /path/to/image.jpg
|
||||
|
||||
result=$(curl -s -F "file=@${1}" https://pictshare.net/api/upload.php | jq -r .url)
|
||||
echo $result
|
||||
```
|
||||
|
||||
# Screenshot to pictshare (linux)
|
||||
|
||||
This script will create a screenshot (you can choose the area), uploads it to PictShare, copies the raw image to your clipborad and opens the image on PictShare in Chrome
|
||||
|
||||
Requirements:
|
||||
- curl (apt-get install curl)
|
||||
- jq (apt-get install jq)
|
||||
- screenshooter (apt-get install xfce4-screenshooter)
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# filename: screenshot2pictshare.sh
|
||||
# usage: ./screenshot2pictshare.sh
|
||||
|
||||
if [[ $# -eq 0 ]] ; then
|
||||
xfce4-screenshooter -r -o $0
|
||||
exit 0
|
||||
fi
|
||||
|
||||
result=$(curl -s -F "file=@${1}" https://pictshare.net/api/upload.php | jq -r .url)
|
||||
|
||||
xclip -selection clipboard -t image/png -i $1
|
||||
google-chrome $result
|
||||
```
|
||||
|
||||
# Screenshot to pictshare (windows)
|
||||
|
||||
This script will upload a screenshot from [Greenshot](https://getgreenshot.org/) to PictShare with the help of a PowerShell script.
|
||||
|
||||
Requirements:
|
||||
- curl (choco install curl)
|
||||
- [PowerShell 7](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows)
|
||||
|
||||
Configure Greenshot:
|
||||
- Settings -> Output
|
||||
- Storage location: C:\Temp\
|
||||
- Filename pattern: GreenShot
|
||||
- Image format: jpg
|
||||
Create a new External command, under Settings -> Plugins -> Click on External command Plugin -> Configure -> New
|
||||
- Name: what ever you want here
|
||||
- Command: find and point to pwsh.exe
|
||||
- Argument: -w Hidden -F Path\to\this\script -Address consto.com
|
||||
|
||||
Create a PowerShell script with the code below.
|
||||
|
||||
Feel free to change "C:\Temp\GreenShot.jpg" and "C:\Temp\pictshare_posts.json" to match your needs.
|
||||
|
||||
pictshare_posts.json is useful for logging and automating deleting old uploads.
|
||||
|
||||
```powershell
|
||||
#Requires -Version 7
|
||||
|
||||
param (
|
||||
# Change the base url to match your Pictshare server
|
||||
[Parameter(Mandatory)][string]$Address,
|
||||
# Change path of where you expect the jpg file to be
|
||||
[string]$File = "C:\Temp\GreenShot.jpg",
|
||||
# Log file of all requests
|
||||
[string]$LogFile = "C:\Temp\pictshare_posts.json",
|
||||
# Use http and not https
|
||||
[switch]$IsNotHttps,
|
||||
# Do not save url to upload to the clipboard
|
||||
[switch]$NoSaveToClipboard
|
||||
)
|
||||
begin {
|
||||
$Protocol = if ($IsNotHttps){
|
||||
"http:"
|
||||
}else{
|
||||
"https:"
|
||||
}
|
||||
}
|
||||
process {
|
||||
# Upload screenshot
|
||||
$Response = $(curl -s -F "file=@$File" $Protocol//$Address/api/upload.php) | ConvertFrom-Json
|
||||
if ($Response.status -like "ok") {
|
||||
if ($NoSaveToClipboard){
|
||||
# Don't save url to clipboard
|
||||
}else{
|
||||
Set-Clipboard -Value $Response.url
|
||||
}
|
||||
}
|
||||
# Output response back from the pictshare server
|
||||
if($Response){
|
||||
$Response | ConvertTo-Json | Out-File -FilePath $LogFile -Append
|
||||
}
|
||||
}
|
||||
end {}
|
||||
|
||||
|
||||
# PHP
|
||||
|
||||
```php
|
||||
/*
|
||||
* @param $path string Path to the file that should be uploaded
|
||||
* @param $hash string Optional. File name we want on pictshare for the file
|
||||
*/
|
||||
function pictshareUploadImage($path,$hash=false)
|
||||
{
|
||||
if(!file_exists($path)) return false;
|
||||
$request = curl_init('https://pictshare.net/api/upload.php');
|
||||
|
||||
curl_setopt($request, CURLOPT_POST, true);
|
||||
curl_setopt(
|
||||
$request,
|
||||
CURLOPT_POSTFIELDS,
|
||||
array(
|
||||
'file' => curl_file_create($path),
|
||||
'hash'=>$hash
|
||||
));
|
||||
|
||||
// output the response
|
||||
curl_setopt($request, CURLOPT_RETURNTRANSFER, true);
|
||||
$json = json_decode(curl_exec($request).PHP_EOL,true);
|
||||
|
||||
// close the session
|
||||
curl_close($request);
|
||||
|
||||
return $json;
|
||||
}
|
||||
```
|
||||
|
||||
34
rtfm/MODIFIERS.md
Normal file
34
rtfm/MODIFIERS.md
Normal file
@@ -0,0 +1,34 @@
|
||||
## Images
|
||||
|
||||
### Resize
|
||||
```
|
||||
/800x600/d8c01b45a6.png
|
||||
```
|
||||
|
||||
width x height
|
||||
|
||||
### Rotate
|
||||
```
|
||||
/upside|left|right/d8c01b45a6.png
|
||||
```
|
||||
|
||||
* `upside`: 180°
|
||||
* `left`: 90°
|
||||
* `right`: -90°
|
||||
|
||||
### WebP conversion
|
||||
```
|
||||
/webp/d8c01b45a6.jpeg
|
||||
```
|
||||
|
||||
### Gif to mp4
|
||||
```
|
||||
/mp4/d8c01b45a6.gif
|
||||
```
|
||||
|
||||
### Filters
|
||||
```
|
||||
/filter/d8c01b45a6.png
|
||||
```
|
||||
|
||||
[See available filters](IMAGEFILTERS.md)
|
||||
@@ -1,6 +1,6 @@
|
||||
# How to scale PictShare
|
||||
|
||||
If your library is huge then you might want to think about scaling your instances. Pictshare (v2+) was rebuilt with scaling in mind but instead of built-in scaling features we use a smarter system
|
||||
If your library is huge then you might want to think about scaling your instances. Pictshare (v2+) was rebuilt with scaling in mind but instead of built-in scaling features we rely on OS level solutions.
|
||||
|
||||
# The "ALT_FOLDER" setting
|
||||
You can set the config var ```ALT_FOLDER``` to point to a directory on the same server where pictshare will look for content and put new uploads.
|
||||
@@ -9,4 +9,46 @@ This allows you to have a shared or even a mounted ftp/nfs folder that will act
|
||||
|
||||
The main site https://pictshare.net uses this technique to scale across many servers in multiple countries.
|
||||
|
||||
Using this method you can have multiple servers for the same domain (with a reverse proxy)
|
||||
Using this method you can have multiple servers for the same domain (with a reverse proxy)
|
||||
|
||||
# Fast, read only instances
|
||||
PictShare needs strong hardware for video conversion but using smart Nginx configurations you can host an instance on a weak but fast server and relay all uploads to another server with stronger hardware running PictShare
|
||||
|
||||
**Example Nginx config for a small VPS that relay all uploads to a faster but private server**
|
||||
|
||||
```
|
||||
server {
|
||||
listen 80;
|
||||
server_name your.awesome.domain.name;
|
||||
|
||||
client_max_body_size 50M; # Set the max file upload size. This needs to be equal or larger than the size you specified in your php.ini
|
||||
|
||||
root /var/www/pictshare; # or where ever you put it
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?url=$request_uri;
|
||||
}
|
||||
|
||||
# Magic begins here. Since all uploads have to be made via some script in the /api/ folder we can just redirect
|
||||
# these requests to another server that doesn't need to be public facing
|
||||
location ^~ /api/ {
|
||||
proxy_pass http://10.12.0.3/; #set to the hostname or ip or url of the powerful server
|
||||
include /etc/nginx/proxy_params;
|
||||
}
|
||||
|
||||
location ~ \.php {
|
||||
fastcgi_pass unix:/var/run/php/php7.3-fpm.sock; #may be slightly different depending on your php version
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param PATH_INFO $fastcgi_script_name;
|
||||
}
|
||||
|
||||
location ~ /(data|tmp|bin|content-controllers|inc|interfaces|storage-controllers|templates|tools) {
|
||||
deny all;
|
||||
return 404;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
@@ -13,23 +13,39 @@ class AltfolderStorage implements StorageController
|
||||
return file_exists($altname);
|
||||
}
|
||||
|
||||
function pullFile($hash)
|
||||
function getItems($dev=false)
|
||||
{
|
||||
$rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(ALT_FOLDER.DS));
|
||||
$files = array();
|
||||
|
||||
foreach ($rii as $file) {
|
||||
if ($file->isDir())
|
||||
continue;
|
||||
$files[] = $file->getFilename();
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
function pullFile($hash,$location)
|
||||
{
|
||||
$altname=ALT_FOLDER.DS.$hash;
|
||||
if(file_exists($altname))
|
||||
{
|
||||
storeFile($altname,$hash,false);
|
||||
copy($altname,$location);
|
||||
}
|
||||
}
|
||||
|
||||
function pushFile($hash)
|
||||
function pushFile($source,$hash)
|
||||
{
|
||||
$altname=ALT_FOLDER.DS.$hash;
|
||||
$orig = ROOT.DS.'data'.DS.$hash.DS.$hash;
|
||||
if(file_exists($orig) && !$this->hashExists($hash))
|
||||
if(!$this->hashExists($hash))
|
||||
{
|
||||
copy($orig,$altname);
|
||||
}
|
||||
copy($source,$altname);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function deleteFile($hash)
|
||||
|
||||
145
storage-controllers/ftp.controller.php
Normal file
145
storage-controllers/ftp.controller.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
class FTPStorage implements StorageController
|
||||
{
|
||||
private $connection;
|
||||
private $login;
|
||||
|
||||
function __destruct()
|
||||
{
|
||||
if($this->connection)
|
||||
ftp_close($this->connection);
|
||||
}
|
||||
|
||||
function connect()
|
||||
{
|
||||
if(!$this->connection)
|
||||
{
|
||||
if(defined('FTP_SSL') && FTP_SSL === true)
|
||||
$this->connection = ftp_ssl_connect(FTP_SERVER, ((defined('FTP_SSL') && is_numeric(FTP_PORT))?FTP_PORT:21) );
|
||||
else
|
||||
$this->connection = ftp_connect(FTP_SERVER, ((defined('FTP_SSL') && is_numeric(FTP_PORT))?FTP_PORT:21) );
|
||||
}
|
||||
if($this->connection && !$this->login)
|
||||
{
|
||||
$this->login = ftp_login($this->connection, FTP_USER, FTP_PASS);
|
||||
|
||||
if( (defined('FTP_PASSIVEMODE') && FTP_PASSIVEMODE === true) || !defined('FTP_PASSIVEMODE') )
|
||||
{
|
||||
ftp_set_option($this->connection, FTP_USEPASVADDRESS, false);
|
||||
ftp_pasv($this->connection, TRUE);
|
||||
}
|
||||
else
|
||||
ftp_pasv($this->connection, false);
|
||||
|
||||
}
|
||||
|
||||
// Was the connection successful?
|
||||
if ((!$this->connection) || (!$this->login)) {
|
||||
$this->connection = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function isEnabled()
|
||||
{
|
||||
return (defined('FTP_SERVER') && FTP_SERVER &&
|
||||
defined('FTP_USER') && FTP_USER &&
|
||||
defined('FTP_PASS') && FTP_PASS);
|
||||
}
|
||||
|
||||
function hashExists($hash)
|
||||
{
|
||||
if(!$this->connect()) return null;
|
||||
$subdir = $this->hashToDir($hash);
|
||||
$ftpfilepath = FTP_BASEDIR.$subdir.'/'.$hash;
|
||||
if(@ftp_chdir($this->connection, FTP_BASEDIR.$subdir))
|
||||
return (ftp_size($this->connection,$ftpfilepath)>0?true:false);
|
||||
return false;
|
||||
}
|
||||
|
||||
function getItems($dev=false)
|
||||
{
|
||||
if(!$this->connect()) return false;
|
||||
return $this->ftp_list_files_recursive(FTP_BASEDIR,$dev);
|
||||
}
|
||||
|
||||
function pullFile($hash,$location)
|
||||
{
|
||||
if(!$this->connect()) return false;
|
||||
$subdir = $this->hashToDir($hash);
|
||||
$ftpfilepath = FTP_BASEDIR.$subdir.'/'.$hash;
|
||||
return ftp_get($this->connection, $location, $ftpfilepath, FTP_BINARY);
|
||||
}
|
||||
|
||||
function pushFile($source,$hash)
|
||||
{
|
||||
if(!$this->connect()) return false;
|
||||
$subdir = $this->hashToDir($hash);
|
||||
$ftpfilepath = FTP_BASEDIR.$subdir.'/'.$hash;
|
||||
$this->ftp_mksubdirs($subdir);
|
||||
|
||||
return ftp_put($this->connection, $ftpfilepath, $source, FTP_BINARY);
|
||||
}
|
||||
|
||||
function deleteFile($hash)
|
||||
{
|
||||
if(!$this->connect()) return false;
|
||||
$subdir = $this->hashToDir($hash);
|
||||
$ftpfilepath = FTP_BASEDIR.$subdir.'/'.$hash;
|
||||
return (ftp_delete($this->connection,$ftpfilepath)?true:false);
|
||||
}
|
||||
|
||||
function hashToDir($hash)
|
||||
{
|
||||
$md5 = md5($hash);
|
||||
$dir = $md5[0].'/'.$md5[1].'/'.$md5[2];
|
||||
|
||||
return $dir;
|
||||
}
|
||||
|
||||
function ftp_mksubdirs($ftpath)
|
||||
{
|
||||
if(!$this->connect()) return false;
|
||||
@ftp_chdir($this->connection, FTP_BASEDIR);
|
||||
$parts = array_filter(explode('/',$ftpath), function($value) {
|
||||
return ($value !== null && $value !== false && $value !== '');
|
||||
});
|
||||
foreach($parts as $part){
|
||||
$part = strval($part);
|
||||
if(!@ftp_chdir($this->connection, $part)){
|
||||
ftp_mkdir($this->connection, $part);
|
||||
ftp_chdir($this->connection, $part);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ftp_list_files_recursive($path,$dev=false)
|
||||
{
|
||||
if(!$this->connect()) return false;
|
||||
$items = ftp_mlsd($this->connection, $path);
|
||||
$result = array();
|
||||
|
||||
if(is_array($items))
|
||||
foreach ($items as $item)
|
||||
{
|
||||
$name = $item['name'];
|
||||
$type = $item['type'];
|
||||
$filepath = $path.'/'. $name;
|
||||
|
||||
if ($type == 'dir')
|
||||
{
|
||||
$result =
|
||||
array_merge($result, $this->ftp_list_files_recursive($filepath,$dev));
|
||||
}
|
||||
else if(mightBeAHash($name) || endswith($name,'.enc'))
|
||||
{
|
||||
$result[] = $name;
|
||||
if($dev===true) echo " Got $name \r";
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
108
storage-controllers/s3.controller.php
Normal file
108
storage-controllers/s3.controller.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Config needed
|
||||
*
|
||||
* S3_BUCKET
|
||||
* S3_ACCESS_KEY
|
||||
* S3_SECRET_KEY
|
||||
* (optional) S3_ENDPOINT
|
||||
*/
|
||||
|
||||
class S3Storage implements StorageController
|
||||
{
|
||||
private $s3;
|
||||
function connect(){
|
||||
|
||||
$this->s3 = new Aws\S3\S3Client([
|
||||
'version' => 'latest',
|
||||
'region' => (defined('S3_REGION') && S3_REGION ?S3_REGION:'us-east-1'),
|
||||
'endpoint' => S3_ENDPOINT,
|
||||
'use_path_style_endpoint' => true,
|
||||
'credentials' => [
|
||||
'key' => S3_ACCESS_KEY,
|
||||
'secret' => S3_SECRET_KEY,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
function isEnabled()
|
||||
{
|
||||
return (defined('S3_BUCKET') && S3_BUCKET);
|
||||
}
|
||||
|
||||
function hashExists($hash)
|
||||
{
|
||||
if(!$this->s3)$this->connect();
|
||||
|
||||
return $this->s3->doesObjectExist(S3_BUCKET,$hash);
|
||||
}
|
||||
|
||||
function getItems($dev=false)
|
||||
{
|
||||
if(!$this->s3)$this->connect();
|
||||
|
||||
$KeyCount = 9999;
|
||||
$keys = 100; //the amount of keys we'll receive per request. 1000 max but that times out sometimes
|
||||
$lastkey = false;
|
||||
$count = 0;
|
||||
$items = array();
|
||||
while($KeyCount>=$keys)
|
||||
{
|
||||
$objects = $this->s3->listObjectsV2([
|
||||
'Bucket' => S3_BUCKET,
|
||||
'MaxKeys'=> $keys,
|
||||
'StartAfter'=>($lastkey?$lastkey:'')
|
||||
]);
|
||||
|
||||
++$count;
|
||||
foreach ($objects['Contents'] as $object){
|
||||
$lastkey = $object['Key'];
|
||||
$items[] = $lastkey;
|
||||
}
|
||||
|
||||
if($dev===true) echo " Got ".($count*$keys)." files \r";
|
||||
|
||||
$KeyCount = $objects['KeyCount'];
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
function pullFile($hash,$location)
|
||||
{
|
||||
if(!$this->s3)$this->connect();
|
||||
|
||||
if(!$this->hashExists($hash)) return false;
|
||||
|
||||
$this->s3->getObject([
|
||||
'Bucket' => S3_BUCKET,
|
||||
'Key' => $hash,
|
||||
'SaveAs' => $location
|
||||
]);
|
||||
return true;
|
||||
}
|
||||
|
||||
function pushFile($source,$hash)
|
||||
{
|
||||
if(!$this->s3)$this->connect();
|
||||
|
||||
$this->s3->putObject([
|
||||
'Bucket' => S3_BUCKET,
|
||||
'Key' => $hash,
|
||||
'SourceFile' => $source
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function deleteFile($hash)
|
||||
{
|
||||
if(!$this->s3)$this->connect();
|
||||
|
||||
$this->s3->deleteObject([
|
||||
'Bucket' => S3_BUCKET,
|
||||
'Key' => $hash
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,10 @@
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<script>
|
||||
var maxUploadFileSize=<?php echo (int)(ini_get('upload_max_filesize'));?>
|
||||
</script>
|
||||
|
||||
<meta name="description" content="Free image sharing, linking and tracking">
|
||||
<meta name="keywords" content="image, share, hosting, free">
|
||||
<meta name="robots" content="index, follow">
|
||||
@@ -37,12 +41,26 @@
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<a href="/"><img src="/css/imgs/logo/horizontal3.png" /></a>
|
||||
<?php
|
||||
if(file_exists(ROOT.DS.'notice.txt'))
|
||||
echo '<div class="alert alert-warning" role="alert">'.file_get_contents(ROOT.DS.'notice.txt').'</div>';
|
||||
?>
|
||||
<div class="well">
|
||||
<?php if($forbidden===true) { ?>
|
||||
|
||||
<h2>Upload forbidden</h2>
|
||||
|
||||
<p>Due to configured restrictions, you are not allowed to upload files at this time</p>
|
||||
|
||||
<?php } else { ?>
|
||||
<div id="uploadinfo"></div>
|
||||
<p>
|
||||
<?php
|
||||
echo "Max Upload size: ". (int)(ini_get('upload_max_filesize'))."MB / File<br/>";
|
||||
echo "Allowed file types: ". implode(', ',getAllContentFiletypes());
|
||||
|
||||
if(defined('UPLOAD_CODE') && UPLOAD_CODE!='')
|
||||
echo '<br>Upload Code: <input type="text" id="uploadcode" />';
|
||||
?>
|
||||
</p>
|
||||
<form class="dropzone well" id="dropzone" method="post" action="/api/upload.php" enctype="multipart/form-data">
|
||||
@@ -50,6 +68,7 @@
|
||||
<input name="file" type="file" multiple />
|
||||
</div>
|
||||
</form>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
<video id="video" poster="<?php echo URL.$url.'/preview/'.$hash; ?>" preload="auto" autoplay="autoplay" controls muted="muted" loop="loop" webkit-playsinline>
|
||||
<source src="<?php echo URL.$url.'/raw' ?>" type="video/mp4">
|
||||
<?php
|
||||
if(file_exists(ROOT.DS.'data'.DS.$hash.DS.'webm_1.'.$hash))
|
||||
if(file_exists(getDataDir().DS.$hash.DS.'webm_1.'.$hash))
|
||||
echo '<source src="'.URL.'raw/webm/'.$hash.'" type="video/webm">'."\n";
|
||||
if(file_exists(ROOT.DS.'data'.DS.$hash.DS.'ogg_1.'.$hash))
|
||||
if(file_exists(getDataDir().DS.$hash.DS.'ogg_1.'.$hash))
|
||||
echo '<source src="'.URL.'raw/ogg/'.$hash.'" type="video/ogg">'."\n";
|
||||
?>
|
||||
</video>
|
||||
|
||||
0
tmp/.gitignore
vendored
Normal file → Executable file
0
tmp/.gitignore
vendored
Normal file → Executable file
@@ -1,99 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Alternative folder upload
|
||||
* This tool copies all raw images/videos/gifs to the defined ALT_FOLDER location
|
||||
* This will create a copy in the location. The location can be a mounted external server like CIFS or sshfs
|
||||
* This will allow you to store a backup of your images on some other server
|
||||
*
|
||||
*/
|
||||
|
||||
if(php_sapi_name() !== 'cli') exit('This script can only be called via CLI');
|
||||
error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE);
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
define('ROOT', dirname(__FILE__).DS.'..');
|
||||
include_once(ROOT.DS.'inc/config.inc.php');
|
||||
include_once(ROOT.DS.'inc/core.php');
|
||||
|
||||
if(!defined('ALT_FOLDER') || !ALT_FOLDER)
|
||||
die("[X] Error: You should define the ALT_FOLDER config in your inc/config.inc.php first");
|
||||
|
||||
$pm = new PictshareModel();
|
||||
|
||||
if(in_array('sim',$argv))
|
||||
{
|
||||
echo "[!!!!] SIMULATION MODE. Nothing will be uploaded [!!!!] \n\n";
|
||||
$sim = true;
|
||||
}
|
||||
else $sim = false;
|
||||
|
||||
|
||||
//gather local data
|
||||
echo "[i] Looping through local files\n";
|
||||
|
||||
$dir = ROOT.DS.'data'.DS;
|
||||
$dh = opendir($dir);
|
||||
$localfiles = array();
|
||||
|
||||
$allhashes=0;$allsize=0;
|
||||
$skips=0;$skipsize=0;
|
||||
$copied=0;$copysize=0;
|
||||
$errors=0;$errorsize=0;
|
||||
|
||||
while (false !== ($hash = readdir($dh))) {
|
||||
if($hash=='.'||$hash=='..') continue;
|
||||
$img = $dir.$hash.DS.$hash;
|
||||
if(!file_exists($img)) continue;
|
||||
$info = strtolower(pathinfo($img, PATHINFO_EXTENSION));
|
||||
$thissize = filesize($img);
|
||||
$type = $pm->isTypeAllowed($info);
|
||||
++$allhashes;
|
||||
$allsize+=$thissize;
|
||||
if($type)
|
||||
{
|
||||
if(file_exists(ALT_FOLDER.DS.$hash))
|
||||
{
|
||||
echo " [!] Skipping existing $hash \n";
|
||||
++$skips;
|
||||
$skipsize+=$thissize;
|
||||
}
|
||||
else
|
||||
{
|
||||
++$copied;
|
||||
$copysize+=$thissize;
|
||||
echo "[i] Copying $hash to ".ALT_FOLDER.DS.$hash." \r";
|
||||
if($sim===false)
|
||||
copy($img,ALT_FOLDER.DS.$hash);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++$errors;
|
||||
$errorsize+=$thissize;
|
||||
echo " [X] ERROR $hash not allowed format: $info \n";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
echo "\n[i] Done\n";
|
||||
echo "\n----------- STATS ----------\n\n";
|
||||
echo " All files found:\t$allhashes\t".renderSize($allsize)."\n";
|
||||
echo " Copied files:\t$copied\t".renderSize($copysize)."\n";
|
||||
echo " Skipped files:\t$skips\t".renderSize($skipsize)."\n";
|
||||
echo " Erroneous files:\t$errors\t".renderSize($errorsize)."\n";
|
||||
echo "\n";
|
||||
|
||||
|
||||
function renderSize($bytes, $precision = 2) {
|
||||
$units = array('B', 'KB', 'MB', 'GB', 'TB');
|
||||
|
||||
$bytes = max($bytes, 0);
|
||||
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
||||
$pow = min($pow, count($units) - 1);
|
||||
|
||||
// Uncomment one of the following alternatives
|
||||
$bytes /= pow(1024, $pow);
|
||||
// $bytes /= (1 << (10 * $pow));
|
||||
|
||||
return round($bytes, $precision) . ' ' . $units[$pow];
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Backblaze uploader
|
||||
* This tool uploads all local images to backblaze if they don't exist yet
|
||||
* You can use this to backup your images to backblaze or set it up as your data source for scaling
|
||||
*
|
||||
*/
|
||||
|
||||
if(php_sapi_name() !== 'cli') exit('This script can only be called via CLI');
|
||||
error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE);
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
define('ROOT', dirname(__FILE__).DS.'..');
|
||||
include_once(ROOT.DS.'inc/config.inc.php');
|
||||
include_once(ROOT.DS.'inc/core.php');
|
||||
include_once(ROOT.DS.'classes/backblaze.php');
|
||||
|
||||
$pm = new PictshareModel();
|
||||
|
||||
if(in_array('sim',$argv))
|
||||
{
|
||||
echo "[!!!!] SIMULATION MODE. Nothing will be uploaded [!!!!] \n\n";
|
||||
$sim = true;
|
||||
}
|
||||
else $sim = false;
|
||||
|
||||
if(in_array('recentlyrendered',$argv))
|
||||
{
|
||||
echo "[O] Will only upload if the image was recently viewed. Recently meaning now minus one year \n\n";
|
||||
$recentonly = true;
|
||||
}
|
||||
else $recentonly = false;
|
||||
|
||||
|
||||
$b = new Backblaze();
|
||||
echo "[i] Loading file list from Backblaze ..";
|
||||
$remotefiles = $b->getAllFilesInBucket();
|
||||
echo " done. Got ".count($remotefiles)." files\n";
|
||||
|
||||
$uploadsize = 0;
|
||||
|
||||
|
||||
//gather local data
|
||||
$dir = ROOT.DS.'data'.DS;
|
||||
$dh = opendir($dir);
|
||||
$localfiles = array();
|
||||
|
||||
echo "[i] Loading local files ..";
|
||||
while (false !== ($filename = readdir($dh))) {
|
||||
$img = $dir.$filename.DS.$filename;
|
||||
if(!file_exists($img)) continue;
|
||||
$type = strtolower(pathinfo($img, PATHINFO_EXTENSION));
|
||||
$type = $pm->isTypeAllowed($type);
|
||||
if($type)
|
||||
{
|
||||
if($recentonly===true)
|
||||
{
|
||||
$recent = @file_get_contents($dir.$filename.DS.'last_rendered.txt');
|
||||
if(!$recent || (time()-$recent) > 3600*24*365) continue;
|
||||
}
|
||||
$localfiles[] = $filename;
|
||||
}
|
||||
}
|
||||
|
||||
echo " done. Got ".count($localfiles)." files\n";
|
||||
|
||||
echo "[i] Checking if there are local files that are not remote\n";
|
||||
foreach($localfiles as $hash)
|
||||
{
|
||||
if(!$remotefiles[$hash])
|
||||
{
|
||||
echo " [!] $hash not found on BB. Uploading...";
|
||||
if($sim!==true)
|
||||
$b->upload($hash);
|
||||
$uploadsize+=filesize($dir.$hash.DS.$hash);
|
||||
echo " done.\tUploaded so far: ".renderSize($uploadsize)."\n";
|
||||
}
|
||||
}
|
||||
function renderSize($bytes, $precision = 2) {
|
||||
$units = array('B', 'KB', 'MB', 'GB', 'TB');
|
||||
|
||||
$bytes = max($bytes, 0);
|
||||
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
||||
$pow = min($pow, count($units) - 1);
|
||||
|
||||
// Uncomment one of the following alternatives
|
||||
$bytes /= pow(1024, $pow);
|
||||
// $bytes /= (1 << (10 * $pow));
|
||||
|
||||
return round($bytes, $precision) . ' ' . $units[$pow];
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Cleanup
|
||||
* This script cleans up all uploads and only leaves the original files
|
||||
* So if you have an image and it was converted to change sizes, these files are deleted
|
||||
* And will be re-created next time they are requested
|
||||
*
|
||||
* usage: php cleanup.php [sim]
|
||||
*
|
||||
* Params:
|
||||
* sim => Just simulate everything, don't actually delete
|
||||
*/
|
||||
|
||||
|
||||
if(php_sapi_name() !== 'cli') exit('This script can only be called via CLI');
|
||||
error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE);
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
define('ROOT', dirname(__FILE__).DS.'..');
|
||||
include_once(ROOT.DS.'inc/config.inc.php');
|
||||
include_once(ROOT.DS.'inc/core.php');
|
||||
|
||||
$pm = new PictshareModel();
|
||||
|
||||
if(in_array('sim',$argv))
|
||||
{
|
||||
echo "[!!!!] SIMULATION MODE. Nothing will be deleted [!!!!] \n\n";
|
||||
$sim = true;
|
||||
}
|
||||
else $sim = false;
|
||||
|
||||
$dir = ROOT.DS.'data'.DS;
|
||||
$dh = opendir($dir);
|
||||
$localfiles = array();
|
||||
|
||||
if(in_array('noskip',$argv))
|
||||
{
|
||||
echo "Won't skip existing files\n\n";
|
||||
$allowskipping = false;
|
||||
}
|
||||
else
|
||||
$allowskipping = true;
|
||||
|
||||
//making sure ffmpeg is executable
|
||||
system("chmod +x ".ROOT.DS.'bin'.DS.'ffmpeg');
|
||||
|
||||
echo "[i] Finding local mp4 files ..";
|
||||
while (false !== ($filename = readdir($dh))) {
|
||||
$img = $dir.$filename.DS.$filename;
|
||||
if(!file_exists($img)) continue;
|
||||
$type = strtolower(pathinfo($img, PATHINFO_EXTENSION));
|
||||
$type = $pm->isTypeAllowed($type);
|
||||
if($type)
|
||||
$localfiles[] = $filename;
|
||||
}
|
||||
|
||||
if(count($localfiles)==0) exit('No files found'."\n");
|
||||
|
||||
echo " done. Got ".count($localfiles)." folders\n";
|
||||
|
||||
$sumsize = 0;
|
||||
|
||||
echo "[i] Looking for files to clean up\n";
|
||||
foreach($localfiles as $hash)
|
||||
{
|
||||
$dir = ROOT.DS.'data'.DS.$hash.DS;
|
||||
$dh = opendir($dir);
|
||||
|
||||
while (false !== ($filename = readdir($dh))) {
|
||||
if(!is_file($dir.$filename) || $filename==$hash || $filename == 'last_rendered.txt')
|
||||
continue;
|
||||
|
||||
echo "[$hash] $filename";
|
||||
$sumsize+=filesize($dir.$filename);
|
||||
if(!$sim)
|
||||
unlink($dir.$filename);
|
||||
|
||||
echo "\t".(file_exists($dir.$filename)?'NOT DELETED':'DELETED')."\n";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
echo "\n[!] Finished! Deleted ".renderSize($sumsize)."\n";
|
||||
|
||||
function renderSize($bytes, $precision = 2) {
|
||||
$units = array('B', 'KB', 'MB', 'GB', 'TB');
|
||||
|
||||
$bytes = max($bytes, 0);
|
||||
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
||||
$pow = min($pow, count($units) - 1);
|
||||
|
||||
// Uncomment one of the following alternatives
|
||||
$bytes /= pow(1024, $pow);
|
||||
// $bytes /= (1 << (10 * $pow));
|
||||
|
||||
return round($bytes, $precision) . ' ' . $units[$pow];
|
||||
}
|
||||
46
tools/cron.php
Normal file
46
tools/cron.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
if(php_sapi_name() !== 'cli') exit('This script can only be called via CLI');
|
||||
|
||||
error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE);
|
||||
ini_set('memory_limit', -1);
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
define('ROOT', dirname(__FILE__).DS.'..');
|
||||
include_once(ROOT.DS.'inc/config.inc.php');
|
||||
include_once(ROOT.DS.'inc/core.php');
|
||||
|
||||
switch($argv[1])
|
||||
{
|
||||
case 'uploadqueue':
|
||||
uploadqueue();
|
||||
break;
|
||||
default:
|
||||
exit("[ERR] Command not found. Available commands are: uploadqueue");
|
||||
}
|
||||
|
||||
function uploadqueue()
|
||||
{
|
||||
$queuefile = ROOT.DS.'tmp'.DS.'controllerqueue.txt';
|
||||
if(!file_exists($queuefile))
|
||||
exit("[i] File does not exist (nothing to upload)\n");
|
||||
$queue = file($queuefile);
|
||||
if(count($queue)<1)
|
||||
exit("[i] Nothing to upload\n");
|
||||
$newqueue = array();
|
||||
foreach($queue as $hash)
|
||||
{
|
||||
$hash = trim($hash);
|
||||
echo " [i] Checking $hash\n";
|
||||
$hash = trim($hash);
|
||||
if(isExistingHash($hash)) //check if hash is still on server
|
||||
{
|
||||
echo " [$hash] still exists locally. Uploading.. ";
|
||||
$success = storageControllerUpload($hash); // and retry the upload
|
||||
echo ($success===true?' => SUCCESS. Removing from queue':'FAILED. Will be re-added to queue')."\n";
|
||||
if(!$success)
|
||||
$newqueue[]=$hash;
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents($queuefile,implode("\n",$newqueue));
|
||||
}
|
||||
2
tools/ffmpeg_worker.php
Normal file
2
tools/ffmpeg_worker.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
|
||||
@@ -31,7 +31,7 @@ require_once(ROOT . DS . 'content-controllers' . DS. 'video'. DS . 'video.contro
|
||||
if(!defined('FFMPEG_BINARY')||FFMPEG_BINARY=='' || !FFMPEG_BINARY) exit('Error: FFMPEG_BINARY not defined, no clue where to look');
|
||||
|
||||
$vc = new VideoController();
|
||||
$dir = ROOT.DS.'data'.DS;
|
||||
$dir = getDataDir().DS;
|
||||
$dh = opendir($dir);
|
||||
$localfiles = array();
|
||||
|
||||
@@ -57,7 +57,7 @@ if(in_array('altfolder',$argv) && defined('ALT_FOLDER') && ALT_FOLDER && is_dir(
|
||||
echo "\n [i] $filename is ..\t";
|
||||
$valid = $vc->rightEncodedMP4($vid);
|
||||
$tmp = ROOT.DS.'tmp'.DS.$hash;
|
||||
$cmd = FFMPEG_BINARY." -loglevel panic -y -i $vid -vcodec libx264 -an -profile:v baseline -level 3.0 -pix_fmt yuv420p -vf \"scale=trunc(iw/2)*2:trunc(ih/2)*2\" $tmp && cp $tmp $img";
|
||||
$cmd = FFMPEG_BINARY." -loglevel panic -y -i $vid -vcodec libx264 -profile:v baseline -level 3.0 -pix_fmt yuv420p -vf \"scale=trunc(iw/2)*2:trunc(ih/2)*2\" $tmp && cp $tmp $img";
|
||||
echo ($valid?'Valid'."\n":'Not valid => Converting..');
|
||||
if(!$valid)
|
||||
{
|
||||
@@ -69,9 +69,6 @@ if(in_array('altfolder',$argv) && defined('ALT_FOLDER') && ALT_FOLDER && is_dir(
|
||||
}
|
||||
}
|
||||
|
||||
//making sure ffmpeg is executable
|
||||
system("chmod +x ".ROOT.DS.'bin'.DS.'ffmpeg');
|
||||
|
||||
if(count($localfiles)==0)
|
||||
{
|
||||
echo "[i] Finding local mp4 files\n";
|
||||
@@ -106,7 +103,7 @@ foreach($localfiles as $hash)
|
||||
{
|
||||
$mp4 = $dir.$hash.DS.$hash;
|
||||
$tmp = ROOT.DS.'tmp'.DS.$hash;
|
||||
$cmd = FFMPEG_BINARY." -loglevel panic -y -i $mp4 -vcodec libx264 -an -profile:v baseline -level 3.0 -pix_fmt yuv420p -vf \"scale=trunc(iw/2)*2:trunc(ih/2)*2\" $tmp && cp $tmp $mp4";
|
||||
$cmd = FFMPEG_BINARY." -loglevel panic -y -i $mp4 -vcodec libx264 -profile:v baseline -level 3.0 -pix_fmt yuv420p -vf \"scale=trunc(iw/2)*2:trunc(ih/2)*2\" $tmp && cp $tmp $mp4";
|
||||
echo " [i] Converting '$hash'";
|
||||
system($cmd);
|
||||
if(defined('ALT_FOLDER') && ALT_FOLDER && is_dir(ALT_FOLDER))
|
||||
|
||||
@@ -4,7 +4,7 @@ define('ROOT', dirname(__FILE__).DS.'..');
|
||||
|
||||
echo "[i] Starting recreation of hashes.csv\n";
|
||||
|
||||
$dir = ROOT.DS.'data'.DS;
|
||||
$dir = getDataDir().DS;
|
||||
$dh = opendir($dir);
|
||||
|
||||
$fp = fopen($dir.'hashes.csv','w');
|
||||
|
||||
@@ -22,7 +22,7 @@ include_once(ROOT.DS.'inc/core.php');
|
||||
|
||||
$pm = new PictshareModel();
|
||||
|
||||
$dir = ROOT.DS.'data'.DS;
|
||||
$dir = getDataDir().DS;
|
||||
$dh = opendir($dir);
|
||||
$localfiles = array();
|
||||
|
||||
@@ -34,9 +34,6 @@ if(in_array('noskip',$argv))
|
||||
else
|
||||
$allowskipping = true;
|
||||
|
||||
//making sure ffmpeg is executable
|
||||
system("chmod +x ".ROOT.DS.'bin'.DS.'ffmpeg');
|
||||
|
||||
echo "[i] Finding local mp4 files ..";
|
||||
while (false !== ($filename = readdir($dh))) {
|
||||
$img = $dir.$filename.DS.$filename;
|
||||
|
||||
168
tools/storagecontroller-sync.php
Normal file
168
tools/storagecontroller-sync.php
Normal file
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Storage controller sync
|
||||
* This tool copies syncs local files to storage controllers
|
||||
*
|
||||
*/
|
||||
|
||||
if(php_sapi_name() !== 'cli') exit('This script can only be called via CLI');
|
||||
error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE);
|
||||
ini_set('memory_limit', -1);
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
define('ROOT', dirname(__FILE__).DS.'..');
|
||||
include_once(ROOT.DS.'inc/config.inc.php');
|
||||
include_once(ROOT.DS.'inc/core.php');
|
||||
|
||||
$dir = getDataDir().DS;
|
||||
$sc = getStorageControllers();
|
||||
$count = 0;
|
||||
$controllers = array();
|
||||
$filehashes = [];
|
||||
foreach($sc as $contr)
|
||||
{
|
||||
if((new $contr())->isEnabled()===true)
|
||||
{
|
||||
$controllers[] = new $contr();
|
||||
echo "[i] Found storage controller ".get_class($controllers[(count($controllers)-1)])."\n";
|
||||
}
|
||||
}
|
||||
|
||||
if(count($controllers)==0)
|
||||
die("[X] Error: You should define at least one storage controller in your inc/config.inc.php first");
|
||||
|
||||
if(in_array('sim',$argv))
|
||||
{
|
||||
echo "[!!!!] SIMULATION MODE. Nothing will be uploaded [!!!!] \n\n";
|
||||
$sim = true;
|
||||
}
|
||||
else $sim = false;
|
||||
|
||||
$enc=false;
|
||||
if(defined('ENCRYPTION_KEY') && ENCRYPTION_KEY)
|
||||
{
|
||||
$enc = new Encryption;
|
||||
echo "[i] Encryption key found. Will encrypt on Storage controllers\n";
|
||||
}
|
||||
|
||||
if(!in_array('p2',$argv))
|
||||
{
|
||||
echo "[i] PHASE 1\n";
|
||||
echo " [P1] Files from Storage controllers will be downloaded if they don't exist localy\n";
|
||||
sleep(1);
|
||||
|
||||
foreach($controllers as $contr)
|
||||
{
|
||||
echo " [P1] Collecting list of items from ".get_class($contr)."..\n";
|
||||
$controllerfiles = $contr->getItems(true);
|
||||
echo "\n done. Got ".count($controllerfiles)." files\n";
|
||||
if($controllerfiles)
|
||||
foreach($controllerfiles as $cfile)
|
||||
{
|
||||
if(endswith($cfile,'.enc'))
|
||||
$hash = substr($cfile,0,-4);
|
||||
else $hash = $cfile;
|
||||
|
||||
$filehashes[$contr][] = $hash;
|
||||
|
||||
if(!is_dir($dir.$hash) || !file_exists($dir.$hash.DS.$hash)) //file only on storage controller. Will download
|
||||
{
|
||||
echo " [P1] $hash is not on the Server but on ".get_class($contr)."\n";
|
||||
if($enc && endswith($cfile,'.enc')) // if its encrypted and we can decrypt, then do it
|
||||
{
|
||||
if($sim!==true)
|
||||
{
|
||||
$contr->pullFile($cfile,ROOT.DS.'tmp'.DS.$cfile);
|
||||
$enc->decryptFile(ROOT.DS.'tmp'.DS.$cfile, ROOT.DS.'tmp'.DS.$hash,base64_decode(ENCRYPTION_KEY));
|
||||
storeFile(ROOT.DS.'tmp'.DS.$hash,$hash,true);
|
||||
unlink(ROOT.DS.'tmp'.DS.$cfile);
|
||||
}
|
||||
}
|
||||
else{ //otherwise just get the file
|
||||
if($sim!==true){
|
||||
$contr->pullFile($hash,ROOT.DS.'tmp'.DS.$hash);
|
||||
storeFile(ROOT.DS.'tmp'.DS.$hash,$hash,true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
echo "\n ----------- END OF PHASE 1 -----------\n\n";
|
||||
}
|
||||
else echo "[i] Skipping Phase 1\n";
|
||||
|
||||
echo "[i] PHASE 2\n";
|
||||
echo " [P2] Local files are synced to all storage controllers\n";
|
||||
sleep(2);
|
||||
|
||||
echo " [P2] Looping through local files\n";
|
||||
|
||||
|
||||
$dh = opendir($dir);
|
||||
$localfiles = array();
|
||||
|
||||
$allhashes=0;$allsize=0;
|
||||
$skips=0;$skipsize=0;
|
||||
$copied=0;$copysize=0;
|
||||
$errors=0;$errorsize=0;
|
||||
$uploaded=0;$uploadsize=0;
|
||||
|
||||
while (false !== ($hash = readdir($dh))) {
|
||||
if($hash=='.'||$hash=='..') continue;
|
||||
$img = $dir.$hash.DS.$hash;
|
||||
if(!file_exists($img)) continue;
|
||||
$thissize = filesize($img);
|
||||
if(!isExistingHash($hash))
|
||||
continue;
|
||||
$allhashes++;
|
||||
$allsize+=$thissize;
|
||||
$realhash = ($enc===false?$hash:$hash.'.enc');
|
||||
|
||||
foreach($controllers as $contr)
|
||||
{
|
||||
|
||||
if( (count($filehashes[$contr]) > 0 && !in_array($hash,$filehashes[$contr])) || !$contr->hashExists($realhash) )
|
||||
{
|
||||
//if($sim!==true)
|
||||
|
||||
if(defined('ENCRYPTION_KEY') && ENCRYPTION_KEY && !$contr->hashExists($hash.'.enc')) //ok so we got an encryption key which means we'll upload the encrypted file
|
||||
{
|
||||
echo " [P2] Controller '".get_class($contr)."' doesn't have $hash. Encrypting and uploading.. ";
|
||||
$encryptedfile = $img.'.enc';
|
||||
|
||||
if($sim!==true)
|
||||
{
|
||||
$enc->encryptFile($img,$encryptedfile,base64_decode(ENCRYPTION_KEY));
|
||||
$uploadsize+=filesize($encryptedfile);
|
||||
$contr->pushFile($encryptedfile,$hash.'.enc');
|
||||
unlink($encryptedfile);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo " [P2] Controller '".get_class($contr)."' doesn't have $hash. Uploading unencrypted.. ";
|
||||
if($sim!==true)
|
||||
$contr->pushFile($img,$hash);
|
||||
$uploadsize+=$thissize;
|
||||
}
|
||||
|
||||
echo "done\n";
|
||||
$uploaded++;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
closedir($dh);
|
||||
|
||||
echo "\n[i] Done\n";
|
||||
echo "\n----------- STATS ----------\n\n";
|
||||
echo " All files found:\t$allhashes\t".renderSize($allsize)."\n";
|
||||
echo " Copied files:\t$copied\t".renderSize($copysize)."\n";
|
||||
echo " Skipped files:\t$skips\t".renderSize($skipsize)."\n";
|
||||
echo " Erroneous files:\t$errors\t".renderSize($errorsize)."\n";
|
||||
echo "\n";
|
||||
|
||||
Reference in New Issue
Block a user