Hi,
                                                                                       
             
   This is a patch that implements a hooking mechanism into the
rfc1867_post_handler() function that process post content of type
multipart/form-data, for example when uploading files. This will allow
execution of a callback routine while php reads and process the incoming data.
This happens before the actual script is loaded and executed. This will make
possible for an extension to register a callback, that will do something
interesting like an upload-progress-meter for the incoming uploads.
                                                                                       
             
   This wishes to be a better approach to do U-P-M than my previous patch, that
was inserting ugly code directly into rfc1867_post_handler() function. This
new implementation provides separation of code, and a standard hooking
mechanism, whatever you need to do.
                                                                                       
             
   This patch define a new configuration option 'upload_progress_tracking'
(default off). You will have to turn it ON, in order for U-P-M to work. This
is intended to reduce the number of wasted cpu cycles when this feature is not
needed. You should turn this on only for the directories/files that actually
need and use this.
                                                                                       
             
   Security considerations: data received from client is checked for valid
characters and valid length before doing anything else with it.
                                  

   What can be done with this ? well... this was intended to be a way to do 
an upload progress meter, but you can do a lot of other things, even RUN PHP 
CODE while data is uploaded. I have a quick&dirt patch available if anyone want 
to play with ...

                                                                  
   See the attached README file for more technical details.
   BTW, patch is against php-4.3.4

--

Best regards,
Doru Petrescu
Senior Software Engineer
Astral Telecom Bucuresti
                                                                                       
             



diff -rubB orig/php-4.3.4/main/main.c php-4.3.4/main/main.c
--- orig/php-4.3.4/main/main.c	2003-10-09 05:59:03.000000000 +0300
+++ php-4.3.4/main/main.c	2003-11-19 10:05:00.000000000 +0200
@@ -343,6 +343,7 @@
 	STD_PHP_INI_ENTRY("upload_max_filesize",	"2M",		PHP_INI_SYSTEM|PHP_INI_PERDIR,		OnUpdateInt,			upload_max_filesize,	php_core_globals,	core_globals)
 	STD_PHP_INI_ENTRY("post_max_size",			"8M",		PHP_INI_SYSTEM|PHP_INI_PERDIR,		OnUpdateInt,			post_max_size,			sapi_globals_struct,sapi_globals)
 	STD_PHP_INI_ENTRY("upload_tmp_dir",			NULL,		PHP_INI_SYSTEM,		OnUpdateStringUnempty,	upload_tmp_dir,			php_core_globals,	core_globals)
+	STD_PHP_INI_ENTRY("upload_progress_tracking",		"0",		PHP_INI_ALL,		OnUpdateBool,			upload_progress_tracking,		php_core_globals,   core_globals)
 
 	STD_PHP_INI_ENTRY("user_dir",				NULL,		PHP_INI_SYSTEM,		OnUpdateString,			user_dir,				php_core_globals,	core_globals)
 	STD_PHP_INI_ENTRY("variables_order",		NULL,		PHP_INI_ALL,		OnUpdateStringUnempty,	variables_order,		php_core_globals,	core_globals)
diff -rubB orig/php-4.3.4/main/php_globals.h php-4.3.4/main/php_globals.h
--- orig/php-4.3.4/main/php_globals.h	2003-05-18 13:22:16.000000000 +0300
+++ php-4.3.4/main/php_globals.h	2003-11-19 09:19:58.000000000 +0200
@@ -133,6 +133,7 @@
 	zend_bool modules_activated;
 
 	zend_bool file_uploads;
+	zend_bool upload_progress_tracking;
 
 	zend_bool during_request_startup;
 
diff -rubB orig/php-4.3.4/main/rfc1867.c php-4.3.4/main/rfc1867.c
--- orig/php-4.3.4/main/rfc1867.c	2003-10-26 09:47:24.000000000 +0200
+++ php-4.3.4/main/rfc1867.c	2003-11-20 09:37:30.000000000 +0200
@@ -61,6 +61,104 @@
 #define UPLOAD_ERROR_C    3  /* Partially uploaded */
 #define UPLOAD_ERROR_D    4  /* No file uploaded */
 
+
+
+/* Defines for upload progress tracking callback */
+#define UPLOAD_UPDATE            0
+#define UPLOAD_DONE             -1
+#define UPLOAD_GOT_A_NEW_FILE   +1
+
+#define UPLOAD_PROGRESS_UPDATE( progress, why )			\
+	if (upload_progress_active > 1) { 			\
+		upload_progress_callback( progress,		\
+				SG(read_post_bytes), SG(request_info).content_length, why );	\
+	}
+
+int upload_progress_init( void**, char*);
+int upload_progress_register_callback( void* );
+void (*upload_progress_callback)( void*, int, int, int) = NULL;
+
+/*
+NOTES: 
+
+prototype for the callback:
+void upload_progress_callback( void *pointer, int read_bytes, int total_bytes, int what_happened )
+
+   
+1. first time call, to initialize: 
+upload_progress_callback( &tmp_pointer, 0, total, 0 )
+	tmp_pointer is an INPUT/OUTPUT parameter!
+	on INPUT: tmp_pointer = (char*) UPLOAD_METTER_ID. his value was pre checked for 
+		valid chars ( a..z A..Z 0..9 . and _ ) and valid length (16..32)
+	on OUTPUT: tmp_pointer will return the value of the 'progress' opaque pointer, that will 
+		be used by the callback to store its data. it is the callback responsability
+		to alloc any storage space it requires, and to free it at the end.
+
+	on INIT call, read_bytes parameter will have a ZERO value, while total_bytes will 
+	have a greater than zero value.
+	on any other call read_bytes will have a greater than zero value guaranteed!
+
+2. regular update data call:
+upload_progress_callback( progress, read, total, what_happened )
+	progress: the opaque pointer returned by the init call
+    read: the new number of bytes read so far. this will always be greater than zero,
+		but might not always be greater than the last value received
+	total: total number of bytes. this value will not change
+    what_happened:
+		- a NEGATIVE value (UPLOAD_DONE) means end-of-upload. this is the last call.
+		- a POZITIVE value (UPLOAD_GOT_A_NEW_FILE) means we've just seen another file 
+			uploaded, more might come. just ++nr_of_uploaded_files_counter.
+		- a ZERO value (UPLOAD_UPDATE) means nothing unusual, just the regular update.
+
+	When the upload finish, either because of an error (read<total), or normaly (read=total),
+	you need to free any resources you previously allocated (like the progress opaque pointer)
+	Just make sure you do not free it before you receive the UPLOAD_DONE signal, or SIGSEGV will 
+	kill us all!
+	
+*/
+
+int upload_progress_init( void **progress, char *value)
+{
+   char *c,*v = estrdup(value);
+   int  ok = 1;
+
+   if (!upload_progress_callback || !SG(request_info).content_length) {
+	  return 0;
+   }
+
+   
+   /* make sure the identifier does not contain strnage things */
+   for (c=v;*c;c++) {
+      if ( (*c >= '0' && *c <= '9') || (*c == '.') || (*c == '_') ||
+	   (*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z')   ) {
+      }else{
+	 ok = 0; /* reject anything dubious */
+	 break;
+      }
+   }
+
+   if (v && *v && ok) {
+      if (strlen(v) > 32) v[32]=0;
+      if (strlen(v) < 16) ok = 0;
+   }else{
+      ok = 0;
+   }
+
+   if (ok) {
+//      snprintf(progress->upload_identifier,32, "%s", v);
+	  void *tmp = v;
+	  upload_progress_callback( &tmp, 0, SG(request_info).content_length, 0);
+	  if (tmp && progress) *progress = tmp;
+   }
+
+   efree(v);
+   return (ok ? 2:0);
+}
+
+/* end upload progress stuff */
+
+
+
 void php_rfc1867_register_constants(TSRMLS_D)
 {
 	REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_OK",         UPLOAD_ERROR_OK, CONST_CS | CONST_PERSISTENT);
@@ -702,6 +800,9 @@
 #if HAVE_MBSTRING && !defined(COMPILE_DL_MBSTRING)
 	int str_len=0;
 #endif
+	void * progress = NULL;
+	int    upload_progress_active = PG(upload_progress_tracking) ? 1:0; 
+										/* 0=inactive, 1=active, 2=actualy tracking something */
 
 	if (SG(request_info).content_length > SG(post_max_size)) {
 		sapi_module.sapi_error(E_WARNING, "POST Content-Length of %d bytes exceeds the limit of %d bytes", SG(request_info).content_length, SG(post_max_size));
@@ -762,6 +863,7 @@
 		zend_llist_clean(&header);
 
 		if (!multipart_buffer_headers(mbuff, &header TSRMLS_CC)) {
+			UPLOAD_PROGRESS_UPDATE( progress, UPLOAD_DONE );
 			SAFE_RETURN;
 		}
 
@@ -823,6 +925,10 @@
 					max_file_size = atol(value);
 				}
 
+				if (upload_progress_active && !strcmp(param, "UPLOAD_IDENTIFIER")) {
+					upload_progress_active = upload_progress_init( &progress, value );
+				}
+
 				efree(param);
 				efree(value);
 				continue;
@@ -845,9 +951,12 @@
 				if (filename) {
 					efree(filename);
 				}
+				UPLOAD_PROGRESS_UPDATE( progress, UPLOAD_DONE );
 				SAFE_RETURN;
 			}
 
+			UPLOAD_PROGRESS_UPDATE( progress, UPLOAD_GOT_A_NEW_FILE );
+
 			if (!skip_upload) {
 				/* Handle file */
 				fp = php_open_temporary_file(PG(upload_tmp_dir), "php", &temp_filename TSRMLS_CC);
@@ -874,6 +983,8 @@
 
 			while (!cancel_upload && (blen = multipart_buffer_read(mbuff, buff, sizeof(buff) TSRMLS_CC)))
 			{
+				UPLOAD_PROGRESS_UPDATE( progress, UPLOAD_UPDATE );
+
 				if (PG(upload_max_filesize) > 0 && total_bytes > PG(upload_max_filesize)) {
 					sapi_module.sapi_error(E_WARNING, "upload_max_filesize of %ld bytes exceeded - file [%s=%s] not saved", PG(upload_max_filesize), param, filename);
 					cancel_upload = UPLOAD_ERROR_A;
@@ -1069,6 +1180,7 @@
 		}
 	}
 
+	UPLOAD_PROGRESS_UPDATE( progress, UPLOAD_DONE );
 	SAFE_RETURN;
 }
 
What is this ?
==============


   This is a patch that implements a hooking mechanism into the
rfc1867_post_handler() function that process post content of type
multipart/form-data, for example when uploading files. This will allow
execution of a callback routine while php reads and process the incoming data.
This happens before the actual script is loaded and executed. This will make
possible for an extension to register a callback, that will do something
interesting like an upload-progress-meter for the incoming uploads.

   This wishes to be a better approach to do U-P-M than my previous patch, that
was inserting ugly code directly into rfc1867_post_handler() function. This
new implementation provides separation of code, and a standard hooking
mechanism, whatever you need to do.

   This patch define a new configuration option 'upload_progress_tracking'
(default off). You will have to turn it ON, in order for U-P-M to work. This
is intended to reduce the number of wasted cpu cycles when this feature is not
needed. You should turn this on only for the directories/files that actually
need and use this.

   Security considerations: data received from client is checked for valid
characters and valid length before doing anything else with it.



How to use Upload-Progress-Tracking:
===================================


 First you have to enable it. Use:
        upload_progress_tracking = On
 or
        php_value upload_progress_tracking 1

 Then you will need to load and configure a PHP extension that do something useful the 
progress
tracking info. See the Upload-Progress-Meter extension for more details on this.

 All being set on the server side, you need a web page with an upload form, with a 
special HIDDEN 
parameter, named UPLOAD_IDENTIFIER, that will have every time a different random 
value. You can 
use either javascript or php script to generate its random value. Valid characters for 
its value 
are: a..z A..Z 0..9 . (dot) and _ (underline). Valid length is 16-32. under 16 it will 
be ignored, 
over 32 it will be truncated to 32. If it is missing, have invalid chars, or is to 
short, it will
be ignored, and upload-tracking-info will not be provided for this upload.
See the Upload-Progress-Meter extension for an working example.



What the Callback:
==================

prototype for the callback:
void upload_progress_callback( void *pointer, int read_bytes, int total_bytes, int 
what_happened )

   
1. first time call, to initialize: 
upload_progress_callback( &tmp_pointer, 0, total, 0 )
        tmp_pointer is an INPUT/OUTPUT parameter!
        on INPUT: tmp_pointer = (char*) UPLOAD_IDENTIFIER. his value was pre-checked 
for 
                valid chars ( a..z A..Z 0..9 . and _ ) and valid length (16..32)
        on OUTPUT: tmp_pointer will return the value of the 'progress' opaque pointer, 
that will 
                be used by the callback to store its data. it is the callback 
responsibility
                to alloc any storage space it requires, and to free it at the end.

        on INIT call, read_bytes parameter will have a ZERO value, while total_bytes 
will 
        have a greater than zero value.
        on any other call read_bytes will have a greater than zero value guaranteed!

2. regular update data call:
upload_progress_callback( progress, read, total, what_happened )
        progress: the opaque pointer returned by the previous init call
    read: the new number of bytes read so far. this will always be greater than zero,
                but might not always be greater than the last value received
        total: total number of bytes. this value will not change
    what_happened:
                - a NEGATIVE value (UPLOAD_DONE) means end-of-upload. this is the last 
call.
                - a POSITIVE value (UPLOAD_GOT_A_NEW_FILE) means we've just seen 
another file 
                        uploaded, more might come. just ++nr_of_uploaded_files_counter.
                - a ZERO value (UPLOAD_UPDATE) means nothing unusual, just the regular 
update.

        When the upload finish, either because of an error (read<total), or normally 
(read=total),
        you need to free any resources you previously allocated (like the progress 
opaque pointer)
        Just make sure you do not free it before you receive the UPLOAD_DONE signal, 
or SIGSEGV will 
        kill us all!



Puting this to work:
====================

Any extension that will use this, need to put this at the top of the file:

        extern int upload_progress_register_callback( void* );

and define a callback like this:


/* begin C code */
void my_callback(void *pointer, int read, int total, int whats_new) 
{ 
        if (total==0) {
                /* INIT time */
                void ** tmp_pointer = pointer;  /* this is actualy (void**) this time. 
see NOTES */
                void * progress;
                char identifier = (char*) pointer;

                /* allocate an internal structure to keep track of things */
                progress = malloc();
                *tmp_pointer = progress;

                /* initialize everything ... */
                
                return;

        }else{

                if (whats_new < 0) {
                        /* last call. its free() time ... */

                   free( pointer );
                   return;
                   
                }else{

                        /* regular UPDATE call */
                        if (whats_new>0) {
                                nr_uploaded_files++;
                        } 
          
                        /* do_stuff... compute upload speed ... whatever */
                        return;
                }
        }
}

/* end of C code */




-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to