upr 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +18 -0
- data/COPYING +339 -0
- data/GIT-VERSION-GEN +40 -0
- data/GNUmakefile +150 -0
- data/LICENSE +55 -0
- data/README +108 -0
- data/Rakefile +119 -0
- data/bin/upr-drb +6 -0
- data/examples/rails_app-2.3.4/Rakefile +10 -0
- data/examples/rails_app-2.3.4/app/controllers/application_controller.rb +3 -0
- data/examples/rails_app-2.3.4/app/controllers/files_controller.rb +41 -0
- data/examples/rails_app-2.3.4/app/helpers/application_helper.rb +3 -0
- data/examples/rails_app-2.3.4/app/models/upr_status.rb +35 -0
- data/examples/rails_app-2.3.4/app/views/files/index.html.erb +66 -0
- data/examples/rails_app-2.3.4/config.ru +3 -0
- data/examples/rails_app-2.3.4/config/boot.rb +110 -0
- data/examples/rails_app-2.3.4/config/database.yml +22 -0
- data/examples/rails_app-2.3.4/config/environment.rb +52 -0
- data/examples/rails_app-2.3.4/config/environments/development.rb +17 -0
- data/examples/rails_app-2.3.4/config/environments/production.rb +28 -0
- data/examples/rails_app-2.3.4/config/environments/test.rb +28 -0
- data/examples/rails_app-2.3.4/config/initializers/new_rails_defaults.rb +21 -0
- data/examples/rails_app-2.3.4/config/routes.rb +43 -0
- data/examples/rails_app-2.3.4/db/.gitignore +2 -0
- data/examples/rails_app-2.3.4/db/migrate/19700000000000_add_upr_status.rb +14 -0
- data/examples/rails_app-2.3.4/db/seeds.rb +7 -0
- data/examples/rails_app-2.3.4/lib/tasks/upr_status.rake +8 -0
- data/examples/rails_app-2.3.4/public/404.html +30 -0
- data/examples/rails_app-2.3.4/public/422.html +30 -0
- data/examples/rails_app-2.3.4/public/500.html +30 -0
- data/examples/rails_app-2.3.4/public/favicon.ico +0 -0
- data/examples/rails_app-2.3.4/public/javascripts/application.js +2 -0
- data/examples/rails_app-2.3.4/public/javascripts/controls.js +963 -0
- data/examples/rails_app-2.3.4/public/javascripts/dragdrop.js +973 -0
- data/examples/rails_app-2.3.4/public/javascripts/effects.js +1128 -0
- data/examples/rails_app-2.3.4/public/javascripts/prototype.js +4320 -0
- data/examples/rails_app-2.3.4/public/javascripts/upr.js +119 -0
- data/examples/rails_app-2.3.4/public/robots.txt +5 -0
- data/examples/rails_app-2.3.4/rainbows_config.rb +13 -0
- data/examples/rails_app-2.3.4/test/test_helper.rb +38 -0
- data/lib/upr.rb +35 -0
- data/lib/upr/input_wrapper.rb +110 -0
- data/lib/upr/monitor.rb +41 -0
- data/local.mk.sample +30 -0
- data/setup.rb +1586 -0
- data/upr.gemspec +40 -0
- metadata +127 -0
data/LICENSE
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
Upload Progress for Rack (upr) is copyrighted Free Software by all
|
2
|
+
contributors, see the logs in revision control for all of them. You can
|
3
|
+
redistribute it and/or modify it under either the terms of the
|
4
|
+
{GPL2}[http://www.gnu.org/licenses/gpl-2.0.txt] (see link:COPYING) or
|
5
|
+
the conditions below:
|
6
|
+
|
7
|
+
1. You may make and give away verbatim copies of the source form of the
|
8
|
+
software without restriction, provided that you duplicate all of the
|
9
|
+
original copyright notices and associated disclaimers.
|
10
|
+
|
11
|
+
2. You may modify your copy of the software in any way, provided that
|
12
|
+
you do at least ONE of the following:
|
13
|
+
|
14
|
+
a) place your modifications in the Public Domain or otherwise make them
|
15
|
+
Freely Available, such as by posting said modifications to Usenet or an
|
16
|
+
equivalent medium, or by allowing the author to include your
|
17
|
+
modifications in the software.
|
18
|
+
|
19
|
+
b) use the modified software only within your corporation or
|
20
|
+
organization.
|
21
|
+
|
22
|
+
c) rename any non-standard executables so the names do not conflict with
|
23
|
+
standard executables, which must also be provided.
|
24
|
+
|
25
|
+
d) make other distribution arrangements with the author.
|
26
|
+
|
27
|
+
3. You may distribute the software in object code or executable
|
28
|
+
form, provided that you do at least ONE of the following:
|
29
|
+
|
30
|
+
a) distribute the executables and library files of the software,
|
31
|
+
together with instructions (in the manual page or equivalent) on where
|
32
|
+
to get the original distribution.
|
33
|
+
|
34
|
+
b) accompany the distribution with the machine-readable source of the
|
35
|
+
software.
|
36
|
+
|
37
|
+
c) give non-standard executables non-standard names, with
|
38
|
+
instructions on where to get the original software distribution.
|
39
|
+
|
40
|
+
d) make other distribution arrangements with the author.
|
41
|
+
|
42
|
+
4. You may modify and include the part of the software into any other
|
43
|
+
software (possibly commercial). But some files in the distribution
|
44
|
+
are not written by the author, so that they are not under this terms.
|
45
|
+
|
46
|
+
5. The scripts and library files supplied as input to or produced as
|
47
|
+
output from the software do not automatically fall under the
|
48
|
+
copyright of the software, but belong to whomever generated them,
|
49
|
+
and may be sold commercially, and may be aggregated with this
|
50
|
+
software.
|
51
|
+
|
52
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
53
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
54
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
55
|
+
PURPOSE.
|
data/README
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
= upr - Upload Progress for Rack
|
2
|
+
|
3
|
+
upr is Rack middleware that allows browser-side upload progress
|
4
|
+
monitoring. It is based on the "mongrel_upload_progress" module, but
|
5
|
+
allows any Moneta-backing store in additon to DRb. There is also a
|
6
|
+
packaged example for using an ActiveRecord model for Rails.
|
7
|
+
|
8
|
+
== Demo
|
9
|
+
|
10
|
+
You can see upr it in action at http://upr-demo.bogomips.org/
|
11
|
+
It will report the size and SHA1 of the file you've uploaded.
|
12
|
+
Much of the demo was stolen from mongrel_upload_progress.
|
13
|
+
|
14
|
+
== Web Server Compatibility
|
15
|
+
|
16
|
+
While upr is completely Rack::Lint-compatible, upr is only compatible
|
17
|
+
with Rack web servers that support a streaming "rack.input". Currently
|
18
|
+
this is limited to {Rainbows!}[http://rainbows.rubyforge.org/] with a
|
19
|
+
handful of concurrency models:
|
20
|
+
|
21
|
+
* ThreadSpawn
|
22
|
+
* ThreadPool
|
23
|
+
* Revactor*
|
24
|
+
|
25
|
+
For use with Revactor, the use of network-based Moneta stores or DRb is
|
26
|
+
only advised if those stores are using Revactor-aware sockets.
|
27
|
+
|
28
|
+
== JavaScript/HTML Compatibility
|
29
|
+
|
30
|
+
The current developer does not react well with GUIs. Thus all (R)HTML
|
31
|
+
and Prototype JavaScript code was stolen from mongrel_upload_progress.
|
32
|
+
|
33
|
+
Contributions to add compatibility for more modern things like JQuery
|
34
|
+
and HTML5 are very welcome.
|
35
|
+
|
36
|
+
|
37
|
+
== Backend Compatibility
|
38
|
+
|
39
|
+
We depend on {Moneta}[http://github.com/wycats/moneta], which allows the
|
40
|
+
use of a multitude of key-value stores. We also provide a
|
41
|
+
DRb+Moneta::Memory server to ease transitions from
|
42
|
+
mongrel_upload_progress.
|
43
|
+
|
44
|
+
Additionally, there is an example for using Rails ActiveRecord as a
|
45
|
+
backend storage mechanism. Cookie-based upload tracking may eventually
|
46
|
+
be used, too (contributions very welcome).
|
47
|
+
|
48
|
+
== Proxy Compatibility
|
49
|
+
|
50
|
+
No proxy is required when used with Rainbows!
|
51
|
+
|
52
|
+
The only incompatible HTTP proxy we know of is nginx. nginx will buffer
|
53
|
+
large requests to the filesystem before sending them to the backend.
|
54
|
+
nginx has its own 3rd-party module for
|
55
|
+
{upload progress}[http://wiki.nginx.org/NginxHttpUploadProgressModule]
|
56
|
+
and may be used instead of upr.
|
57
|
+
|
58
|
+
Most other HTTP-aware and all TCP-only proxies should be compatible.
|
59
|
+
Disabling Nagle's algorithm in both the Rack web server and proxy is
|
60
|
+
advised for lower latency, especially with stunnel.
|
61
|
+
|
62
|
+
== Unicorn Compatibility
|
63
|
+
|
64
|
+
While {Unicorn}[http://unicorn.bogomips.org/] provides the streaming
|
65
|
+
"rack.input" for Rainbows!, using Unicorn with upr is generally NOT
|
66
|
+
recommended. Unicorn only supports fast clients and progress reporting
|
67
|
+
is unnecessary unless clients are uploading files that are hundreds of
|
68
|
+
megabyte in size or larger.
|
69
|
+
|
70
|
+
== Getting Started
|
71
|
+
|
72
|
+
gem install upr
|
73
|
+
|
74
|
+
For Rails, look at the Rails application
|
75
|
+
{example}[http://git.bogomips.org/cgit/upr.git/tree/examples/rails_app-2.3.4]
|
76
|
+
and RDoc. More documentation is on the way.
|
77
|
+
|
78
|
+
== Disclaimer
|
79
|
+
|
80
|
+
There is NO WARRANTY whatsoever if anything goes wrong, but let us know
|
81
|
+
and we'll try our best to fix it.
|
82
|
+
|
83
|
+
== License
|
84
|
+
|
85
|
+
upr is copyright 2009 by all contributors (see logs in git). It is
|
86
|
+
based on mongrel_upload_progress and carries the same license (Ruby +
|
87
|
+
GPL2). See the included LICENSE file for details.
|
88
|
+
|
89
|
+
upr is 100% Free Software. We will never support the proliferation of
|
90
|
+
non-Free browsers or plugins.
|
91
|
+
|
92
|
+
== Contact
|
93
|
+
|
94
|
+
All feedback (bug reports, user/development dicussion, patches, pull
|
95
|
+
requests) go to the mailing list/newsgroup. Patches must be sent inline
|
96
|
+
(git format-patch -M + git send-email). No top posting.
|
97
|
+
|
98
|
+
To subscribe or post to the mailing list, just send an email to
|
99
|
+
upr@librelist.com and follow the instructions in the automated reply.
|
100
|
+
|
101
|
+
* email: mailto:upr@librelist.com
|
102
|
+
* git: git://git.bogomips.org/upr.git
|
103
|
+
* cgit: http://git.bogomips.org/cgit/upr.git
|
104
|
+
|
105
|
+
We will adhere to mostly the same conventions for patch submissions as
|
106
|
+
git itself. See the Documentation/SubmittingPatches document
|
107
|
+
distributed with git on on patch submission guidelines to follow. Just
|
108
|
+
don't email the git mailing list with upr patches :)
|
data/Rakefile
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
3
|
+
# most tasks are in the GNUmakefile which offers better parallelism
|
4
|
+
|
5
|
+
def tags
|
6
|
+
timefmt = '%Y-%m-%dT%H:%M:%SZ'
|
7
|
+
@tags ||= `git tag -l`.split(/\n/).map do |tag|
|
8
|
+
if %r{\Av[\d\.]+\z} =~ tag
|
9
|
+
header, subject, body = `git cat-file tag #{tag}`.split(/\n\n/, 3)
|
10
|
+
header = header.split(/\n/)
|
11
|
+
tagger = header.grep(/\Atagger /).first
|
12
|
+
body ||= "initial"
|
13
|
+
{
|
14
|
+
:time => Time.at(tagger.split(/ /)[-2].to_i).utc.strftime(timefmt),
|
15
|
+
:tagger_name => %r{^tagger ([^<]+)}.match(tagger)[1].strip,
|
16
|
+
:tagger_email => %r{<([^>]+)>}.match(tagger)[1].strip,
|
17
|
+
:id => `git rev-parse refs/tags/#{tag}`.chomp!,
|
18
|
+
:tag => tag,
|
19
|
+
:subject => subject,
|
20
|
+
:body => body,
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end.compact.sort { |a,b| b[:time] <=> a[:time] }
|
24
|
+
end
|
25
|
+
|
26
|
+
cgit_url = "http://git.bogomips.org/cgit/upr.git"
|
27
|
+
|
28
|
+
desc 'prints news as an Atom feed'
|
29
|
+
task :news_atom do
|
30
|
+
require 'nokogiri'
|
31
|
+
new_tags = tags[0,10]
|
32
|
+
puts(Nokogiri::XML::Builder.new do
|
33
|
+
feed :xmlns => "http://www.w3.org/2005/Atom" do
|
34
|
+
id! "http://upr.bogomips.org/NEWS.atom.xml"
|
35
|
+
title "upr news"
|
36
|
+
subtitle "Upload Progress for Rack"
|
37
|
+
link! :rel => 'alternate', :type => 'text/html',
|
38
|
+
:href => 'http://upr.bogomips.org/NEWS.html'
|
39
|
+
updated(new_tags.empty? ? "1970-01-01T00:00:00Z" : new_tags.first[:time])
|
40
|
+
new_tags.each do |tag|
|
41
|
+
entry do
|
42
|
+
title tag[:subject]
|
43
|
+
updated tag[:time]
|
44
|
+
published tag[:time]
|
45
|
+
author {
|
46
|
+
name tag[:tagger_name]
|
47
|
+
email tag[:tagger_email]
|
48
|
+
}
|
49
|
+
url = "#{cgit_url}/tag/?id=#{tag[:tag]}"
|
50
|
+
link! :rel => "alternate", :type => "text/html", :href =>url
|
51
|
+
id! url
|
52
|
+
content({:type => 'text'}, tag[:body])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end.to_xml)
|
57
|
+
end
|
58
|
+
|
59
|
+
desc 'prints RDoc-formatted news'
|
60
|
+
task :news_rdoc do
|
61
|
+
tags.each do |tag|
|
62
|
+
time = tag[:time].tr!('T', ' ').gsub!(/:\d\dZ/, ' UTC')
|
63
|
+
puts "=== #{tag[:tag].sub(/^v/, '')} / #{time}"
|
64
|
+
puts ""
|
65
|
+
|
66
|
+
body = tag[:body]
|
67
|
+
puts tag[:body].gsub(/^/sm, " ").gsub(/[ \t]+$/sm, "")
|
68
|
+
puts ""
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
desc "print release changelog for Rubyforge"
|
73
|
+
task :release_changes do
|
74
|
+
version = ENV['VERSION'] or abort "VERSION= needed"
|
75
|
+
version = "v#{version}"
|
76
|
+
vtags = tags.map { |tag| tag[:tag] =~ /\Av/ and tag[:tag] }.sort
|
77
|
+
prev = vtags[vtags.index(version) - 1]
|
78
|
+
if prev
|
79
|
+
system('git', 'diff', '--stat', prev, version) or abort $?
|
80
|
+
puts ""
|
81
|
+
system('git', 'log', "#{prev}..#{version}") or abort $?
|
82
|
+
else
|
83
|
+
system('git', 'log', version) or abort $?
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
desc "print release notes for Rubyforge"
|
88
|
+
task :release_notes do
|
89
|
+
require 'rubygems'
|
90
|
+
|
91
|
+
git_url = ENV['GIT_URL'] || 'git://git.bogomips.org/upr.git'
|
92
|
+
|
93
|
+
spec = Gem::Specification.load('upr.gemspec')
|
94
|
+
puts spec.description.strip
|
95
|
+
puts ""
|
96
|
+
puts "* #{spec.homepage}"
|
97
|
+
puts "* #{spec.email}"
|
98
|
+
puts "* #{git_url}"
|
99
|
+
|
100
|
+
_, _, body = `git cat-file tag v#{spec.version}`.split(/\n\n/, 3)
|
101
|
+
print "\nChanges:\n\n"
|
102
|
+
puts body
|
103
|
+
end
|
104
|
+
|
105
|
+
desc "read news article from STDIN and post to rubyforge"
|
106
|
+
task :publish_news do
|
107
|
+
require 'rubyforge'
|
108
|
+
IO.select([STDIN], nil, nil, 1) or abort "E: news must be read from stdin"
|
109
|
+
msg = STDIN.readlines
|
110
|
+
subject = msg.shift
|
111
|
+
blank = msg.shift
|
112
|
+
blank == "\n" or abort "no newline after subject!"
|
113
|
+
subject.strip!
|
114
|
+
body = msg.join("").strip!
|
115
|
+
|
116
|
+
rf = RubyForge.new.configure
|
117
|
+
rf.login
|
118
|
+
rf.post_news('rainbows', subject, body)
|
119
|
+
end
|
data/bin/upr-drb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
2
|
+
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
3
|
+
|
4
|
+
require(File.join(File.dirname(__FILE__), 'config', 'boot'))
|
5
|
+
|
6
|
+
require 'rake'
|
7
|
+
require 'rake/testtask'
|
8
|
+
require 'rake/rdoctask'
|
9
|
+
|
10
|
+
require 'tasks/rails'
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
|
3
|
+
class FilesController < ApplicationController
|
4
|
+
defined?($upr) or before_filter do
|
5
|
+
# grab the backend in case we forget to set it (or if we're using DRb)
|
6
|
+
defined?($upr) or ObjectSpace.each_object(Upr::InputWrapper) do |x|
|
7
|
+
$upr ||= x.backend
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def index
|
12
|
+
end
|
13
|
+
|
14
|
+
def status
|
15
|
+
tmp = $upr.read(params[:upload_id]).inspect
|
16
|
+
render :text => "#{Rack::Utils.escape_html(tmp)}\n"
|
17
|
+
end
|
18
|
+
|
19
|
+
def progress
|
20
|
+
render :update do |page|
|
21
|
+
status = $upr.read(params[:upload_id]) and
|
22
|
+
page.upload_progress.update(status.length, status.seen)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def upload
|
27
|
+
file = params[:data]
|
28
|
+
digest = Digest::SHA1.new
|
29
|
+
if buf = file.read(16384)
|
30
|
+
begin
|
31
|
+
digest.update(buf)
|
32
|
+
end while file.read(16384, buf)
|
33
|
+
end
|
34
|
+
size = file.stat.size
|
35
|
+
File.unlink(file.path)
|
36
|
+
render :text => "sha1: #{digest.hexdigest}<br />" \
|
37
|
+
"size: #{size}<br />" \
|
38
|
+
'<script type="text/javascript">' \
|
39
|
+
'window.parent.UploadProgress.finish();</script>'
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class UprStatus < ActiveRecord::Base
|
2
|
+
cattr_accessor :gc_cutoff
|
3
|
+
@@gc_cutoff = 10
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def read(upid)
|
7
|
+
if rv = find_by_upid(upid)
|
8
|
+
rv.time = Time.now.to_i
|
9
|
+
rv.save
|
10
|
+
rv
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def start(upid, length)
|
15
|
+
# this must be a find_or_create_by since some users have twitchy
|
16
|
+
# fingers and hit the upload button prematurely
|
17
|
+
find_or_create_by_upid(upid) do |x|
|
18
|
+
x.length = length
|
19
|
+
x.time = Time.now.to_i
|
20
|
+
x.seen = 0
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def incr(upid, nr)
|
25
|
+
update_all("seen = seen + #{nr.to_i}, time = #{Time.now.to_i}",
|
26
|
+
{ :upid => upid })
|
27
|
+
end
|
28
|
+
|
29
|
+
def gc
|
30
|
+
cutoff = Time.now.to_i - @@gc_cutoff
|
31
|
+
delete_all "time < #{cutoff}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
4
|
+
<title>upr test, http://upr.bogomips.org/</title>
|
5
|
+
<%= javascript_include_tag :all %>
|
6
|
+
<style type="text/css">
|
7
|
+
#progress-bar {
|
8
|
+
width:500px;
|
9
|
+
height:25px;
|
10
|
+
margin:15px;
|
11
|
+
border:solid 1px #000;
|
12
|
+
position:relative;
|
13
|
+
}
|
14
|
+
|
15
|
+
#progress-bar #status-bar {
|
16
|
+
display:block;
|
17
|
+
height:25px;
|
18
|
+
width:0;
|
19
|
+
background-color:#00f;
|
20
|
+
border-right:solid 1px #000;
|
21
|
+
position:absolute;
|
22
|
+
top:0; left:0;
|
23
|
+
}
|
24
|
+
|
25
|
+
#progress-bar #status-text {
|
26
|
+
display:block;
|
27
|
+
padding: 0 15px;
|
28
|
+
line-height:25px;
|
29
|
+
position:absolute;
|
30
|
+
top:0; left:0;
|
31
|
+
}
|
32
|
+
</style>
|
33
|
+
</head>
|
34
|
+
<body>
|
35
|
+
<%
|
36
|
+
upid = "#{Time.now.to_i}.#{rand}"
|
37
|
+
act = { :action => 'upload', :upload_id => upid }
|
38
|
+
opt = {
|
39
|
+
:multipart => true,
|
40
|
+
:target => 'upload',
|
41
|
+
:onsubmit => "UploadProgress.monitor('#{escape_javascript(upid)}')"
|
42
|
+
}
|
43
|
+
-%>
|
44
|
+
<p>
|
45
|
+
This is a demo of <a href="http://upr.bogomips.org/">upr</a> in action.
|
46
|
+
Much of this (including all JS) was stolen from the mongrel_upload_progress
|
47
|
+
examples.
|
48
|
+
</p>
|
49
|
+
<p><%= link_to upid, :action => 'status', :upload_id => upid %></p>
|
50
|
+
<% form_tag(act, opt) do %>
|
51
|
+
<div id="file-fields">
|
52
|
+
<p><%= file_field_tag :data %></p>
|
53
|
+
</div>
|
54
|
+
<p><%= link_to_function 'Add File Field', 'UploadProgress.FileField.add()' %>
|
55
|
+
</p>
|
56
|
+
<p><%= submit_tag :Upload %></p>
|
57
|
+
<% end %>
|
58
|
+
|
59
|
+
<div id="results"></div>
|
60
|
+
<div id="progress-bar"></div>
|
61
|
+
|
62
|
+
<p>The size and SHA1 of the file you uploaded should appear here</p>
|
63
|
+
<iframe id="upload" name="upload" src="about:blank"></iframe>
|
64
|
+
|
65
|
+
</body>
|
66
|
+
</html>
|