├── examples ├── .tmp │ └── .gitkeep ├── .gitignore ├── .docker │ ├── Dockerfile │ └── uploadprogress.ini ├── docker-compose.yml ├── nginx.conf ├── README.md ├── handle-upload.php ├── check-progress.php └── index.html ├── tests ├── mocks │ ├── get_contents │ │ ├── upload_contents_variation004-aFieldId │ │ ├── variation005-fieldName │ │ ├── upload_contents_variation006-field │ │ ├── upload_contents_basic-formFieldId │ │ └── upload_contents_variation003-formFieldId │ └── get_info │ │ ├── upt_basic.txt │ │ ├── upt_variation003.txt │ │ ├── variation001 │ │ ├── upt_variation004.txt │ │ └── upt_bug58318.txt ├── uploadprogress_get_info_variation002.phpt ├── uploadprogress_get_info_error002.phpt ├── uploadprogress_get_info_error003.phpt ├── bug58318.phpt ├── uploadprogress_get_info_error001.phpt ├── uploadprogress_get_contents_variation001.phpt ├── uploadprogress_get_contents_variation002.phpt ├── uploadprogress_get_contents_variation004.phpt ├── uploadprogress_get_contents_error003.phpt ├── uploadprogress_get_contents_variation005.phpt ├── uploadprogress_get_contents_variation003.phpt ├── uploadprogress_get_contents_error001.phpt ├── uploadprogress_get_contents_error002.phpt ├── uploadprogress_get_info_error004.phpt ├── uploadprogress_get_info_basic.phpt ├── uploadprogress_get_contents_error004.phpt ├── uploadprogress_get_info_variation001.phpt ├── uploadprogress_get_info_variation004.phpt ├── uploadprogress_get_info_variation003.phpt ├── uploadprogress_get_contents_variation006.phpt └── uploadprogress_get_contents_basic.phpt ├── config.w32 ├── uploadprogress.stub.php ├── config.m4 ├── .gitignore ├── uploadprogress_legacy_arginfo.h ├── uploadprogress_arginfo.h ├── .github └── workflows │ └── continuous-integration.yml ├── LICENSE ├── php_uploadprogress.h ├── uploadprogress.dsp ├── README.md ├── package.xml └── uploadprogress.c /examples/.tmp/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | .tmp/* 2 | !.tmp/.gitkeep 3 | -------------------------------------------------------------------------------- /tests/mocks/get_contents/upload_contents_variation004-aFieldId: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/mocks/get_contents/variation005-fieldName: -------------------------------------------------------------------------------- 1 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. 2 | -------------------------------------------------------------------------------- /tests/mocks/get_contents/upload_contents_variation006-field: -------------------------------------------------------------------------------- 1 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. 2 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // vim:ft=javascript 2 | 3 | ARG_ENABLE('uploadprogress' , 'The Upload Progress extension', 'no'); 4 | 5 | if (PHP_UPLOADPROGRESS != "no") { 6 | EXTENSION("uploadprogress", "uploadprogress.c"); 7 | AC_DEFINE('HAVE_UPLOADPROGRESS', 1, 'The Upload Progress extension'); 8 | } 9 | -------------------------------------------------------------------------------- /tests/mocks/get_info/upt_basic.txt: -------------------------------------------------------------------------------- 1 | upload_id=file001 2 | fieldname=uploadFile 3 | filename=example.txt 4 | time_start=1579893044 5 | time_last=30 6 | speed_average=300 7 | speed_last=300 8 | bytes_uploaded=1592342 9 | bytes_total=3000000 10 | files_uploaded=1 11 | est_sec=45 12 | -------------------------------------------------------------------------------- /tests/mocks/get_info/upt_variation003.txt: -------------------------------------------------------------------------------- 1 | upload_id=file004 2 | fieldname=uploadFile 3 | filename=example.txt 4 | time_start=1579893044 5 | time_last=30 6 | speed_average=300 7 | speed_last=300 8 | bytes_uploaded=1592342 9 | bytes_total=3000000 10 | files_uploaded=1 11 | est_sec=45 12 | -------------------------------------------------------------------------------- /tests/mocks/get_info/variation001: -------------------------------------------------------------------------------- 1 | upload_id=file003 2 | fieldname=uploadFile 3 | filename=/path/to/tmp/filename.txt 4 | time_start=1579893044 5 | time_last=30 6 | speed_average=300 7 | speed_last=300 8 | bytes_uploaded=1592342 9 | bytes_total=3000000 10 | files_uploaded=1 11 | est_sec=45 12 | -------------------------------------------------------------------------------- /uploadprogress.stub.php: -------------------------------------------------------------------------------- 1 | = 8) exit('skip test not valid on PHP 8'); 8 | 9 | --FILE-- 10 | = 8) exit('skip test not valid on PHP 8'); 8 | 9 | --INI-- 10 | uploadprogress.get_contents = On 11 | 12 | --FILE-- 13 | 'file001', 18 | 'fieldname' => 'uploadFile', 19 | 'filename' => 'example.txt', 20 | 'time_start' => '1579893044', 21 | 'time_last' => '30', 22 | 'speed_average' => '300', 23 | 'speed_last' => '300', 24 | 'bytes_uploaded' => '1592342', 25 | 'bytes_total' => '3000000', 26 | 'files_uploaded' => '1', 27 | 'est_sec' => '45', 28 | ) 29 | -------------------------------------------------------------------------------- /uploadprogress_legacy_arginfo.h: -------------------------------------------------------------------------------- 1 | /* This is a generated file, edit the .stub.php file instead. 2 | * Stub hash: 2d041ae6d6a3d972168fe0cc71978048f694a39c */ 3 | 4 | ZEND_BEGIN_ARG_INFO_EX(arginfo_uploadprogress_get_info, 0, 0, 1) 5 | ZEND_ARG_INFO(0, identifier) 6 | ZEND_END_ARG_INFO() 7 | 8 | ZEND_BEGIN_ARG_INFO_EX(arginfo_uploadprogress_get_contents, 0, 0, 2) 9 | ZEND_ARG_INFO(0, identifier) 10 | ZEND_ARG_INFO(0, fieldname) 11 | ZEND_ARG_INFO(0, maxlen) 12 | ZEND_END_ARG_INFO() 13 | 14 | 15 | ZEND_FUNCTION(uploadprogress_get_info); 16 | ZEND_FUNCTION(uploadprogress_get_contents); 17 | 18 | 19 | static const zend_function_entry ext_functions[] = { 20 | ZEND_FE(uploadprogress_get_info, arginfo_uploadprogress_get_info) 21 | ZEND_FE(uploadprogress_get_contents, arginfo_uploadprogress_get_contents) 22 | ZEND_FE_END 23 | }; 24 | -------------------------------------------------------------------------------- /tests/uploadprogress_get_contents_error004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | uploadprogress_get_contents - Warns when too few arguments 3 | 4 | --SKIPIF-- 5 | 'file003', 18 | 'fieldname' => 'uploadFile', 19 | 'filename' => '/path/to/tmp/filename.txt', 20 | 'time_start' => '1579893044', 21 | 'time_last' => '30', 22 | 'speed_average' => '300', 23 | 'speed_last' => '300', 24 | 'bytes_uploaded' => '1592342', 25 | 'bytes_total' => '3000000', 26 | 'files_uploaded' => '1', 27 | 'est_sec' => '45', 28 | ) 29 | -------------------------------------------------------------------------------- /tests/uploadprogress_get_info_variation004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | uploadprogress_get_info - Trims whitespace around keys and values 3 | 4 | --SKIPIF-- 5 | 'file005', 18 | 'fieldname' => 'uploadFile', 19 | 'filename' => 'a value with spaces in it', 20 | 'time_start' => '1579893044', 21 | 'time_last' => '30', 22 | 'speed_average' => '300', 23 | 'speed_last' => '300', 24 | 'bytes_uploaded' => '1592342', 25 | 'bytes_total' => '3000000', 26 | 'files_uploaded' => '1', 27 | 'est_sec' => '45', 28 | ) 29 | -------------------------------------------------------------------------------- /uploadprogress_arginfo.h: -------------------------------------------------------------------------------- 1 | /* This is a generated file, edit the .stub.php file instead. 2 | * Stub hash: 2d041ae6d6a3d972168fe0cc71978048f694a39c */ 3 | 4 | ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_uploadprogress_get_info, 0, 1, IS_ARRAY, 1) 5 | ZEND_ARG_TYPE_INFO(0, identifier, IS_STRING, 0) 6 | ZEND_END_ARG_INFO() 7 | 8 | ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_uploadprogress_get_contents, 0, 2, IS_STRING, 1) 9 | ZEND_ARG_TYPE_INFO(0, identifier, IS_STRING, 0) 10 | ZEND_ARG_TYPE_INFO(0, fieldname, IS_STRING, 0) 11 | ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, maxlen, IS_LONG, 0, "-1") 12 | ZEND_END_ARG_INFO() 13 | 14 | 15 | ZEND_FUNCTION(uploadprogress_get_info); 16 | ZEND_FUNCTION(uploadprogress_get_contents); 17 | 18 | 19 | static const zend_function_entry ext_functions[] = { 20 | ZEND_FE(uploadprogress_get_info, arginfo_uploadprogress_get_info) 21 | ZEND_FE(uploadprogress_get_contents, arginfo_uploadprogress_get_contents) 22 | ZEND_FE_END 23 | }; 24 | -------------------------------------------------------------------------------- /tests/uploadprogress_get_info_variation003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | uploadprogress_get_info - When info file cannot be read 3 | 4 | --SKIPIF-- 5 | = 7.2 with the [fileinfo][] extension 8 | 9 | The easiest way to run this example is by using [Docker][] with the provided 10 | `docker-compose.yml` file (which includes all the requirements). 11 | 12 | ## Using docker-compose 13 | 14 | To run the Docker image, open a terminal window, change to this examples 15 | directory (i.e. `cd /path/to/examples`), and enter the following command. It may 16 | take a few minutes the first time you enter this, since it will build the image 17 | for you and then run it. 18 | 19 | ``` 20 | docker-compose up 21 | ``` 22 | 23 | After it starts, open a web browser and go to to run the 24 | example. Follow the instructions on the page, and read the code in this 25 | directory for a better understanding of what's going on. 26 | 27 | To stop `docker-compose`, press `Ctrl`-`C` in your terminal window, and then 28 | enter `docker-compose rm` to remove the container that was created. 29 | 30 | 31 | [docker]: https://www.docker.com 32 | [Apache HTTP Server]: https://httpd.apache.org 33 | [mod_php]: https://www.php.net/manual/en/install.unix.apache2.php 34 | [php-fpm]: https://www.php.net/fpm 35 | [fileinfo]: https://www.php.net/fileinfo 36 | [nginx]: https://nginx.org 37 | [caddy]: https://caddyserver.com 38 | -------------------------------------------------------------------------------- /examples/handle-upload.php: -------------------------------------------------------------------------------- 1 | 'ok']); 48 | -------------------------------------------------------------------------------- /.github/workflows/continuous-integration.yml: -------------------------------------------------------------------------------- 1 | # GitHub Actions Documentation: https://docs.github.com/en/actions 2 | 3 | name: "build" 4 | 5 | on: ["pull_request", "push"] 6 | 7 | jobs: 8 | ubuntu: 9 | name: "Test on Ubuntu" 10 | runs-on: "ubuntu-latest" 11 | 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | php-version: 16 | - "7.2" 17 | - "7.3" 18 | - "7.4" 19 | - "8.0" 20 | - "8.1" 21 | 22 | steps: 23 | - name: "Install dependencies" 24 | run: | 25 | sudo apt-get update 26 | sudo apt-get install -y re2c 27 | 28 | - name: "Checkout repository" 29 | uses: "actions/checkout@v2" 30 | 31 | - name: "Setup PHP" 32 | uses: "shivammathur/setup-php@v2" 33 | with: 34 | php-version: "${{ matrix.php-version }}" 35 | coverage: "none" 36 | tools: "phpize, php-config" 37 | 38 | - name: "Generate build files" 39 | run: "phpize" 40 | 41 | - name: "Configure build" 42 | run: "./configure --enable-uploadprogress" 43 | 44 | - name: "Build" 45 | run: "make" 46 | 47 | - name: "Run tests" 48 | run: | 49 | export TEST_PHP_ARGS="-n -g 'FAIL,XFAIL,BORK,WARN,LEAK,SKIP' -d extension=modules/uploadprogress.so" 50 | php run-tests.php -P --show-diff tests 51 | 52 | windows: 53 | name: "Test on Windows" 54 | runs-on: "windows-latest" 55 | 56 | defaults: 57 | run: 58 | shell: "cmd" 59 | 60 | strategy: 61 | fail-fast: false 62 | matrix: 63 | php-version: 64 | - "7.2" 65 | - "7.3" 66 | - "7.4" 67 | - "8.0" 68 | - "8.1" 69 | arch: 70 | - "x64" 71 | - "x86" 72 | ts: 73 | - "ts" 74 | - "nts" 75 | 76 | steps: 77 | - name: "Configure Git" 78 | run: "git config --system core.autocrlf false" 79 | 80 | - name: "Checkout repository" 81 | uses: "actions/checkout@v2" 82 | 83 | - name: "Setup PHP" 84 | id: "setup-php" 85 | uses: "cmb69/setup-php-sdk@v0.2" 86 | with: 87 | version: "${{ matrix.php-version }}" 88 | arch: "${{ matrix.arch }}" 89 | ts: "${{ matrix.ts }}" 90 | 91 | - name: "Enable developer command prompt" 92 | uses: "ilammy/msvc-dev-cmd@v1" 93 | with: 94 | arch: "${{ matrix.arch }}" 95 | toolset: "${{ steps.setup-php.outputs.toolset }}" 96 | 97 | - name: "Generate build files" 98 | run: "phpize" 99 | 100 | - name: "Configure build" 101 | run: "configure --enable-uploadprogress --with-prefix=${{ steps.setup-php.outputs.prefix }}" 102 | 103 | - name: "Build" 104 | run: "nmake" 105 | 106 | - name: "Run tests" 107 | run: nmake test TESTS="-n -g 'FAIL,XFAIL,BORK,WARN,LEAK,SKIP' --show-diff tests" 108 | -------------------------------------------------------------------------------- /examples/check-progress.php: -------------------------------------------------------------------------------- 1 | 'An error occurred.', 54 | 'messages' => $errors, 55 | ]); 56 | 57 | exit; 58 | } 59 | 60 | // Get the MIME type of the bytes uploaded, using the Fileinfo extension. 61 | $finfo = new finfo(FILEINFO_MIME_TYPE); 62 | $detectedMimeType = $finfo->buffer($contents); 63 | 64 | $info = array_merge($info, [ 65 | 'detectedMimeType' => $detectedMimeType, 66 | ]); 67 | 68 | // In our example, we respond with the uploadprogress information, as well as 69 | // the detected MIME type from the initial bytes uploaded (if we can detect it). 70 | header('HTTP/1.1 200 OK'); 71 | header('Content-Type: application/json'); 72 | 73 | echo json_encode($info); 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------- 2 | The PHP License, version 3.01 3 | Copyright (c) 1999 - 2021 The PHP Group. All rights reserved. 4 | -------------------------------------------------------------------- 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, is permitted provided that the following conditions 8 | are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in 15 | the documentation and/or other materials provided with the 16 | distribution. 17 | 18 | 3. The name "PHP" must not be used to endorse or promote products 19 | derived from this software without prior written permission. For 20 | written permission, please contact group@php.net. 21 | 22 | 4. Products derived from this software may not be called "PHP", nor 23 | may "PHP" appear in their name, without prior written permission 24 | from group@php.net. You may indicate that your software works in 25 | conjunction with PHP by saying "Foo for PHP" instead of calling 26 | it "PHP Foo" or "phpfoo" 27 | 28 | 5. The PHP Group may publish revised and/or new versions of the 29 | license from time to time. Each version will be given a 30 | distinguishing version number. 31 | Once covered code has been published under a particular version 32 | of the license, you may always continue to use it under the terms 33 | of that version. You may also choose to use such covered code 34 | under the terms of any subsequent version of the license 35 | published by the PHP Group. No one other than the PHP Group has 36 | the right to modify the terms applicable to covered code created 37 | under this License. 38 | 39 | 6. Redistributions of any form whatsoever must retain the following 40 | acknowledgment: 41 | "This product includes PHP software, freely available from 42 | ". 43 | 44 | THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND 45 | ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 46 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 47 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP 48 | DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 49 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 50 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 51 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 53 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 54 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 55 | OF THE POSSIBILITY OF SUCH DAMAGE. 56 | 57 | -------------------------------------------------------------------- 58 | 59 | This software consists of voluntary contributions made by many 60 | individuals on behalf of the PHP Group. 61 | 62 | The PHP Group can be contacted via Email at group@php.net. 63 | 64 | For more information on the PHP Group and the PHP project, 65 | please see . 66 | 67 | PHP includes the Zend Engine, freely available at 68 | . 69 | -------------------------------------------------------------------------------- /php_uploadprogress.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Uploadprogress extension | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt. | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Christian Stocker (chregu@liip.ch) | 16 | | Derived from: Doru Petrescu (pdoru-php-upm@kappa.ro)    | 17 | | http://pdoru.from.ro/upload-progress-meter/ | 18 | +----------------------------------------------------------------------+ 19 | */ 20 | 21 | #ifndef PHP_UPLOADPROGRESS_H 22 | #define PHP_UPLOADPROGRESS_H 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | #ifdef HAVE_CONFIG_H 29 | #include "config.h" 30 | #endif 31 | 32 | #include 33 | 34 | #ifdef HAVE_UPLOADPROGRESS 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #ifdef __cplusplus 42 | } // extern "C" 43 | #endif 44 | #ifdef __cplusplus 45 | extern "C" { 46 | #endif 47 | 48 | extern zend_module_entry uploadprogress_module_entry; 49 | #define phpext_uploadprogress_ptr &uploadprogress_module_entry 50 | 51 | #define PHP_UPLOADPROGRESS_VERSION "2.0.2" 52 | 53 | #ifdef PHP_WIN32 54 | #define PHP_UPLOADPROGRESS_API __declspec(dllexport) 55 | #else 56 | #define PHP_UPLOADPROGRESS_API 57 | #endif 58 | 59 | typedef struct _uploadprogress_data { 60 | /* Full filename, or just the identifier, depending on method */ 61 | char *identifier; 62 | 63 | /* Full filename, or just the identifier, depending on method */ 64 | char *identifier_tmp; 65 | 66 | /* Raw string of the UPLOAD_IDENTIFIER */ 67 | char *upload_id; 68 | 69 | /* Full filename of temporary data file */ 70 | char *data_filename; 71 | 72 | /* Name of form field for current file being uploaded */ 73 | char *fieldname; 74 | 75 | /* Filename of the uploaded file */ 76 | char *filename; 77 | 78 | time_t time_start; 79 | time_t time_last; 80 | unsigned int speed_average; 81 | unsigned int speed_last; 82 | unsigned long bytes_uploaded; 83 | unsigned long bytes_total; 84 | unsigned int files_uploaded; 85 | int est_sec; 86 | } uploadprogress_data; 87 | 88 | static char *uploadprogress_mk_filename(char *identifier, char *template); 89 | 90 | static void uploadprogress_file_php_get_info(char *, zval *); 91 | static void uploadprogress_file_php_get_contents(char *, char *, long, zval *); 92 | 93 | PHP_MINIT_FUNCTION(uploadprogress); 94 | PHP_MSHUTDOWN_FUNCTION(uploadprogress); 95 | PHP_RINIT_FUNCTION(uploadprogress); 96 | PHP_RSHUTDOWN_FUNCTION(uploadprogress); 97 | PHP_MINFO_FUNCTION(uploadprogress); 98 | 99 | #ifdef ZTS 100 | #include "TSRM.h" 101 | #endif 102 | 103 | PHP_FUNCTION(uploadprogress_get_info); 104 | PHP_FUNCTION(uploadprogress_get_contents); 105 | 106 | #ifdef __cplusplus 107 | } // extern "C" 108 | #endif 109 | 110 | #endif /* PHP_HAVE_UPLOADPROGRESS */ 111 | 112 | #endif /* PHP_UPLOADPROGRESS_H */ 113 | -------------------------------------------------------------------------------- /uploadprogress.dsp: -------------------------------------------------------------------------------- 1 | # Microsoft Developer Studio Project File - Name="uploadprogress" - Package Owner=<4> 2 | # Microsoft Developer Studio Generated Build File, Format Version 6.00 3 | # ** DO NOT EDIT ** 4 | 5 | # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 6 | 7 | CFG=uploadprogress - Win32 Debug_TS 8 | !MESSAGE This is not a valid makefile. To build this project using NMAKE, 9 | !MESSAGE use the Export Makefile command and run 10 | !MESSAGE 11 | !MESSAGE NMAKE /f "uploadprogress.mak". 12 | !MESSAGE 13 | !MESSAGE You can specify a configuration when running NMAKE 14 | !MESSAGE by defining the macro CFG on the command line. For example: 15 | !MESSAGE 16 | !MESSAGE NMAKE /f "uploadprogress.mak" CFG="uploadprogress - Win32 Debug_TS" 17 | !MESSAGE 18 | !MESSAGE Possible choices for configuration are: 19 | !MESSAGE 20 | !MESSAGE "uploadprogress - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library") 21 | !MESSAGE "uploadprogress - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library") 22 | !MESSAGE 23 | 24 | # Begin Project 25 | # PROP AllowPerConfigDependencies 0 26 | # PROP Scc_ProjName "" 27 | # PROP Scc_LocalPath "" 28 | CPP=cl.exe 29 | MTL=midl.exe 30 | RSC=rc.exe 31 | 32 | !IF "$(CFG)" == "uploadprogress - Win32 Release_TS" 33 | 34 | # PROP BASE Use_MFC 0 35 | # PROP BASE Use_Debug_Libraries 0 36 | # PROP BASE Output_Dir "Release_TS" 37 | # PROP BASE Intermediate_Dir "Release_TS" 38 | # PROP BASE Target_Dir "" 39 | # PROP Use_MFC 0 40 | # PROP Use_Debug_Libraries 0 41 | # PROP Output_Dir "Release_TS" 42 | # PROP Intermediate_Dir "Release_TS" 43 | # PROP Ignore_Export_Lib 0 44 | # PROP Target_Dir "" 45 | # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "UPLOADPROGRESS_EXPORTS" /YX /FD /c 46 | # ADD CPP /nologo /MT /W3 /GX /O2 /I "..\.." /I "..\..\Zend" /I "..\..\TSRM" /I "..\..\main" /D "WIN32" /D "PHP_EXPORTS" /D "COMPILE_DL_UPLOADPROGRESS" /D ZTS=1 /D HAVE_UPLOADPROGRESS=1 /D ZEND_DEBUG=0 /D "NDEBUG" /D "_WINDOWS" /D "ZEND_WIN32" /D "PHP_WIN32" /YX /FD /c 47 | # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 48 | # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 49 | # ADD BASE RSC /l 0x407 /d "NDEBUG" 50 | # ADD RSC /l 0x407 /d "NDEBUG" 51 | BSC32=bscmake.exe 52 | # ADD BASE BSC32 /nologo 53 | # ADD BSC32 /nologo 54 | LINK32=link.exe 55 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 56 | # ADD LINK32 php4ts.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"..\..\Release_TS\php_uploadprogress.dll" /libpath:"..\..\Release_TS" /libpath:"..\..\Release_TS_Inline" 57 | 58 | !ELSEIF "$(CFG)" == "uploadprogress - Win32 Debug_TS" 59 | 60 | # PROP BASE Use_MFC 0 61 | # PROP BASE Use_Debug_Libraries 1 62 | # PROP BASE Output_Dir "Debug_TS" 63 | # PROP BASE Intermediate_Dir "Debug_TS" 64 | # PROP BASE Target_Dir "" 65 | # PROP Use_MFC 0 66 | # PROP Use_Debug_Libraries 1 67 | # PROP Output_Dir "Debug_TS" 68 | # PROP Intermediate_Dir "Debug_TS" 69 | # PROP Ignore_Export_Lib 0 70 | # PROP Target_Dir "" 71 | # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "UPLOADPROGRESS_EXPORTS" /YX /FD /GZ /c 72 | # ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\.." /I "..\..\Zend" /I "..\..\TSRM" /I "..\..\main" /D ZEND_DEBUG=1 /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "PHP_EXPORTS" /D "COMPILE_DL_UPLOADPROGRESS" /D ZTS=1 /D "ZEND_WIN32" /D "PHP_WIN32" /D HAVE_UPLOADPROGRESS=1 /YX /FD /GZ /c 73 | # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 74 | # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 75 | # ADD BASE RSC /l 0x407 /d "_DEBUG" 76 | # ADD RSC /l 0x407 /d "_DEBUG" 77 | BSC32=bscmake.exe 78 | # ADD BASE BSC32 /nologo 79 | # ADD BSC32 /nologo 80 | LINK32=link.exe 81 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept 82 | # ADD LINK32 php4ts_debug.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"..\..\Debug_TS\php_uploadprogress.dll" /pdbtype:sept /libpath:"..\..\Debug_TS" 83 | 84 | !ENDIF 85 | 86 | # Begin Target 87 | 88 | # Name "uploadprogress - Win32 Release_TS" 89 | # Name "uploadprogress - Win32 Debug_TS" 90 | 91 | # Begin Group "Source Files" 92 | 93 | # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" 94 | 95 | # Begin Source File 96 | 97 | SOURCE=./uploadprogress.c 98 | # End Source File 99 | 100 | # End Group 101 | 102 | # Begin Group "Header Files" 103 | 104 | # PROP Default_Filter "h;hpp;hxx;hm;inl" 105 | 106 | # Begin Source File 107 | 108 | SOURCE=.\php_uploadprogress.h 109 | # End Source File 110 | # End Group 111 | # End Target 112 | # End Project 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uploadprogress 2 | 3 | A PHP extension to track progress of a file upload, including details on the 4 | speed of the upload, estimated time remaining, and access to the contents of the 5 | file as it is being uploaded. 6 | 7 | ## Requirements 8 | 9 | The uploadprogress extension works on PHP 7.2+ and PHP 8. It works with 10 | [Apache HTTP Server][] using [mod_php][], as well as [Apache HTTP Server][], 11 | [nginx][], and [Caddy][] through [PHP-FPM][]. It might work on other web 12 | servers; let us know where you're using it. 13 | 14 | ## Example 15 | 16 | Check out the [examples][] directory for a working example that you can run on 17 | your local machine. 18 | 19 | ## Installation 20 | 21 | Install uploadprogress with `pecl`: 22 | 23 | ``` 24 | pecl install uploadprogress 25 | ``` 26 | 27 | If it is not automatically added to your `php.ini` file by the `pecl` command, 28 | you will need to update `php.ini` by adding the following line: 29 | 30 | ``` ini 31 | extension=uploadprogress 32 | ``` 33 | 34 | ## Documentation 35 | 36 | In forms for which you wish to track a file upload using uploadprogress, you 37 | must include a field named `UPLOAD_IDENTIFIER`. 38 | 39 | The value of the `UPLOAD_IDENTIFIER` field may be any string. We recommend a 40 | random, unique string per upload. This extension will use this value to keep 41 | track of the upload, and you may query the extension using this identifier to 42 | check the progress of the upload. 43 | 44 | For example, you might choose to define the `UPLOAD_IDENTIFIER` field as such: 45 | 46 | ``` html 47 | 48 | ``` 49 | 50 | The uploadprogress extension provides two functions: `uploadprogress_get_info()` 51 | and `uploadprogress_get_contents()`. 52 | 53 | While a file is uploading, you may call these functions from a different script 54 | to check on the progress of the uploading file, providing the same identifier 55 | used as the `UPLOAD_IDENTIFIER`. 56 | 57 | For example, you might make an HTTP `GET` request to 58 | `/check-progress.php?identifier=some_identifier&fieldName=the_file_upload_form_field_name`. 59 | The contents of `check-progress.php` might contain code like this: 60 | 61 | ``` php 62 | $identifier = filter_input(INPUT_GET, 'identifier', FILTER_SANITIZE_STRING); 63 | $fieldName = filter_input(INPUT_GET, 'fieldName', FILTER_SANITIZE_STRING); 64 | 65 | $info = uploadprogress_get_info($identifier); 66 | $contents = uploadprogress_get_contents($identifier, $fieldName); 67 | ``` 68 | 69 | ### php.ini Settings 70 | 71 | * `uploadprogress.file.filename_template`: 72 | 73 | Set the path and pattern to which the *info* file should be written. This is 74 | where we will store the data about the uploaded file, while it is being 75 | uploaded. You may set it to a directory, or you may optionally use a filename 76 | pattern. It defaults to `sys_get_temp_dir() . '/upt_%s.txt'`. The `%s` is 77 | replaced with the value of `UPLOAD_IDENTIFIER`. 78 | 79 | * `uploadprogress.file.contents_template`: 80 | 81 | Set the path and pattern to which the *contents* of the uploaded file should 82 | be written. This allows us to read the contents of the file, while it is still 83 | being uploaded. You may set it to a directory, or you may optionally use a 84 | filename pattern. It defaults to `sys_get_temp_dir() . '/upload_contents_%s'`. 85 | The `%s` is replaced with the value of `UPLOAD_IDENTIFIER` combined with the 86 | name of the file upload form field. 87 | 88 | * `uploadprogress.get_contents`: 89 | 90 | Set to "On" to enable the ability to read a file's contents while it is still 91 | uploading. Defaults to "Off." 92 | 93 | **NOTE:** The paths for these INI settings must be *absolute* paths. Relative 94 | paths will not work. 95 | 96 | #### Example php.ini 97 | 98 | ``` ini 99 | extension=uploadprogress 100 | uploadprogress.get_contents=On 101 | uploadprogress.file.filename_template=/tmp/upt_%s.txt 102 | uploadprogress.file.contents_template=/tmp/upload_contents_%s 103 | ``` 104 | 105 | ### uploadprogress_get_info 106 | 107 | ``` php 108 | uploadprogress_get_info ( string $identifier ) : array 109 | ``` 110 | 111 | The `$identifier` is the value of the form field named `UPLOAD_IDENTIFIER`. 112 | 113 | This returns an associative array with the following keys: 114 | 115 | * `upload_id` - The value of `UPLOAD_IDENTIFIER`. 116 | * `fieldname` - The name of the file upload form field for this file upload. 117 | * `filename` - The original name of the file being uploaded. 118 | * `time_start` - The Unix timestamp at which the upload began. 119 | * `time_last` - The Unix timestamp at which this information was last updated. 120 | * `speed_average` - The average upload speed, in bytes. 121 | * `speed_last` - The last speed calculation, in bytes. 122 | * `bytes_uploaded` - The number of bytes uploaded so far. 123 | * `bytes_total` - The total number of bytes to be upload. 124 | * `files_uploaded` - The total number of files uploaded so far. 125 | * `est_sec` - The estimated number of seconds remaining until the upload is 126 | complete. 127 | 128 | ### uploadprogress_get_contents 129 | 130 | ``` php 131 | uploadprogress_get_contents ( string $identifier , string $fieldName [, int $maxLength ] ) : string 132 | ``` 133 | 134 | The `$identifier` is the value of the form field named `UPLOAD_IDENTIFIER`. 135 | 136 | The `$fieldName` is the value of the file upload form field name. 137 | 138 | The `$maxLength` is an optional number of bytes to read, if you wish to read 139 | only the first `$maxLength` bytes of the uploading file. Otherwise, all bytes 140 | currently uploaded will be read. 141 | 142 | This returns a string of all the bytes currently uploaded for the uploading file. 143 | 144 | ## Contributing 145 | 146 | Your contributions and bug reports are highly appreciated. To contribute, fork 147 | and create a pull request. To report a bug use the [PHP Bug Tracking 148 | System](https://bugs.php.net/report.php?package=uploadprogress). 149 | 150 | ### Building on *nix systems 151 | 152 | To compile this extension, execute the following steps: 153 | 154 | ```shell 155 | phpize 156 | ./configure --enable-uploadprogress 157 | make 158 | ``` 159 | 160 | ### Building on Windows 161 | 162 | The extension provides the VisualStudio V6 project file: 163 | 164 | uploadprogress.dsp 165 | 166 | To compile the extension you open this file using VisualStudio, select the 167 | apropriate configuration for your installation (either "Release_TS" or 168 | "Debug_TS") and create `php_uploadprogress.dll`. 169 | 170 | After successfull compilation you have to copy the newly created 171 | `php_uploadprogress.dll` to the PHP extension directory (default: 172 | `C:\PHP\extensions`). 173 | 174 | ### Testing 175 | 176 | You may now run the tests with the following (on Windows, use `nmake`): 177 | 178 | ```shell 179 | make test TESTS="-n --show-diff tests" 180 | ``` 181 | 182 | For application testing, you can now load the extension using a php.ini directive 183 | 184 | ``` ini 185 | extension=uploadprogress 186 | ``` 187 | 188 | The extension should now be available, which you can test using the 189 | `extension_loaded()` function: 190 | 191 | ``` php 192 | if (extension_loaded('uploadprogress')) { 193 | echo "uploadprogress loaded :)"; 194 | } else { 195 | echo "something is wrong :("; 196 | } 197 | ``` 198 | 199 | The extension will also add its own block to the output of `phpinfo();`. 200 | 201 | 202 | [Apache HTTP Server]: https://httpd.apache.org 203 | [mod_php]: https://www.php.net/manual/en/install.unix.apache2.php 204 | [php-fpm]: https://www.php.net/fpm 205 | [examples]: ./examples 206 | [nginx]: https://nginx.org 207 | [caddy]: https://caddyserver.com 208 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | uploadprogress Example 26 | 27 | 28 |
29 |

uploadprogress Example

30 | 31 |

32 | Welcome to the uploadprogress 33 | extension example! 34 |

35 | 36 |

37 | Click on the file field below or the "Browse" button to select 38 | a file, and then click the "Upload" button. For best results, 39 | select a large file — something over 10 MB. As the file uploads, 40 | you will see the progress bar fill up. You should also see a 41 | guess about the file's type. This uses the 42 | fileinfo extension to 43 | examine the bytes as they're being uploaded to guess the file 44 | type. 45 |

46 | 47 |

48 | To learn more about what's going on in this example, 49 | read 50 | the code. 51 |

52 | 53 |
54 | 55 |
56 |
57 | 58 | 59 |
60 |
61 | 62 |
63 | 64 |
65 |
0%
66 |
67 | 68 |
69 | 70 | It looks like you're uploading . 71 |
72 | 73 |
74 |
75 | 76 | 77 | 78 | 79 | 80 | 81 | 244 | 245 | 246 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | uploadprogress 9 | pecl.php.net 10 | An extension to track progress of a file upload. 11 | 12 | A PHP extension to track progress of a file upload, including details on the speed of the upload, estimated time remaining, and access to the contents of the file as it is being uploaded. 13 | 14 | The uploadprogress extension works on PHP 7.2+ and PHP 8. It works with Apache HTTP Server using mod_php, as well as Apache HTTP Server, nginx, and Caddy through PHP-FPM. It might work on other web servers; let us know where you're using it. 15 | 16 | See https://github.com/php/pecl-php-uploadprogress for documentation and examples. 17 | 18 | 19 | Christian Stocker 20 | chregu 21 | chregu@php.net 22 | yes 23 | 24 | 25 | Ben Ramsey 26 | ramsey 27 | ramsey@php.net 28 | yes 29 | 30 | 2021-10-01 31 | 32 | 33 | 2.0.2 34 | 1.0.0 35 | 36 | 37 | stable 38 | stable 39 | 40 | PHP License 41 | 42 | - Update release package with latest examples and documentation 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 7.2.0 115 | 116 | 117 | 1.4.0 118 | 119 | 120 | 121 | uploadprogress 122 | 123 | 124 | 125 | 2021-09-29 126 | 127 | 2.0.1 128 | 1.0.0 129 | 130 | 131 | stable 132 | stable 133 | 134 | 135 | - Fix parameter and return type declarations for PHP 7 and PHP 8 136 | 137 | 138 | 139 | 2021-09-29 140 | 141 | 2.0.0 142 | 1.0.0 143 | 144 | 145 | stable 146 | stable 147 | 148 | 149 | - Drop support for PHP < 7.2 150 | 151 | 152 | 153 | 2021-09-28 154 | 155 | 1.1.4 156 | 1.0.0 157 | 158 | 159 | stable 160 | stable 161 | 162 | 163 | - Fix #79584: Segmentation fault in uploadprogress 1.1.0 and up 164 | - Add arginfo for functions to support PHP 8 and up 165 | 166 | 167 | 168 | 2020-01-28 169 | 170 | 1.1.3 171 | 1.0.0 172 | 173 | 174 | stable 175 | stable 176 | 177 | 178 | - Improved documentation and examples included in the release package 179 | 180 | 181 | 182 | 2020-01-26 183 | 184 | 1.1.2 185 | 1.0.0 186 | 187 | 188 | stable 189 | stable 190 | 191 | 192 | - PHP 8 compatibility; this now builds on PHP 5, PHP 7, and PHP 8 193 | 194 | 195 | 196 | 2020-01-26 197 | 198 | 1.1.1 199 | 1.0.0 200 | 201 | 202 | stable 203 | stable 204 | 205 | 206 | - Mark maximum PHP version as 8.0.0; this will not build on PHP 8 207 | 208 | 209 | 210 | 2020-01-26 211 | 212 | 1.1.0 213 | 1.0.0 214 | 215 | 216 | stable 217 | stable 218 | 219 | 220 | - PHP 7 compatibility 221 | 222 | 223 | 224 | 2011-08-15 225 | 226 | 1.0.3.1 227 | 1.0 228 | 229 | 230 | stable 231 | stable 232 | 233 | 234 | - Wrong version number in .h file 235 | 236 | 237 | 238 | 2011-08-08 239 | 240 | 1.0.3 241 | 1.0 242 | 243 | 244 | stable 245 | stable 246 | 247 | 248 | - Another make it work with PHP 5.4 249 | 250 | 251 | 252 | 2011-07-26 253 | 254 | 1.0.2 255 | 1.0 256 | 257 | 258 | stable 259 | stable 260 | 261 | 262 | - Make it work with PHP 5.4 263 | 264 | 265 | 266 | 2009-06-16 267 | 268 | 1.0.1 269 | 1.0 270 | 271 | 272 | stable 273 | stable 274 | 275 | 276 | - Default temporary directory is taken from system settings during compile time (guenter). 277 | - Fix a while loop error in removing preceding whitespace (terralith). 278 | - Example: Added a check, if the extension is installed at all. 279 | - Example: Added checks for a valid uploadprogress.file.filename_template setting. 280 | 281 | 282 | 283 | 2009-03-15 284 | 285 | 1.0.0 286 | 1.0 287 | 288 | 289 | stable 290 | stable 291 | 292 | 293 | - Fixed a renaming issue with temporary files on Windows and PHP 5.2. 294 | 295 | 296 | 297 | 2009-01-22 298 | 299 | 0.9.2 300 | 0.9 301 | 302 | 303 | beta 304 | beta 305 | 306 | 307 | - Fixed an issue with filenames containing spaces, see http://pecl.php.net/bugs/bug.php?id=14525 for details (by franck). 308 | - Extended the example to check for too large files. 309 | 310 | 311 | 312 | 2008-08-25 313 | 314 | 0.9.1 315 | 0.9 316 | 317 | 318 | beta 319 | beta 320 | 321 | 322 | - Fixed version information in phpinfo() 323 | - Added php.ini options to phpinfo() 324 | 325 | 326 | 327 | 2008-07-08 328 | 329 | 0.9.0 330 | 0.9 331 | 332 | 333 | beta 334 | beta 335 | 336 | 337 | - Added uploadprogress_get_contents(id, fieldname) to return the contents of that file (Ben Ramsey) 338 | - Added uploadprogress.get_contents INI option to enable above function (Ben Ramsey) 339 | - Added a simple example 340 | - Minor Fixes 341 | 342 | 343 | 344 | 2006-12-05 345 | 346 | 0.3.0 347 | 0.3 348 | 349 | 350 | beta 351 | beta 352 | 353 | 354 | Initial release 355 | 356 | 357 | 358 | 359 | -------------------------------------------------------------------------------- /uploadprogress.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Uploadprogress extension | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt. | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Christian Stocker (chregu@liip.ch) | 16 | | Derived from: Doru Petrescu (pdoru-php-upm@kappa.ro)    | 17 | | http://pdoru.from.ro/upload-progress-meter/ | 18 | +----------------------------------------------------------------------+ 19 | */ 20 | 21 | #include "php_uploadprogress.h" 22 | #include "rfc1867.h" 23 | 24 | #if HAVE_UPLOADPROGRESS 25 | 26 | #ifdef P_tmpdir 27 | #define TMPDIR P_tmpdir 28 | #else 29 | #define TMPDIR "/tmp" 30 | #endif 31 | 32 | /* {{{ argument information */ 33 | #if PHP_VERSION_ID < 80000 34 | #include "uploadprogress_legacy_arginfo.h" 35 | #else 36 | #include "uploadprogress_arginfo.h" 37 | #endif 38 | 39 | PHP_INI_BEGIN() 40 | PHP_INI_ENTRY("uploadprogress.file.filename_template", TMPDIR"/upt_%s.txt", PHP_INI_ALL, NULL) 41 | PHP_INI_ENTRY("uploadprogress.file.contents_template", TMPDIR"/upload_contents_%s", PHP_INI_ALL, NULL) 42 | PHP_INI_ENTRY("uploadprogress.get_contents", "0", PHP_INI_ALL, NULL) 43 | PHP_INI_END() 44 | 45 | /* {{{ uploadprogress_module_entry 46 | */ 47 | zend_module_entry uploadprogress_module_entry = { 48 | STANDARD_MODULE_HEADER, 49 | "uploadprogress", 50 | ext_functions, 51 | PHP_MINIT(uploadprogress), 52 | PHP_MSHUTDOWN(uploadprogress), 53 | PHP_RINIT(uploadprogress), 54 | PHP_RSHUTDOWN(uploadprogress), 55 | PHP_MINFO(uploadprogress), 56 | PHP_UPLOADPROGRESS_VERSION, 57 | STANDARD_MODULE_PROPERTIES 58 | }; 59 | /* }}} */ 60 | 61 | #ifdef COMPILE_DL_UPLOADPROGRESS 62 | ZEND_GET_MODULE(uploadprogress) 63 | #endif 64 | 65 | PHPAPI extern int (*php_rfc1867_callback)(unsigned int , void *, void **); 66 | 67 | /* {{{ uploadprogress_php_rfc1867_file 68 | */ 69 | static int uploadprogress_php_rfc1867_file(unsigned int event, void *event_data, void **data) 70 | { 71 | uploadprogress_data *progress; 72 | size_t read_bytes; 73 | zend_bool get_contents = INI_BOOL("uploadprogress.get_contents"); 74 | 75 | progress = *data; 76 | 77 | if (event == MULTIPART_EVENT_START) { 78 | multipart_event_start *e_data; 79 | e_data = (multipart_event_start*) event_data; 80 | progress = emalloc(sizeof(uploadprogress_data)); 81 | progress->upload_id = NULL; 82 | progress->fieldname = NULL; 83 | progress->data_filename = NULL; 84 | progress->bytes_total = e_data->content_length; 85 | progress->identifier = NULL; 86 | progress->identifier_tmp = NULL; 87 | progress->time_start = time(NULL); 88 | *data = progress; 89 | } else if (event == MULTIPART_EVENT_FORMDATA) { 90 | multipart_event_formdata *e_data; 91 | e_data = (multipart_event_formdata*) event_data; 92 | read_bytes = e_data->post_bytes_processed; 93 | 94 | if (e_data->newlength) { 95 | *e_data->newlength = e_data->length; 96 | } 97 | 98 | if (strcmp(e_data->name, "UPLOAD_IDENTIFIER") == 0) { 99 | char *template = INI_STR("uploadprogress.file.filename_template"); 100 | 101 | if (strcmp(template, "") == 0) { 102 | return FAILURE; 103 | } 104 | 105 | progress->upload_id = emalloc(strlen(*e_data->value) + 1); 106 | strcpy(progress->upload_id, *e_data->value); 107 | progress->time_last = time(NULL); 108 | progress->speed_average = 0; 109 | progress->speed_last = 0; 110 | progress->bytes_uploaded = read_bytes; 111 | progress->files_uploaded = 0; 112 | progress->est_sec = 0; 113 | progress->identifier = uploadprogress_mk_filename(progress->upload_id, template); 114 | progress->identifier_tmp = emalloc(strlen( progress->identifier) + 4); 115 | sprintf(progress->identifier_tmp, "%s.wr", progress->identifier); 116 | } 117 | } 118 | 119 | if (progress->identifier) { 120 | time_t crtime = time(NULL); 121 | int d, dt, ds; 122 | 123 | if (event == MULTIPART_EVENT_FILE_START) { 124 | char *data_identifier; 125 | multipart_event_file_start *e_data; 126 | 127 | e_data = (multipart_event_file_start*) event_data; 128 | read_bytes = e_data->post_bytes_processed; 129 | 130 | progress->fieldname = e_data->name; 131 | progress->filename = *e_data->filename; 132 | 133 | data_identifier = emalloc(strlen(progress->upload_id) + strlen(progress->fieldname) + 2); 134 | sprintf(data_identifier, "%s-%s", progress->upload_id, progress->fieldname); 135 | 136 | if (get_contents) { 137 | char *data_template = INI_STR("uploadprogress.file.contents_template"); 138 | 139 | if (strcmp(data_template, "") == 0) { 140 | return FAILURE; 141 | } 142 | 143 | progress->data_filename = uploadprogress_mk_filename(data_identifier, data_template); 144 | } 145 | 146 | if (data_identifier) { 147 | efree(data_identifier); 148 | } 149 | } else if (event == MULTIPART_EVENT_FILE_DATA) { 150 | multipart_event_file_data *e_data; 151 | 152 | e_data = (multipart_event_file_data*) event_data; 153 | read_bytes = e_data->post_bytes_processed; 154 | 155 | if (get_contents) { 156 | php_stream *stream; 157 | int options = 0; 158 | 159 | stream = php_stream_open_wrapper(progress->data_filename, "ab", options, NULL); 160 | 161 | if (stream) { 162 | php_stream_write(stream, e_data->data, e_data->length); 163 | } 164 | 165 | php_stream_close(stream); 166 | } 167 | } else if (event == MULTIPART_EVENT_FILE_END) { 168 | multipart_event_file_end *e_data; 169 | 170 | e_data = (multipart_event_file_end*) event_data; 171 | 172 | read_bytes = e_data->post_bytes_processed; 173 | progress->files_uploaded++; 174 | 175 | if (get_contents) { 176 | VCWD_UNLINK(progress->data_filename); 177 | efree(progress->data_filename); 178 | } 179 | } else if (event == MULTIPART_EVENT_END) { 180 | VCWD_UNLINK(progress->identifier); 181 | efree(progress->upload_id); 182 | efree(progress->identifier); 183 | efree(progress->identifier_tmp); 184 | efree(progress); 185 | 186 | return SUCCESS; 187 | } 188 | 189 | /* Just in case we encounter a fracture in time. */ 190 | if (progress->time_last > crtime) { 191 | progress->time_last = crtime; 192 | } 193 | 194 | dt = crtime - progress->time_last; 195 | ds = crtime - progress->time_start; 196 | d = read_bytes - progress->bytes_uploaded; 197 | 198 | if (dt) { 199 | progress->speed_last = d/dt; 200 | progress->time_last = crtime; 201 | progress->bytes_uploaded = read_bytes; 202 | progress->speed_average = ds ? read_bytes / ds : 0; 203 | progress->est_sec = progress->speed_average ? (progress->bytes_total - read_bytes) / progress->speed_average : -1; 204 | } 205 | 206 | if (dt || event >= MULTIPART_EVENT_FILE_END) { 207 | FILE *F; 208 | 209 | F = VCWD_FOPEN(progress->identifier_tmp, "wb"); 210 | 211 | if (F) { 212 | fprintf(F, 213 | "upload_id=%s\nfieldname=%s\nfilename=%s\ntime_start=%ld\n" 214 | "time_last=%ld\nspeed_average=%u\nspeed_last=%u\n" 215 | "bytes_uploaded=%lu\nbytes_total=%lu\n" 216 | "files_uploaded=%u\nest_sec=%d\n", 217 | progress->upload_id, 218 | progress->fieldname, 219 | progress->filename, 220 | progress->time_start, 221 | progress->time_last, 222 | progress->speed_average, 223 | progress->speed_last, 224 | progress->bytes_uploaded, 225 | progress->bytes_total, 226 | progress->files_uploaded, 227 | progress->est_sec); 228 | 229 | fclose(F); 230 | VCWD_RENAME(progress->identifier_tmp, progress->identifier); 231 | } 232 | } 233 | } 234 | 235 | if (event == MULTIPART_EVENT_END ) { 236 | if (progress->identifier) { 237 | efree(progress->identifier); 238 | } 239 | 240 | if (progress->upload_id) { 241 | efree(progress->upload_id); 242 | } 243 | 244 | if (progress->identifier_tmp) { 245 | efree(progress->identifier_tmp); 246 | } 247 | 248 | if (get_contents && progress->data_filename) { 249 | efree(progress->data_filename); 250 | } 251 | 252 | efree(progress); 253 | } 254 | 255 | return SUCCESS; 256 | } 257 | /* }}} */ 258 | 259 | /* {{{ PHP_MINIT_FUNCTION */ 260 | PHP_MINIT_FUNCTION(uploadprogress) 261 | { 262 | REGISTER_INI_ENTRIES(); 263 | php_rfc1867_callback = uploadprogress_php_rfc1867_file; 264 | 265 | return SUCCESS; 266 | } 267 | /* }}} */ 268 | 269 | /* {{{ PHP_MSHUTDOWN_FUNCTION */ 270 | PHP_MSHUTDOWN_FUNCTION(uploadprogress) 271 | { 272 | UNREGISTER_INI_ENTRIES(); 273 | php_rfc1867_callback = NULL; 274 | 275 | return SUCCESS; 276 | } 277 | /* }}} */ 278 | 279 | /* {{{ PHP_RINIT_FUNCTION */ 280 | PHP_RINIT_FUNCTION(uploadprogress) 281 | { 282 | return SUCCESS; 283 | } 284 | /* }}} */ 285 | 286 | /* {{{ PHP_RSHUTDOWN_FUNCTION */ 287 | PHP_RSHUTDOWN_FUNCTION(uploadprogress) 288 | { 289 | return SUCCESS; 290 | } 291 | /* }}} */ 292 | 293 | /* {{{ PHP_MINFO_FUNCTION */ 294 | PHP_MINFO_FUNCTION(uploadprogress) 295 | { 296 | php_info_print_table_start(); 297 | php_info_print_table_header(2, "uploadprogress support", "enabled"); 298 | php_info_print_table_row(2, "Version", PHP_UPLOADPROGRESS_VERSION); 299 | php_info_print_table_end(); 300 | DISPLAY_INI_ENTRIES(); 301 | } 302 | /* }}} */ 303 | 304 | /* {{{ proto array uploadprogress_get_info(string identifier) 305 | */ 306 | PHP_FUNCTION(uploadprogress_get_info) 307 | { 308 | char *id; 309 | size_t id_lg; 310 | 311 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &id, &id_lg) == FAILURE) { 312 | return; 313 | } 314 | 315 | uploadprogress_file_php_get_info(id, return_value); 316 | 317 | return; 318 | } 319 | /* }}} */ 320 | 321 | /* {{{ proto string uploadprogress_get_contents(string identifier, string fieldname[, int maxlen]) 322 | */ 323 | PHP_FUNCTION(uploadprogress_get_contents) 324 | { 325 | char *id, *fieldname; 326 | size_t id_len, fieldname_len; 327 | long maxlen = PHP_STREAM_COPY_ALL; 328 | zend_bool get_contents = INI_BOOL("uploadprogress.get_contents"); 329 | 330 | if (!get_contents) { 331 | php_error_docref(NULL, E_WARNING, 332 | "this function is disabled; set uploadprogress.get_contents = On to enable it"); 333 | RETURN_FALSE; 334 | 335 | return; 336 | } 337 | 338 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|l", 339 | &id, &id_len, &fieldname, &fieldname_len, &maxlen) == FAILURE) { 340 | return; 341 | } 342 | 343 | if (ZEND_NUM_ARGS() == 3 && maxlen < 0) { 344 | php_error_docref(NULL, E_WARNING, "length must be greater than or equal to zero"); 345 | RETURN_FALSE; 346 | } 347 | 348 | uploadprogress_file_php_get_contents(id, fieldname, maxlen, return_value); 349 | return; 350 | } 351 | /* }}} */ 352 | 353 | /* {{{ uploadprogress_mk_filename 354 | */ 355 | static char *uploadprogress_mk_filename(char *identifier, char *template) 356 | { 357 | char *x; 358 | char *filename; 359 | 360 | filename = emalloc(strlen(template) + strlen(identifier) + 3); 361 | x = strstr(template, "%s"); 362 | 363 | if (x == NULL) { 364 | sprintf(filename, "%s/%s", template, identifier); 365 | } else { 366 | strcpy(filename, template); 367 | strcpy(filename + (x - template), identifier); 368 | strcat(filename, x + 2); 369 | } 370 | 371 | return filename; 372 | } 373 | /* }}} */ 374 | 375 | /* {{{ uploadprogress_file_php_get_info 376 | */ 377 | static void uploadprogress_file_php_get_info(char *id, zval *return_value) 378 | { 379 | char s[1024]; 380 | char *filename; 381 | char *template; 382 | FILE *F; 383 | 384 | template = INI_STR("uploadprogress.file.filename_template"); 385 | 386 | if (strcmp(template, "") == 0) { 387 | return; 388 | } else { 389 | filename = uploadprogress_mk_filename(id, template); 390 | 391 | if (!filename) { 392 | return; 393 | } 394 | 395 | F = VCWD_FOPEN(filename, "rb"); 396 | 397 | if (F) { 398 | array_init(return_value); 399 | 400 | while (fgets(s, 1000, F)) { 401 | char *k, *v, *e; 402 | int index = 0; 403 | 404 | e = strchr(s, '='); 405 | 406 | if (!e) { 407 | continue; 408 | } 409 | 410 | /* Break the line into 2 parts. */ 411 | *e = 0; 412 | v = e + 1; 413 | k = s; 414 | 415 | /* Trim spaces in front of the name/value. */ 416 | while (*k && *k <= 32) { 417 | k++; 418 | } 419 | 420 | while (*v && *v <= 32) { 421 | v++; 422 | } 423 | 424 | /* Trim spaces everywhere in the name. */ 425 | for (e = k; *e; e++) { 426 | if (*e <= 32) { 427 | *e = 0; 428 | break; 429 | } 430 | } 431 | 432 | /* Trim spaces only at the end of the value. */ 433 | if (v != NULL) { 434 | for (index = strlen(v); index > 0; index--) { 435 | if (v[index] > 32) { 436 | break; 437 | } 438 | 439 | v[index] = 0; 440 | } 441 | } 442 | 443 | add_assoc_string(return_value, k, v); 444 | } 445 | 446 | fclose(F); 447 | } 448 | 449 | if (filename) { 450 | efree(filename); 451 | } 452 | 453 | return; 454 | } 455 | } 456 | /* }}} */ 457 | 458 | /* {{{ uploadprogress_file_php_get_contents 459 | */ 460 | static void uploadprogress_file_php_get_contents(char *id, char *fieldname, long maxlen, zval *return_value) 461 | { 462 | char *filename, *template, *data_identifier; 463 | zend_string *contents; 464 | int options = 0; 465 | size_t len; 466 | php_stream *stream; 467 | 468 | template = INI_STR("uploadprogress.file.contents_template"); 469 | 470 | if (strcmp(template, "") == 0) { 471 | return; 472 | } else { 473 | data_identifier = emalloc(strlen(id) + strlen(fieldname) + 2); 474 | sprintf(data_identifier, "%s-%s", id, fieldname); 475 | 476 | filename = uploadprogress_mk_filename(data_identifier, template); 477 | 478 | if (!filename) { 479 | if (data_identifier) { 480 | efree(data_identifier); 481 | } 482 | 483 | return; 484 | } 485 | 486 | stream = php_stream_open_wrapper(filename, "rb", options, NULL); 487 | 488 | if (!stream) { 489 | if (data_identifier) { 490 | efree(data_identifier); 491 | } 492 | 493 | if (filename) { 494 | efree(filename); 495 | } 496 | 497 | RETURN_FALSE; 498 | } 499 | 500 | /* Uses mmap if possible. */ 501 | contents = php_stream_copy_to_mem(stream, maxlen, 0); 502 | 503 | if (contents) { 504 | len = contents->len; 505 | } 506 | 507 | if (contents && len > 0) { 508 | RETVAL_STR(contents); 509 | } else { 510 | RETVAL_EMPTY_STRING(); 511 | } 512 | 513 | php_stream_close(stream); 514 | 515 | if (data_identifier) { 516 | efree(data_identifier); 517 | } 518 | 519 | if (filename) { 520 | efree(filename); 521 | } 522 | 523 | return; 524 | } 525 | } 526 | /* }}} */ 527 | 528 | #endif /* HAVE_UPLOADPROGRESS */ 529 | --------------------------------------------------------------------------------