/* +----------------------------------------------------------------------+ | Uploadprogress extension | +----------------------------------------------------------------------+ | Copyright (c) 2006-2008 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Christian Stocker (chregu@liip.ch) | | Derived from: Doru Petrescu (pdoru-php-upm@kappa.ro)    | | http://pdoru.from.ro/upload-progress-meter/ | +----------------------------------------------------------------------+ */ /* $Id$ */ #include "php_uploadprogress.h" #include "rfc1867.h" #if HAVE_UPLOADPROGRESS #ifdef P_tmpdir #define TMPDIR P_tmpdir #else #define TMPDIR "/tmp" #endif /* {{{ uploadprogress_functions[] */ zend_function_entry uploadprogress_functions[] = { PHP_FE(uploadprogress_get_info, NULL) PHP_FE(uploadprogress_get_contents, NULL) { NULL, NULL, NULL } }; /* }}} */ PHP_INI_BEGIN() PHP_INI_ENTRY("uploadprogress.file.filename_template", TMPDIR"/upt_%s.txt", PHP_INI_ALL, NULL) PHP_INI_ENTRY("uploadprogress.file.contents_template", TMPDIR"/upload_contents_%s", PHP_INI_ALL, NULL) PHP_INI_ENTRY("uploadprogress.get_contents", "0", PHP_INI_ALL, NULL) PHP_INI_END() /* {{{ uploadprogress_module_entry */ zend_module_entry uploadprogress_module_entry = { STANDARD_MODULE_HEADER, "uploadprogress", uploadprogress_functions, PHP_MINIT(uploadprogress), /* Replace with NULL if there is nothing to do at php startup */ PHP_MSHUTDOWN(uploadprogress), /* Replace with NULL if there is nothing to do at php shutdown */ PHP_RINIT(uploadprogress), /* Replace with NULL if there is nothing to do at request start */ PHP_RSHUTDOWN(uploadprogress), /* Replace with NULL if there is nothing to do at request end */ PHP_MINFO(uploadprogress), PHP_UPLOADPROGRESS_VERSION, STANDARD_MODULE_PROPERTIES }; /* }}} */ #ifdef COMPILE_DL_UPLOADPROGRESS ZEND_GET_MODULE(uploadprogress) #endif PHPAPI extern int (*php_rfc1867_callback)(unsigned int , void *, void ** TSRMLS_DC); /* {{{ uploadprogress_php_rfc1867_file */ static int uploadprogress_php_rfc1867_file(unsigned int event, void *event_data, void **data TSRMLS_DC) { zval handler; char *callable = NULL; uploadprogress_data * progress; int read_bytes; zend_bool get_contents = INI_BOOL("uploadprogress.get_contents"); progress = *data; if (event == MULTIPART_EVENT_START) { multipart_event_start *e_data; e_data = (multipart_event_start*) event_data; progress = emalloc( sizeof(uploadprogress_data) ); progress->upload_id = NULL; progress->fieldname = NULL; progress->data_filename = NULL; progress->bytes_total = e_data->content_length; progress->identifier = NULL; progress->identifier_tmp = NULL; progress->time_start = time(NULL); *data = progress; } else if (event == MULTIPART_EVENT_FORMDATA) { multipart_event_formdata *e_data; e_data = (multipart_event_formdata*) event_data; read_bytes = e_data->post_bytes_processed; if (e_data->newlength) { *e_data->newlength = e_data->length; } if (strcmp(e_data->name, "UPLOAD_IDENTIFIER") == 0) { char * upload_id; char * template = INI_STR("uploadprogress.file.filename_template"); if (strcmp(template, "") == 0) { return 0; } upload_id = emalloc(strlen(*e_data->value) + 1); strcpy(upload_id, *e_data->value); progress->upload_id = upload_id; progress->time_last = time(NULL); progress->speed_average = 0; progress->speed_last = 0; progress->bytes_uploaded = read_bytes; progress->files_uploaded = 0; progress->est_sec = 0; progress->identifier = uploadprogress_mk_filename(upload_id, template); progress->identifier_tmp = emalloc(strlen( progress->identifier) + 4); sprintf( progress->identifier_tmp, "%s.wr", progress->identifier ); } } if (progress->identifier) { time_t crtime = time(NULL); int d,dt,ds; if (event == MULTIPART_EVENT_FILE_START) { char * data_identifier; multipart_event_file_start *e_data; e_data = (multipart_event_file_start*) event_data; read_bytes = e_data->post_bytes_processed; progress->fieldname = e_data->name; progress->filename = *e_data->filename; data_identifier = emalloc(strlen(progress->upload_id) + strlen(progress->fieldname) + 2); sprintf(data_identifier, "%s-%s", progress->upload_id, progress->fieldname); if (get_contents) { char * data_template = INI_STR("uploadprogress.file.contents_template"); if (strcmp(data_template, "") == 0) { return 0; } progress->data_filename = uploadprogress_mk_filename(data_identifier, data_template); } } else if (event == MULTIPART_EVENT_FILE_DATA) { multipart_event_file_data *e_data; e_data = (multipart_event_file_data*) event_data; read_bytes = e_data->post_bytes_processed; if (get_contents) { php_stream *stream; int options = ENFORCE_SAFE_MODE; stream = php_stream_open_wrapper(progress->data_filename, "ab", options, NULL); if (stream) { php_stream_write(stream, e_data->data, e_data->length); } php_stream_close(stream); } } else if (event == MULTIPART_EVENT_FILE_END) { multipart_event_file_end *e_data; e_data = (multipart_event_file_end*) event_data; read_bytes = e_data->post_bytes_processed; progress->files_uploaded++; if (get_contents) { VCWD_UNLINK(progress->data_filename); efree(progress->data_filename); } } else if ( event == MULTIPART_EVENT_END ) { VCWD_UNLINK( progress->identifier ); efree( progress->identifier ); efree( progress->identifier_tmp ); efree( progress ); return 0; } if (progress->time_last > crtime) { /* just in case we encounter a fracture in time */ progress->time_last = crtime; } dt = crtime - progress->time_last; ds = crtime - progress->time_start; d = read_bytes - progress->bytes_uploaded; if (dt) { progress->speed_last = d/dt; progress->time_last = crtime; progress->bytes_uploaded = read_bytes; progress->speed_average = ds ? read_bytes / ds : 0; progress->est_sec = progress->speed_average ? (progress->bytes_total - read_bytes) / progress->speed_average : -1; } if (dt || event >= MULTIPART_EVENT_FILE_END) { FILE *F; F = VCWD_FOPEN(progress->identifier_tmp, "wb"); if (F) { fprintf(F, "upload_id=%s\nfieldname=%s\nfilename=%s\ntime_start=%d\ntime_last=%d\nspeed_average=%d\nspeed_last=%d\nbytes_uploaded=%d\nbytes_total=%d\nfiles_uploaded=%d\nest_sec=%d\n", progress->upload_id, progress->fieldname, progress->filename, progress->time_start, progress->time_last, progress->speed_average, progress->speed_last, progress->bytes_uploaded, progress->bytes_total, progress->files_uploaded, progress->est_sec ); fclose(F); /* VCWD_RENAME on WIN32 and PHP < 5.3 has a bug, if target does exist */ #ifdef PHP_WIN32 #if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3 VCWD_UNLINK(progress->identifier); #endif #endif VCWD_RENAME(progress->identifier_tmp,progress->identifier); } } } if (event == MULTIPART_EVENT_END ) { if (progress->identifier) { efree( progress->identifier ); } if (progress->identifier_tmp) { efree( progress->identifier_tmp ); } if (get_contents && progress->data_filename) { efree(progress->data_filename); } efree( progress ); } } /* }}} */ /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(uploadprogress) { REGISTER_INI_ENTRIES(); php_rfc1867_callback = uploadprogress_php_rfc1867_file; return SUCCESS; } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(uploadprogress) { UNREGISTER_INI_ENTRIES(); php_rfc1867_callback = NULL; return SUCCESS; } /* }}} */ /* {{{ PHP_RINIT_FUNCTION */ PHP_RINIT_FUNCTION(uploadprogress) { return SUCCESS; } /* }}} */ /* {{{ PHP_RSHUTDOWN_FUNCTION */ PHP_RSHUTDOWN_FUNCTION(uploadprogress) { return SUCCESS; } /* }}} */ /* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(uploadprogress) { php_info_print_table_start(); php_info_print_table_header(2, "uploadprogress support", "enabled"); php_info_print_table_row(2, "Version", PHP_UPLOADPROGRESS_VERSION); php_info_print_table_end(); DISPLAY_INI_ENTRIES(); } /* }}} */ /* {{{ proto array uploadprogress_get_info(string identifier) */ PHP_FUNCTION(uploadprogress_get_info) { char * id; int id_lg; char method; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &id, &id_lg) == FAILURE) { return; } uploadprogress_file_php_get_info( id, return_value ); return; } /* }}} */ /* {{{ proto string uploadprogress_get_contents(string identifier, string fieldname[, int maxlen]) */ PHP_FUNCTION(uploadprogress_get_contents) { char *id, *fieldname; int id_len, fieldname_len; long maxlen = PHP_STREAM_COPY_ALL; zend_bool get_contents = INI_BOOL("uploadprogress.get_contents"); if (!get_contents) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "this function is disabled; set uploadprogress.get_contents = On to enable it"); RETURN_FALSE; return; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &id, &id_len, &fieldname, &fieldname_len, &maxlen) == FAILURE) { return; } if (ZEND_NUM_ARGS() == 3 && maxlen < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "length must be greater than or equal to zero"); RETURN_FALSE; } uploadprogress_file_php_get_contents(id, fieldname, maxlen, return_value); return; } /* }}} */ /* {{{ uploadprogress_mk_filename */ static char * uploadprogress_mk_filename(char * identifier, char * template) { char * x; char * filename; filename = emalloc( strlen(template) + strlen(identifier) + 3 ); x = strstr( template, "%s" ); if (x==NULL) { sprintf( filename, "%s/%s", template, identifier ); }else{ strcpy( filename, template ); strcpy( filename + (x - template), identifier ); strcat( filename, x+2 ); } return filename; } /* }}} */ /* {{{ uploadprogress_file_php_get_info */ static void uploadprogress_file_php_get_info(char * id, zval * return_value) { char s[1024]; char * filename; char * template; FILE *F; TSRMLS_FETCH(); template = INI_STR("uploadprogress.file.filename_template"); if (strcmp(template, "") == 0) { return; } else { filename = uploadprogress_mk_filename( id, template ); if (!filename) return; F = VCWD_FOPEN(filename, "rb"); if (F) { array_init(return_value); while ( fgets(s, 1000, F) ) { char *k, *v, *e; int index = 0; e = strchr(s,'='); if (!e) continue; *e = 0; /* break the line into 2 parts */ v = e+1; k = s; /* trim spaces in front of the name/value */ while (*k && *k <= 32) k++; while (*v && *v <= 32) v++; /* trim spaces everywhere in the name */ for (e=k; *e; e++) if (*e <= 32) { *e = 0; break; } /* trim spaces only at the end of the value */ /* http://pecl.php.net/bugs/bug.php?id=14525 */ //for (e=v; *e; e++) if (*e <= 32) { *e = 0; break; } if (v != NULL) { for (index = strlen(v); index > 0; index--) { if (v[index] > 32) break; v[index] = 0; } } add_assoc_string( return_value, k, v, 1 ); } fclose(F); } if (filename) efree(filename); return; } } /* }}} */ /* {{{ uploadprogress_file_php_get_contents */ static void uploadprogress_file_php_get_contents(char *id, char *fieldname, long maxlen, zval *return_value) { char *filename, *template, *contents, *data_identifier; php_stream *stream; int options = ENFORCE_SAFE_MODE; int len, newlen; TSRMLS_FETCH(); template = INI_STR("uploadprogress.file.contents_template"); if (strcmp(template, "") == 0) { return; } else { data_identifier = emalloc(strlen(id) + strlen(fieldname) + 2); sprintf(data_identifier, "%s-%s", id, fieldname); filename = uploadprogress_mk_filename(data_identifier, template); if (!filename) return; stream = php_stream_open_wrapper(filename, "rb", options, NULL); if (!stream) { RETURN_FALSE; } /* uses mmap if possible */ if ((len = php_stream_copy_to_mem(stream, &contents, maxlen, 0)) > 0) { #if PHP_API_VERSION < 20100412 if (PG(magic_quotes_runtime)) { contents = php_addslashes(contents, len, &newlen, 1 TSRMLS_CC); len = newlen; } #endif RETVAL_STRINGL(contents, len, 0); } else if (len == 0) { RETVAL_EMPTY_STRING(); } else { RETVAL_FALSE; } php_stream_close(stream); if (data_identifier) efree(data_identifier); if (filename) efree(filename); return; } } /* }}} */ #endif /* HAVE_UPLOADPROGRESS */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */