return $str;
}
-function format_filename($str) {
- $str = strtolower($str);
- $str = ereg_replace('[^a-z0-9_.-]', '_', $str);
+function format_filename($str, $allow_uppercase = false) {
+ if(!$allow_uppercase) {
+ $str = strtolower($str);
+ }
+ $str = ereg_replace('[^a-zA-Z0-9_.-]', '_', $str);
return ereg_replace('^[.-]', '_', $str);
}
+function client_path_to_filename($path) {
+ $filename = ereg_replace(".*[:/\\]", '', $path);
+ return format_filename($filename, true);
+}
+
+
function format_h_w_image($str) {
$fields = explode(' ', $str);
if(count($fields) != 3) {
return $url;
}
+# just the hostname, no port number
+function this_host() {
+ if($_SERVER['HTTP_HOST']) {
+ $host = $_SERVER['HTTP_HOST'];
+ $p = strpos($host, ':');
+ if($p) {
+ $host = substr($host, 0, $p);
+ }
+ return $host;
+ } else {
+ return $_SERVER['SERVER_NAME'];
+ }
+}
+
+
+
# return our best guess at the url used to access this page
function this_url() {
$url = this_url_sans_path();
# along with wfpl; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-# you'll need this file that calls db_connect()
-if(!isset($GLOBALS['wfpl_db_handle'])) {
- if(file_exists('db_connect.php')) {
- require_once('db_connect.php');
- } elseif(file_exists('code/db_connect.php')) {
- require_once('code/db_connect.php');
- } else {
- die("session.php requires a file db_connect.php or that you call db_connect() first. See code/wfpl/db.php for more information.");
- }
-}
-# and these database tables:
+# you'll need these database tables:
# create table wfpl_sessions (id int unique auto_increment, session_key varchar(16), length int, expires int);
# create table wfpl_session_data (id int unique auto_increment, session_id int, name varchar(100), value text);
# run this command to install/clear the tables:
--- /dev/null
+<?php
+
+require_once('code/wfpl/template.php');
+require_once('code/wfpl/encode.php');
+require_once('code/wfpl/session.php');
+require_once('code/wfpl/upload.php'); # FIXME for path_to() which should be somewhere else
+
+# This function is for making an uploader with a progress bar.
+#
+# Parameter: (optional)
+# progress_url: URL javascript should use to get progress updates (defaults to this_url() with the query string replaced with ?wfpl_upload_progress=FILENAME (where FILENAME is the first parameter.))
+#
+# You must also set $GLOBALS['wfpl_uploader_port'] to an available port for the upload receiver to run on.
+#
+# Returns: (an array containing)
+# html
+# css
+# javascript
+# filename
+
+function uploader($progress_url = '') {
+ if(!$filename) {
+ $filename = strtolower(session_generate_key());
+ }
+ if(!$progress_url) {
+ $progress_url = this_url();
+ $q = strpos($progress_url, '?');
+ if($q) {
+ $progress_url = substr($progress_url, 0, $q);
+ }
+ $progress_url .= '?wfpl_upload_progress=' . enc_url_val($filename);
+ }
+ if(!$GLOBALS['wfpl_uploader_host']) {
+ $GLOBALS['wfpl_uploader_host'] = this_host();
+ }
+
+ $html = new tem();
+ $html->load('code/wfpl/uploader/uploader.html');
+ $html->set('filename', $filename);
+ $html->set('host', $GLOBALS['wfpl_uploader_host']);
+ $html->set('port', $GLOBALS['wfpl_uploader_port']);
+ $html->show('main');
+ $html = $html->get('main');
+
+ $css = read_whole_file('code/wfpl/uploader/uploader.css');
+
+ $javascript = new tem();
+ $javascript->load('code/wfpl/uploader/progress.js');
+ $javascript->set('url', $progress_url);
+ $javascript = $javascript->run();
+
+ uploader_daemon_start($GLOBALS['wfpl_uploader_port']);
+
+ return array($html, $css, $javascript, $filename);
+}
+
+function uploader_move($tmp_filename, $filename) {
+ $tmp_path = $GLOBALS['wfpl_uploader_path'] . '/partial/' . $tmp_filename;
+ $out_path = $GLOBALS['wfpl_uploader_path'] . '/' . $filename;
+ unlink($GLOBALS['wfpl_uploader_path'] . '/progress/' . $tmp_filename);
+ rename($tmp_path, $out_path);
+}
+
+# start a daemon to accept file uploads and give progress indicators
+# if the port is used (eg if the daemon is already running) this will do nothing.
+function uploader_daemon_start($port) {
+ exec(path_to('tcpserver') . " -q -R -H -llocalhost 0 $port " . path_to('perl') . ' code/wfpl/uploader/daemon.pl ' . $GLOBALS['wfpl_uploader_path'] . ' >/dev/null 2>/dev/null < /dev/null &');
+}
+
+/* call this to respond to the javascript async request for progress on the upload */
+function wfpl_uploader_progress() {
+ if(!isset($_REQUEST['wfpl_upload_progress'])) {
+ return;
+ }
+
+ # allow this script to run for 8 hours
+ set_time_limit(28800);
+
+ $file = $_REQUEST['wfpl_upload_progress'];
+ $file = strtolower($file);
+ $file = ereg_replace('[^a-z0-9.-]', '_', $file);
+ $file = ereg_replace('^[.-]', '_', $file);
+ $file = $GLOBALS['wfpl_uploader_path'] . "/progress/$file";
+
+ $waited = 0;
+ while(!file_exists($file)) {
+ usleep(500000);
+ ++$waited;
+ if($waited > 100) {
+ return;
+ }
+ }
+
+ $progress_sent = 0;
+ while(true) {
+ clearstatcache();
+ $stats = stat($file);
+ if($stats !== false) {
+ $progress = $stats['size'];
+ if($progress > $progress_sent) {
+ print(substr('............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................', 0, $progress - $progress_sent));
+ flush();
+ $progress_sent = $progress;
+ if($progress == 1000) {
+ return;
+ }
+ }
+ }
+ usleep(500000); # wait half a second
+ }
+}
--- /dev/null
+#!/usr/bin/perl
+
+# FIXME rewrite to use non-blocking IO and put limits on waiting
+
+#use Fcntl;
+
+#$flags = '';
+#fcntl(HANDLE, F_GETFL, $flags)
+# or die "Couldn't get flags for HANDLE : $!\n";
+#$flags |= O_NONBLOCK;
+#fcntl(HANDLE, F_SETFL, $flags)
+# or die "Couldn't set flags for HANDLE: $!\n";
+#
+#Once a filehandle is set for non-blocking I/O, the sysread or syswrite calls that would block will instead return undef and
+#set $! to EAGAIN:
+
+
+use strict;
+
+use vars qw($output_path $g_filename $flags $buffer $the_end $refills_at_end $content_length $bytes_written $progress_written $bytes_left $boundary);
+
+$output_path = $ARGV[0];
+
+$the_end = 0;
+$content_length = -1;
+$boundary = '';
+$refills_at_end = 0;
+$bytes_left = 4000; # if the headers are bigger than this... too bad
+# FIXME if the entire request (including file contents) is less than 4000 this causes the program to hang. This happens with firefox when the file is deleted before hitting submit
+
+sub refill_buffer {
+ my $ret;
+ my $size;
+ my $max_read;
+ $size = length $buffer;
+ if($the_end == 1 || $bytes_left - $size < 1) {
+ $refills_at_end += 1;
+ if($refills_at_end > 10) {
+ die('refill_buffer called too many times (11) after EOF was reached');
+ }
+ return;
+ }
+ return unless $size < 1000;
+ $max_read = (1100 - $size);
+ if($max_read > ($bytes_left - $size)) {
+ $max_read = ($bytes_left - $size);
+ }
+ $ret = sysread STDIN, $buffer, $max_read, $size;
+ if($ret == 0) {
+ $the_end = 1;
+ } elsif($ret == undef) {
+ die("read returned: " . $!);
+ }
+}
+
+# remove x bytes from buffer and return them
+# read_line doesn't use this, but keeps bytes_count anyway
+sub read_buff {
+ my $count = shift;
+ my $str;
+ $str = substr $buffer, 0, $count;
+ $buffer = substr $buffer, $count;
+ $bytes_left -= $count;
+ return $str;
+}
+
+# mark the entire buffer as used
+sub buffer_used {
+ $bytes_left -= length $buffer;
+ $buffer = '';
+}
+
+# returns the next line from the input stream (not including the trailing crlf)
+sub read_line {
+ my $size;
+ my $crlf_index;
+ my $line;
+ refill_buffer();
+ $size = length $buffer;
+ $crlf_index = index $buffer, "\r\n";
+ if($crlf_index < 0) {
+ die("expected a line, but didn't find a CRLF for $size characters (bytes_left: $bytes_left)");
+ }
+ $line = substr $buffer, 0, $crlf_index;
+ $buffer = substr $buffer, ($crlf_index + 2);
+ $bytes_left -= $crlf_index + 2;
+ return $line;
+}
+
+sub parse_main_headers {
+ my $line;
+ my $i;
+
+ $line = read_line;
+ $i = index($line, '/');
+ die(500) if $i < 0;
+ $line = substr($line, $i + 1);
+ $i = index($line, ' ');
+ die(501) if $i < 0;
+ $line = substr($line, 0, $i);
+
+ if($line eq '') {
+ # FIXME return 404?
+ die('no filename passed');
+ }
+
+ $line = lc($line);
+ $line =~ s/[^a-z0-9.-]/_/g;
+ $line =~ s/^[.-]/_/;
+
+ $g_filename = $line;
+
+
+
+ while(1) {
+ $line = read_line;
+ if(substr(lc($line), 0, 16) eq 'content-length: ') {
+ $content_length = substr($line, 16);
+ } elsif(substr(lc($line), 0, 14) eq 'content-type: ') {
+ $i = index(lc($line), 'boundary=');
+ if($i < 0) {
+ die('no boundary= in content-type header');
+ }
+ $boundary = substr $line, ($i + 9);
+ } elsif($line eq '') {
+ if($content_length == -1) {
+ die('No Content-Length header');
+ }
+ if($boundary eq "") {
+ die('No boundary found in headers');
+ }
+ $boundary = '--' . $boundary;
+ $bytes_left = $content_length;
+ return;
+ }
+ }
+}
+
+# pass int from 0-1000
+sub progress_bar_update {
+ my $pct = shift;
+ my $dots;
+ if($pct > $progress_written) {
+ syswrite(PROGRESS_FD, '............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................', $pct - $progress_written);
+ $progress_written = $pct;
+ }
+}
+
+sub progress_bar_start {
+ my $progress_filename = shift; # global
+ $progress_written = 0;
+ $bytes_written = 0;
+ open PROGRESS_FD, ">$progress_filename";
+}
+
+sub progress_bar_finish {
+ progress_bar_update(1000);
+ close PROGRESS_FD;
+}
+
+
+# save bytes past and update progress bar
+sub output {
+ my $out = shift;
+ my $prog;
+ print FD $out;
+
+ # update progressbar
+ $bytes_written += length($out);
+ $prog = $bytes_written / $content_length; # FIXME off by size of headers. do we care?
+ $prog = int($prog * 999 + .99);
+ progress_bar_update($prog);
+}
+
+sub save_to_next_boundary {
+ my $filename = shift;
+ my $i;
+ my $crlfboundary = "\r\n$boundary";
+ open FD, ">$output_path/partial/$filename";
+ progress_bar_start("$output_path/progress/$filename");
+ while(1) {
+ refill_buffer;
+ $i = index $buffer, $crlfboundary;
+ if($i < 0) {
+ output $buffer;
+ buffer_used;
+ } else {
+ if ($i > 0) {
+ output(read_buff($i));
+ }
+ read_buff(2); # remove crlf between content and boundary #FIXME make sure this exists
+ close FD;
+ progress_bar_finish();
+ return;
+ }
+ }
+}
+
+
+sub parse_sub {
+ my $sub_length = -1;
+ my $line;
+ my $i;
+ my $i2;
+
+ while(1) {
+ $line = lc(read_line());
+ if($line eq "") {
+ return save_to_next_boundary($g_filename);
+ }
+ #if(substr($line, 0, 21) eq 'content-disposition: ') {
+ # $i = index($line, 'filename="');
+ # if($i < 0) {
+ # die('no filename=" in content-disposition sub-header');
+ # }
+ # $i2 = index($line, '"', ($i + 10));
+ # if($i2 < 0) {
+ # die('no filename=" in content-disposition sub-header');
+ # }
+ # $filename = lc(substr($line, ($i + 10), ($i2 - ($i + 10))));
+ # $filename =~ s/[^a-z0-9.-]/_/g;
+ # $filename =~ s/^[.-]/_/;
+ #} elsif($line eq '') {
+ # if($filename eq "") {
+ # die('No filename found in headers on part');
+ # }
+ # return save_to_next_boundary($filename);
+ #}
+ }
+}
+
+sub reply_and_quit {
+ print "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: text/plain\r\nContent-Length: 8\r\n\r\nReceived";
+ exit 0;
+}
+
+sub parse_body {
+ my $line;
+
+ while(1) {
+ $line = read_line;
+ if($line eq $boundary) {
+ parse_sub;
+ } elsif($line eq ($boundary . '--')) {
+ reply_and_quit;
+ } else {
+ die("Expecting boundary \"$boundary\" but got: \"$line\"");
+ }
+ }
+}
+
+
+#$flags = '';
+#fcntl(STDIN, F_GETFL, $flags)
+# or die "Couldn't get flags for STDIN : $!\n";
+#$flags |= O_NONBLOCK;
+#fcntl(STDIN, F_SETFL, $flags)
+# or die "Couldn't set flags for STDIN: $!\n";
+parse_main_headers;
+parse_body;
--- /dev/null
+function tag(name) {
+ return document.getElementById(name);
+}
+
+var dbg_url;
+function sendRequest(url,callback,postData) {
+ var req = createXMLHTTPObject();
+ if (!req) return;
+ var method = (postData) ? "POST" : "GET";
+ req.open(method,url,true);
+ req.setRequestHeader('User-Agent','XMLHTTP/1.0');
+ if (postData)
+ req.setRequestHeader('Content-type','application/x-www-form-urlencoded');
+ dbg_url = url;
+ req.onreadystatechange = function () {
+ if(req.readyState != 4) {
+ callback(req);
+ return;
+ }
+ if (req.status != 200 && req.status != 304) {
+ /* alert('url:' + dbg_url + ' HTTP error ' + req.status); */
+ progress_start_delayed();
+ return;
+ }
+ callback(req);
+ }
+ if (req.readyState == 4) return;
+ req.send(postData);
+}
+
+var XMLHttpFactories = [
+ function () {return new XMLHttpRequest()},
+ function () {return new ActiveXObject("Msxml2.XMLHTTP")},
+ function () {return new ActiveXObject("Msxml3.XMLHTTP")},
+ function () {return new ActiveXObject("Microsoft.XMLHTTP")}
+];
+
+function createXMLHTTPObject() {
+ var xmlhttp = false;
+ for (var i=0;i<XMLHttpFactories.length;i++) {
+ try {
+ xmlhttp = XMLHttpFactories[i]();
+ }
+ catch (e) {
+ continue;
+ }
+ break;
+ }
+ return xmlhttp;
+}
+
+
+function progress_start() {
+ sendRequest('~url~', progress_update_with);
+}
+
+function progress_start_delayed() {
+ setTimeout(progress_start, 1500);
+}
+
+
+function progress_finished() {
+ var appears;
+ tag('wfpl_progress_header').innerHTML = 'Upload Finished';
+ appears = tag('wfpl_upload_finished');
+ if(appears) {
+ appears.style.position = 'static';
+ }
+ if(wfpl_upload_finished) {
+ wfpl_upload_finished();
+ }
+}
+
+function progress_update_with(rec) {
+ length = rec.responseText.length;
+ bar = tag('wfpl_progress_bar');
+ bar.style.backgroundPosition = (Math.floor(length / 5) - 200) + 'px 0';
+
+ whole = Math.floor(length/10);
+ pct = '' + whole + '.' + (length - (whole * 10));
+ bar.innerHTML = pct + '%';
+
+ if(length == 1000) {
+ progress_finished();
+ }
+}
+
+function submitting() {
+ if(wfpl_upload_starting) {
+ wfpl_upload_starting();
+ }
+ tag('wfpl_progress_form').style.display = 'none';
+ tag('wfpl_progress_section').style.position = 'static';
+ progress_start_delayed();
+}
+
+
+
+
+
+
--- /dev/null
+#wfpl_progress_post_response { display: none; }
+#wfpl_progress_section { position: absolute; left: -2000px; top: 0px; }
+#wfpl_progress_bar_border { width: 200px; height: 16px; border: 1px solid #ddd; font: 11px Verdana; }
+#wfpl_progress_bar { text-align: center; width: 200px; height: 16px; margin: 0 auto 0 0; background: transparent url(images/wfpl_uploader_bar.png) no-repeat -200px 0px; }
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title></title>
+</head>
+
+<body>
+<!--~main start~-->
+ <div id="wfpl_progress_form">
+ <form action="http://~host~:~port~/~filename~" enctype="multipart/form-data" target="wfpl_progress_post_response" method="post">
+ <p>Upload a file: <input type="file" name="wfpl_uploader_file" id="wfpl_uploader_file" /></p>
+
+ <p><input type="submit" onclick="submitting()" value="Submit" /></p>
+ </form>
+ </div>
+
+ <p><iframe id="wfpl_progress_post_response" name="wfpl_progress_post_response"></iframe></p>
+
+ <div id="wfpl_progress_section">
+ <h2 id="wfpl_progress_header">Uploading...</h2>
+
+ <div id="wfpl_progress_bar_border">
+ <div id="wfpl_progress_bar"></div>
+ </div>
+ </div>
+<!--~end~-->
+</body>
+</html>