75 Commits
3.0 ... 4.3

Author SHA1 Message Date
Antoni Sawicki
4212678d81 readme updates 2019-07-16 23:47:37 -07:00
Antoni Sawicki
0d2ba9d4b2 added readme to ld 2019-07-16 23:44:27 -07:00
Antoni Sawicki
a63d4ef50d added historical versions to old directory 2019-07-16 23:39:40 -07:00
Antoni Sawicki
127114f753 added gitignore 2019-07-16 23:31:54 -07:00
Antoni Sawicki
b313e703fb fixes for ps build 2019-07-16 23:30:40 -07:00
Antoni Sawicki
ee26c40eb3 added simple ps1 build script 2019-07-16 23:20:52 -07:00
Antoni Sawicki
6c29008eb5 added cache control cruft 2019-07-16 22:47:23 -07:00
Antoni Sawicki
bb84d43d31 fixed handling interrupts and shutdown 2019-07-16 22:29:35 -07:00
Antoni Sawicki
7067d2cdf8 img update 2019-07-14 22:51:57 -07:00
Antoni Sawicki
640a405622 Merge branch 'master' of https://github.com/tenox7/wrp 2019-07-14 01:54:50 -07:00
Antoni Sawicki
22dd6aaab2 removed alt as it was being annoying in netscape 2019-07-14 01:54:41 -07:00
Antoni Sawicki
d102016ba9 reduced sleep to 2 sec 2019-07-14 01:53:55 -07:00
Antoni Sawicki
253fef2aad fixed logging for wrpreq from from 2019-07-14 01:53:33 -07:00
Antoni Sawicki
fdfad6bc69 Update README.md 2019-07-14 01:40:17 -07:00
Antoni Sawicki
1807790629 readme update 2019-07-13 01:31:24 -07:00
Antoni Sawicki
bd7d92393d readme update 2019-07-13 01:25:35 -07:00
Antoni Sawicki
e3b28e93c5 readme update 2019-07-13 01:23:34 -07:00
Antoni Sawicki
6784d47892 readme update 2019-07-13 00:47:19 -07:00
Antoni Sawicki
c96eb9ae35 readme update 2019-07-13 00:45:01 -07:00
Antoni Sawicki
cebebfa408 ver bump 2019-07-13 00:42:27 -07:00
Antoni Sawicki
9d7bb952c5 added buttons for backspace, enter and arrows 2019-07-13 00:40:56 -07:00
Antoni Sawicki
fd4b7a381e Update README.md 2019-07-12 14:28:26 -07:00
Antoni Sawicki
b894c3f809 readme update 2019-07-12 02:36:04 -07:00
Antoni Sawicki
733efaea56 ver bump 2019-07-12 02:25:17 -07:00
Antoni Sawicki
ad668d1bca readme update 2019-07-12 02:20:56 -07:00
Antoni Sawicki
4e28a50a8d readme update 2019-07-12 02:10:51 -07:00
Antoni Sawicki
2fab53d8a3 readme update 2019-07-12 02:04:35 -07:00
Antoni Sawicki
9557f172ed Merge branch 'clicky' 2019-07-12 01:54:59 -07:00
Antoni Sawicki
a3661003b0 add support for sending keys in to web forms 2019-07-12 01:51:23 -07:00
Antoni Sawicki
7baaa0bd6e more clicky stuff 2019-07-10 23:58:40 -07:00
Antoni Sawicki
e5e5e321e8 more cruft to make clicks work 2019-07-10 18:09:20 -07:00
Antoni Sawicki
650ac026c3 comments 2019-07-10 01:20:18 -07:00
Antoni Sawicki
579d67f7fb made clicky work somehow 2019-07-10 01:01:40 -07:00
0502f7a99d Fix typo in README. 2019-06-27 00:24:21 +01:00
Antoni Sawicki
eb1476e579 fix 2019-06-26 01:25:50 -07:00
Antoni Sawicki
f599a51c8d added more clicky cruft 2019-06-26 01:07:13 -07:00
Antoni Sawicki
dedf7479b8 clicky data encoding 2019-06-25 17:07:43 -07:00
Antoni Sawicki
877c42a388 Update README.md 2019-06-25 12:22:45 -07:00
Antoni Sawicki
1f5592cbde initial 2019-06-24 00:40:34 -07:00
Antoni Sawicki
404af50aa1 Update README.md 2019-06-20 10:20:41 -07:00
Antoni Sawicki
bb59229438 Update README.md 2019-06-18 14:49:32 -07:00
Antoni Sawicki
faa0818f18 Update README.md 2019-06-18 14:48:55 -07:00
Antoni Sawicki
26f999f262 Update README.md 2019-06-18 14:48:39 -07:00
Antoni Sawicki
4d02165619 Update README.md 2019-06-18 14:46:19 -07:00
Antoni Sawicki
1578b14fcd Update README.md 2019-06-18 14:45:50 -07:00
Antoni Sawicki
ceb6a67ff3 added shutdown handler 2019-06-17 23:53:22 -07:00
Antoni Sawicki
3ee146dee7 fix for #38, changed Content to Padding 2019-06-17 00:39:32 -07:00
Antoni Sawicki
1e58c94263 fix for #40 hang on page with no nodes, added timeout for cdp.Nodes 2019-06-17 00:38:52 -07:00
Antoni Sawicki
ce51eb6226 because lint said so 2019-06-17 00:21:32 -07:00
Antoni Sawicki
1b68593fd2 makefile fix 2019-06-17 00:19:01 -07:00
Antoni Sawicki
8f16abacde Update README.md 2019-06-11 02:56:00 -07:00
Antoni Sawicki
b1e0b417c3 Update README.md 2019-06-10 15:14:17 -07:00
Antoni Sawicki
fd6f8592ef Update README.md 2019-06-10 15:12:27 -07:00
Antoni Sawicki
210a12fe3d Update README.md 2019-06-10 15:11:40 -07:00
Antoni Sawicki
92f3cb7aee create new context on context cancelled 2019-06-08 17:32:35 -07:00
Antoni Sawicki
d9381ef71a print error message when failed to start server 2019-06-08 17:30:30 -07:00
Antoni Sawicki
64f86b4fd9 Update README.md 2019-06-07 01:30:32 -07:00
Antoni Sawicki
2d41aa1044 Update README.md 2019-06-06 16:46:40 -07:00
Antoni Sawicki
6e43026100 Update README.md 2019-06-06 16:33:39 -07:00
Antoni Sawicki
3285a60c69 Update README.md 2019-06-06 02:05:03 -07:00
Antoni Sawicki
c873e53df0 Update README.md 2019-06-06 01:35:23 -07:00
Antoni Sawicki
dc6c8eca52 Merge pull request #39 from djames1/master
Add support for PPC64 Little Endian, fix incorrect spelling in wrp.go and README.md
2019-06-06 01:21:23 -07:00
Antoni Sawicki
849239fc8e added real color quantizer, number of colors works now 2019-06-06 01:06:36 -07:00
Daniel James
d71a48b746 README.md: Fix typos 2019-06-06 04:05:29 -04:00
Daniel James
2671fc236c wrp.go: Fixed typos 2019-06-06 04:01:19 -04:00
Daniel James
15b227ccf1 Makefile: Add support for PPC64LE 2019-06-06 04:00:07 -04:00
Antoni Sawicki
89f5f556f9 assign to map before flush 2019-06-06 00:15:57 -07:00
Antoni Sawicki
af3aef5c39 switched to websafe color pallete, disabled number of colors input box for now 2019-06-06 00:15:36 -07:00
Antoni Sawicki
26ad732d99 Update README.md 2019-06-05 01:42:17 -07:00
Antoni Sawicki
033f2f3578 Update README.md 2019-06-05 01:35:42 -07:00
Antoni Sawicki
277d70f4c3 Add files via upload 2019-06-05 01:34:41 -07:00
Antoni Sawicki
ab4122a9ba Update README.md 2019-06-05 01:20:14 -07:00
Antoni Sawicki
9bd1359a4d Update README.md 2019-06-05 01:19:07 -07:00
Antoni Sawicki
adff09c6b9 Update README.md 2019-06-05 01:18:23 -07:00
Antoni Sawicki
93c84fdfca Update README.md 2019-06-05 01:17:25 -07:00
10 changed files with 1371 additions and 188 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
wrp-*

View File

@@ -3,6 +3,12 @@ all: linux freebsd openbsd macos windows rpi
linux:
GOOS=linux GOARCH=amd64 go build -a -o wrp-linux wrp.go
linux-ppc64le:
GOOS=linux GOARCH=ppc64le go build -a -o wrp-linux-ppc64le wrp.go
rpi:
GOOS=linux GOARCH=arm go build -a -o wrp-linux-rpi wrp.go
freebsd:
GOOS=freebsd GOARCH=amd64 go build -a -o wrp-freebsd wrp.go
@@ -15,8 +21,5 @@ macos:
windows:
GOOS=windows GOARCH=amd64 go build -a -o wrp-windows.exe wrp.go
rpi:
GOOS=linux GOARCH=arm go build -a -o wrp-linux-rpi wrp.go
clean:
rm -rf wrp-linux wrp-freebsd wrp-openbsd wrp-macos wrp-windows.exe wrp-linux-rpi

View File

@@ -1,28 +1,24 @@
# WRP - Web Rendering Proxy
A HTTP proxy server that allows to use historical and obsolete web browsers on the modern web. It works by rendering the web page in to a GIF image associated with clickable imagemap of original web links.
A HTTP proxy server that allows to use historical and obsolete web browsers on the modern web. It works by rendering the web page in to a GIF image. It sends mouse clicks via ISMAP and keystrokes from a text box form input.
## Current Status
* This is the new GoLang/ChdomeDP version.
* It's still lacking some features of the [older version](/old) (such as real http proxy mode and image manipulation) but it surpases it in terms of stability and usability.
* It's beta quality but I can fix/maintain the code unlike the older version.
* This is a new reimplementation in GoLang/[ChromeDP](https://github.com/chromedp/chromedp). Python/Webkit being now deprecated.
* Beta versiom, but fully supported an maintained.
* Works as browser-in-browser. A real http proxy mode is being investigated. Check [issue #35](https://github.com/tenox7/wrp/issues/35) for updates.
* As of 4.1 supports clicking on non-link elements (eg. cookie warnings, dropdown menus, etc.) and sending keystrokes. Yes, you can login and use Gmail or play web based games from any old browser.
## Todo
## Usage
* Configurable color palete and quantization.
* Real http proxy support via [goproxy](https://github.com/elazarl/goproxy) - if you really need a real proxy, for now use the [old/](/old) version.
* Padded box model coordinates.
* Input boxes support. However today you can cheat by using headed mode and input your data on the WRP server.
1. [Download a WRP binary](https://github.com/tenox7/wrp/releases/) and run it on a machine that will become your WRP server.
2. Point your legacy browser to `http://address:port` of WRP server. Do not set or use it as a "Proxy Server" (yet).
3. Type a search string or a http/https URL and click GO.
4. Adjust your screen width/height/scale/#colors to fit in your old browser.
5. Scroll web page by clicking on the in-image scroll bar.
6. Send keystrokes by filling in K input box and pressing Go. You also have buttons for backspace, enter and arrow keys.
## Usage
1. [Download a WRP binary](https://github.com/tenox7/wrp/releases) and run on a server/gateway.
2. Point your legacy browser to the IP address:port of WRP server.
3. Type a search string or a http/https URL and click Go.
4. Adjust your screen width/heigh/scale to fit in your old browser.
5. For very very very old browsers such as Mosaic 2.x and IBM WebExplorer 1.x check the I checkbox to enable ISMAP mode. However this normally should not be needed.
6. Scroll web page by clicking Up/Down. To go to top enter 0 and click Go.
![Internet Explorer 1.5 doing Gmail](wrp.png)
## Flags
```
@@ -31,6 +27,20 @@ A HTTP proxy server that allows to use historical and obsolete web browsers on t
-d chromedp debug logging
```
## More info and screenshots
* http://virtuallyfun.superglobalmegacorp.com/2014/03/11/web-rendering-proxy-update/
* http://virtuallyfun.superglobalmegacorp.com/2014/03/03/surfing-modern-web-with-ancient-browsers/
## History
* In 2014, version 1.0 started as a cgi-bin script, adaptation of `webkit2png.py` and `pcidade.py`, [blog post](https://virtuallyfun.com/2014/03/03/surfing-modern-web-with-ancient-browsers/).
* Later in 2014, version 2.0 became a stand alone http-proxy server, also support for both Linux/MacOS, [another post](https://virtuallyfun.com/wordpress/2014/03/11/web-rendering-proxy-update//).
* In 2016 the whole internet migrated to HTTPS/SSL/TLS and WRP largely stopped working. Python code became unmaintainable and mostly unportable (especially to Windows, even WSL).
* In 2019 WRP 3.0 has been rewritten in Golang/Chromedp as browser-in-browser instead of http proxy.
* Later in 2019, WRP 4.0 has been completely refactored to use mouse clicks instead using a href nodes. Also in 4.1 added sending keystrokes in to input boxes. You can now login to Gmail.
## Credits
* Uses [chromedp](https://github.com/chromedp), thanks to [mvdan](https://github.com/mvdan) for dealing with my issues
* Uses [go-quantize](https://github.com/ericpauley/go-quantize), thanks to [ericpauley](https://github.com/ericpauley) for developing the missing go quantizer
* Thanks to Jason Stevens of [Fun With Virtualization](https://virtuallyfun.com/) for graciously hosting my rumblings
* Thanks to [claunia](https://github.com/claunia/) for help with the Python/Webkit version in the past
## Legal Stuff
License: Apache 2.0
Copyright (c) 2013-2018 Antoni Sawicki
Copyright (c) 2019 Google LLC

20
make.ps1 Normal file
View File

@@ -0,0 +1,20 @@
param (
[switch]$clean = $false
)
$env:GOARCH="amd64"
foreach($os in ("linux", "freebsd", "openbsd", "darwin", "windows")) {
$env:GOOS=$os
$o="wrp-$(if ($os -eq "windows") {$os="windows.exe"})$os"
Remove-Item -ErrorAction Ignore $o
if (!$clean) {
Invoke-Expression "& go build -a -o $o wrp.go"
}
}
$env:GOARCH="arm"
$env:GOOS="linux"
$o="wrp-linux-rpi"
Remove-Item -ErrorAction Ignore $o
if (!$clean) {
Invoke-Expression "& go build -a -o $o wrp.go"
}

9
old/README Normal file
View File

@@ -0,0 +1,9 @@
Historical versions of WRP and prior art
License: GNU
Copyright (c) 2013-2018 Antoni Sawicki
Copyright (c) 2012-2013 picidae.net
Copyright (c) 2004-2013 Paul Hammond
Copyright (c) 2017-2018 Natalia Portillo
Copyright (c) 2018 //gir.st/

392
old/picidae.py Normal file
View File

@@ -0,0 +1,392 @@
#!/usr/bin/env python
# picidae.py - makes screenshots of webpages
# and analyzes the webpage structure and writes image-maps of the links
# as well as forms that are placed on the exact position of the old form.
# It is a part of the art project www.picidae.net
# http://www.picidae.net
#
# This script is based on webkit2png from Paul Hammond.
# It was extended by picidae.net
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
__version__ = "1.0"
import sys
#print "hello ... "
try:
import Foundation
import WebKit
import AppKit
import objc
import urllib
except ImportError:
print "Cannot find pyobjc library files. Are you sure it is installed?"
sys.exit()
#try:
# from optparse import OptionParser
#except ImportError:
# print "OptionParser not imported"
# sys.exit()
from optparse import OptionParser
class AppDelegate (Foundation.NSObject):
# what happens when the app starts up
def applicationDidFinishLaunching_(self, aNotification):
webview = aNotification.object().windows()[0].contentView()
webview.frameLoadDelegate().getURL(webview)
class WebkitLoad (Foundation.NSObject, WebKit.protocols.WebFrameLoadDelegate):
# what happens if something goes wrong while loading
def webView_didFailLoadWithError_forFrame_(self,webview,error,frame):
print " ... something went wrong 1"
self.getURL(webview)
def webView_didFailProvisionalLoadWithError_forFrame_(self,webview,error,frame):
print " ... something went wrong 2"
self.getURL(webview)
def makeFilename(self,URL,options):
# make the filename
if options.filename:
filename = options.filename
elif options.md5:
try:
import md5
except ImportError:
print "--md5 requires python md5 library"
AppKit.NSApplication.sharedApplication().terminate_(None)
filename = md5.new(URL).hexdigest()
else:
import re
filename = re.sub('\W','',URL);
filename = re.sub('^http','',filename);
if options.datestamp:
import time
now = time.strftime("%Y%m%d")
filename = now + "-" + filename
import os
dir = os.path.abspath(os.path.expanduser(options.dir))
return os.path.join(dir,filename)
def saveImages(self,bitmapdata,filename,options):
# save the fullsize png
if options.fullsize:
bitmapdata.representationUsingType_properties_(AppKit.NSPNGFileType,None).writeToFile_atomically_(filename + ".png",objc.YES)
if options.thumb or options.clipped:
# work out how big the thumbnail is
width = bitmapdata.pixelsWide()
height = bitmapdata.pixelsHigh()
thumbWidth = (width * options.scale)
thumbHeight = (height * options.scale)
# make the thumbnails in a scratch image
scratch = AppKit.NSImage.alloc().initWithSize_(
Foundation.NSMakeSize(thumbWidth,thumbHeight))
scratch.lockFocus()
AppKit.NSGraphicsContext.currentContext().setImageInterpolation_(
AppKit.NSImageInterpolationHigh)
thumbRect = Foundation.NSMakeRect(0.0, 0.0, thumbWidth, thumbHeight)
clipRect = Foundation.NSMakeRect(0.0,
thumbHeight-options.clipheight,
options.clipwidth, options.clipheight)
bitmapdata.drawInRect_(thumbRect)
thumbOutput = AppKit.NSBitmapImageRep.alloc().initWithFocusedViewRect_(thumbRect)
clipOutput = AppKit.NSBitmapImageRep.alloc().initWithFocusedViewRect_(clipRect)
scratch.unlockFocus()
# save the thumbnails as pngs
if options.thumb:
thumbOutput.representationUsingType_properties_(
AppKit.NSPNGFileType,None
).writeToFile_atomically_(filename + "-thumb.png",objc.YES)
if options.clipped:
clipOutput.representationUsingType_properties_(
AppKit.NSPNGFileType,None
).writeToFile_atomically_(filename + "-clipped.png",objc.YES)
def getURL(self,webview):
if self.urls:
if self.urls[0] == '-':
url = sys.stdin.readline().rstrip()
if not url: AppKit.NSApplication.sharedApplication().terminate_(None)
else:
url = self.urls.pop(0)
else:
AppKit.NSApplication.sharedApplication().terminate_(None)
#print "<urlcall href=\"\" />", url, "..."
#print "<urlcall href=\"%s\" />" % (url)
self.resetWebview(webview)
webview.mainFrame().loadRequest_(Foundation.NSURLRequest.requestWithURL_(Foundation.NSURL.URLWithString_(url)))
if not webview.mainFrame().provisionalDataSource():
print "<nosuccess />"
self.getURL(webview)
def resetWebview(self,webview):
rect = Foundation.NSMakeRect(0,0,self.options.initWidth,self.options.initHeight)
webview.window().setContentSize_((self.options.initWidth,self.options.initHeight))
webview.setFrame_(rect)
def resizeWebview(self,view):
view.window().display()
view.window().setContentSize_(view.bounds().size)
view.setFrame_(view.bounds())
def captureView(self,view):
view.lockFocus()
bitmapdata = AppKit.NSBitmapImageRep.alloc()
bitmapdata.initWithFocusedViewRect_(view.bounds())
view.unlockFocus()
return bitmapdata
# what happens when the page has finished loading
def webView_didFinishLoadForFrame_(self,webview,frame):
# don't care about subframes
if (frame == webview.mainFrame()):
view = frame.frameView().documentView()
self.resizeWebview(view)
URL = frame.dataSource().initialRequest().URL().absoluteString()
filename = self.makeFilename(URL, self.options)
bitmapdata = self.captureView(view)
self.saveImages(bitmapdata,filename,self.options)
# ----------------------------------
# picidae my stuff
#print "url"
print "<page>"
print frame.dataSource().request().URL().absoluteString()
print "</page>"
# Analyse HTML and get links
xmloutput = "<map name=\"map\">\r";
domdocument = frame.DOMDocument()
domnodelist = domdocument.getElementsByTagName_('A')
i = 0
while i < domnodelist.length():
# linkvalue
value = domnodelist.item_(i).valueForKey_('href')
# position-rect
myrect = domnodelist.item_(i).boundingBox()
xmin = Foundation.NSMinX(myrect)
ymin = Foundation.NSMinY(myrect)
xmax = Foundation.NSMaxX(myrect)
ymax = Foundation.NSMaxY(myrect)
# print Link
prefix = ""
xmloutput += "<area shape=\"rect\" coords=\"%i,%i,%i,%i\" alt=\"\"><![CDATA[%s%s]]></area>\r" % (xmin, ymin, xmax, ymax, prefix, value)
i += 1
#print "</map>"
xmloutput += "</map>"
f = open(filename +'.xml', 'w+')
f.write(xmloutput)
f.close()
# ----------------------------------
# get forms
xmloutput = "<forms>\r";
xmloutput += "<page><![CDATA["
xmloutput += frame.dataSource().request().URL().absoluteString()
xmloutput += "]]></page>\r"
domdocument = frame.DOMDocument()
domnodelist = domdocument.getElementsByTagName_('form')
i = 0
while i < domnodelist.length():
# form
action = domnodelist.item_(i).valueForKey_('action')
method = domnodelist.item_(i).valueForKey_('method')
xmloutput += "<form method=\"%s\" ><action><![CDATA[%s]]></action>\r" % (method, action)
# form fields
fieldlist = domnodelist.item_(i).getElementsByTagName_('input')
j=0
while j < fieldlist.length():
# values
type = fieldlist.item_(j).valueForKey_('type')
name = fieldlist.item_(j).valueForKey_('name')
formvalue = fieldlist.item_(j).valueForKey_('value')
size = fieldlist.item_(j).valueForKey_('size')
checked = fieldlist.item_(j).valueForKey_('checked')
# write output
xmloutput += "\t<input "
if (type):
xmloutput += "type=\"%s\" " % (type)
if (name):
xmloutput += "name=\"%s\" " % (name)
if (size):
xmloutput += "size=\"%s\" " % (size)
if (type and type != "hidden"):
myrect = fieldlist.item_(j).boundingBox()
xmin = Foundation.NSMinX(myrect)
ymin = Foundation.NSMinY(myrect)
xmax = Foundation.NSMaxX(myrect)
ymax = Foundation.NSMaxY(myrect)
height = ymax - ymin
width = xmax - xmin
if (type == "radio" or type == "checkbox"):
xmin -= 3
ymin -= 3
xmloutput += "style=\"position:absolute;top:%i;left:%i;width:%i;height:%i;\" " % (ymin, xmin, width, height)
if (checked):
xmloutput += "checked=\"%s\" " % (checked)
xmloutput += "><![CDATA["
if (formvalue and type!="text" and type!="password"):
#xmloutput += urllib.quote(formvalue)
dummy=10
xmloutput += "]]></input>\r"
j += 1
xmloutput += "</form>\r"
i += 1
xmloutput += "</forms>"
f = open(filename +'.form.xml', 'w+')
f.write(xmloutput)
f.close()
# End picidae
# ----------------------------------
#print " ... done"
self.getURL(webview)
#trying to give back the real url
def main():
# parse the command line
usage = """%prog [options] [http://example.net/ ...]
examples:
%prog http://google.com/ # screengrab google
%prog -W 1000 -H 1000 http://google.com/ # bigger screengrab of google
%prog -T http://google.com/ # just the thumbnail screengrab
%prog -TF http://google.com/ # just thumbnail and fullsize grab
%prog -o foo http://google.com/ # save images as "foo-thumb.png" etc
%prog - # screengrab urls from stdin"""
cmdparser = OptionParser(usage, version=("webkit2png "+__version__))
# TODO: add quiet/verbose options
cmdparser.add_option("-W", "--width",type="float",default=800.0,
help="initial (and minimum) width of browser (default: 800)")
cmdparser.add_option("-H", "--height",type="float",default=600.0,
help="initial (and minimum) height of browser (default: 600)")
cmdparser.add_option("--clipwidth",type="float",default=200.0,
help="width of clipped thumbnail (default: 200)",
metavar="WIDTH")
cmdparser.add_option("--clipheight",type="float",default=150.0,
help="height of clipped thumbnail (default: 150)",
metavar="HEIGHT")
cmdparser.add_option("-s", "--scale",type="float",default=0.25,
help="scale factor for thumbnails (default: 0.25)")
cmdparser.add_option("-m", "--md5", action="store_true",
help="use md5 hash for filename (like del.icio.us)")
cmdparser.add_option("-o", "--filename", type="string",default="",
metavar="NAME", help="save images as NAME.png,NAME-thumb.png etc")
cmdparser.add_option("-F", "--fullsize", action="store_true",
help="only create fullsize screenshot")
cmdparser.add_option("-T", "--thumb", action="store_true",
help="only create thumbnail sreenshot")
cmdparser.add_option("-C", "--clipped", action="store_true",
help="only create clipped thumbnail screenshot")
cmdparser.add_option("-d", "--datestamp", action="store_true",
help="include date in filename")
cmdparser.add_option("-D", "--dir",type="string",default="./",
help="directory to place images into")
(options, args) = cmdparser.parse_args()
if len(args) == 0:
cmdparser.print_help()
return
if options.filename:
if len(args) != 1 or args[0] == "-":
print "--filename option requires exactly one url"
return
if options.scale == 0:
cmdparser.error("scale cannot be zero")
# make sure we're outputing something
if not (options.fullsize or options.thumb or options.clipped):
options.fullsize = True
options.thumb = True
options.clipped = True
# work out the initial size of the browser window
# (this might need to be larger so clipped image is right size)
options.initWidth = (options.clipwidth / options.scale)
options.initHeight = (options.clipheight / options.scale)
if options.width>options.initWidth:
options.initWidth = options.width
if options.height>options.initHeight:
options.initHeight = options.height
app = AppKit.NSApplication.sharedApplication()
# create an app delegate
delegate = AppDelegate.alloc().init()
AppKit.NSApp().setDelegate_(delegate)
# create a window
rect = Foundation.NSMakeRect(-16000,-16000,100,100)
win = AppKit.NSWindow.alloc()
win.initWithContentRect_styleMask_backing_defer_ (rect,
AppKit.NSBorderlessWindowMask, 2, 0)
# create a webview object
webview = WebKit.WebView.alloc()
webview.initWithFrame_(rect)
# turn off scrolling so the content is actually x wide and not x-15
webview.mainFrame().frameView().setAllowsScrolling_(objc.NO)
# add the webview to the window
win.setContentView_(webview)
# create a LoadDelegate
loaddelegate = WebkitLoad.alloc().init()
loaddelegate.options = options
loaddelegate.urls = args
webview.setFrameLoadDelegate_(loaddelegate)
app.run()
if __name__ == '__main__' : main()

506
old/webkit2png.py Normal file
View File

@@ -0,0 +1,506 @@
#!/usr/bin/python
# webkit2png - makes screenshots of web pages
# http://www.paulhammond.org/webkit2png
__version__ = "dev"
# Copyright (c) 2004-2013 Paul Hammond
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
import sys
import optparse
import re
import os
try:
import Foundation
import WebKit
import AppKit
import Quartz
import objc
except ImportError:
print "Cannot find pyobjc library files. Are you sure it is installed?"
sys.exit()
class AppDelegate(Foundation.NSObject):
# what happens when the app starts up
def applicationDidFinishLaunching_(self, aNotification):
webview = aNotification.object().windows()[0].contentView()
webview.frameLoadDelegate().getURL(webview)
self.performSelector_withObject_afterDelay_("timeout:", None,
self.timeout)
def timeout_(self, obj):
Foundation.NSLog("timed out!")
AppKit.NSApplication.sharedApplication().terminate_(None)
class Webkit2PngScriptBridge(Foundation.NSObject):
def init(self):
self = super(Webkit2PngScriptBridge, self).init()
self.is_stopped = False
self.start_callback = False
return self
def stop(self):
self.is_stopped = True
def start(self):
self.is_stopped = False
self.start_callback()
def isSelectorExcludedFromWebScript_(self, sel):
if sel in ['stop', 'start']:
return False
else:
return True
class WebkitLoad (Foundation.NSObject, WebKit.protocols.WebFrameLoadDelegate):
# what happens if something goes wrong while loading
def webView_didFailLoadWithError_forFrame_(self, webview, error, frame):
if error.code() == Foundation.NSURLErrorCancelled:
return
print " ... something went wrong: "+error.localizedDescription()
self.getURL(webview)
def webView_didFailProvisionalLoadWithError_forFrame_(self, webview, error,
frame):
if error.code() == Foundation.NSURLErrorCancelled:
return
print " ... something went wrong: "+error.localizedDescription()
self.getURL(webview)
def makeFilename(self, URL, options):
# make the filename
if options.filename:
filename = options.filename
elif options.md5:
try:
import md5
except ImportError:
print "--md5 requires python md5 library"
AppKit.NSApplication.sharedApplication().terminate_(None)
filename = md5.new(URL).hexdigest()
else:
filename = re.sub('^https?', '', URL)
filename = re.sub('\W', '', filename)
if options.datestamp:
import time
now = time.strftime("%Y%m%d")
filename = now + "-" + filename
dir = os.path.abspath(os.path.expanduser(options.dir))
if not os.path.exists(options.dir):
os.makedirs(dir)
return os.path.join(dir, filename)
def saveImages(self, bitmapdata, filename, options):
# save the fullsize png
if options.fullsize:
bitmapdata.representationUsingType_properties_(
AppKit.NSPNGFileType,
None
).writeToFile_atomically_(filename + "-full.png", objc.YES)
if options.thumb or options.clipped:
# work out how big the thumbnail is
width = bitmapdata.pixelsWide()
height = bitmapdata.pixelsHigh()
thumbWidth = (width * options.scale)
thumbHeight = (height * options.scale)
# make the thumbnails in a scratch image
scratch = AppKit.NSImage.alloc().initWithSize_(
Foundation.NSMakeSize(thumbWidth, thumbHeight))
scratch.lockFocus()
AppKit.NSGraphicsContext.currentContext().setImageInterpolation_(
AppKit.NSImageInterpolationHigh)
thumbRect = Foundation.NSMakeRect(0.0, 0.0, thumbWidth,
thumbHeight)
clipRect = Foundation.NSMakeRect(
0.0,
thumbHeight-options.clipheight,
options.clipwidth,
options.clipheight)
bitmapdata.drawInRect_(thumbRect)
thumbOutput = AppKit.NSBitmapImageRep.alloc()\
.initWithFocusedViewRect_(thumbRect)
clipOutput = AppKit.NSBitmapImageRep.alloc()\
.initWithFocusedViewRect_(clipRect)
scratch.unlockFocus()
# save the thumbnails as pngs
if options.thumb:
thumbOutput.representationUsingType_properties_(
AppKit.NSPNGFileType, None).writeToFile_atomically_(
filename + "-thumb.png", objc.YES)
if options.clipped:
clipOutput.representationUsingType_properties_(
AppKit.NSPNGFileType, None).writeToFile_atomically_(
filename + "-clipped.png", objc.YES)
def getURL(self, webview):
if self.urls:
if self.urls[0] == '-':
url = sys.stdin.readline().rstrip()
if not url:
AppKit.NSApplication.sharedApplication().terminate_(None)
else:
url = self.urls.pop(0)
else:
AppKit.NSApplication.sharedApplication().terminate_(None)
nsurl = Foundation.NSURL.URLWithString_(url)
if not (nsurl and nsurl.scheme()):
nsurl = Foundation.NSURL.alloc().initFileURLWithPath_(url)
nsurl = nsurl.absoluteURL()
if self.options.ignore_ssl_check:
Foundation.NSURLRequest.setAllowsAnyHTTPSCertificate_forHost_(objc.YES, nsurl.host())
print "Fetching", nsurl, "..."
self.resetWebview(webview)
scriptobject = webview.windowScriptObject()
scriptobject.setValue_forKey_(Webkit2PngScriptBridge.alloc().init(),
'webkit2png')
webview.mainFrame().loadRequest_(Foundation.NSURLRequest.requestWithURL_(nsurl))
if not webview.mainFrame().provisionalDataSource():
print " ... not a proper url?"
self.getURL(webview)
def resetWebview(self, webview):
rect = Foundation.NSMakeRect(0, 0, self.options.initWidth,
self.options.initHeight)
window = webview.window()
window.setContentSize_((self.options.initWidth,
self.options.initHeight))
if self.options.transparent:
window.setOpaque_(objc.NO)
window.setBackgroundColor_(AppKit.NSColor.clearColor())
webview.setDrawsBackground_(objc.NO)
webview.setFrame_(rect)
def captureView(self, view):
bounds = view.bounds()
if bounds.size.height > self.options.UNSAFE_max_height:
print >> sys.stderr, "Error: page height greater than %s, " \
"clipping to avoid crashing windowserver." % \
self.options.UNSAFE_max_height
bounds.size.height = self.options.UNSAFE_max_height
if bounds.size.width > self.options.UNSAFE_max_width:
print >> sys.stderr, "Error: page width greater than %s, " \
"clipping to avoid crashing windowserver." % \
self.options.UNSAFE_max_width
bounds.size.width = self.options.UNSAFE_max_width
view.window().display()
view.window().setContentSize_(
Foundation.NSSize(self.options.initWidth, self.options.initHeight))
view.setFrame_(bounds)
if hasattr(view, "bitmapImageRepForCachingDisplayInRect_"):
bitmapdata = view.bitmapImageRepForCachingDisplayInRect_(bounds)
view.cacheDisplayInRect_toBitmapImageRep_(bounds, bitmapdata)
else:
view.lockFocus()
bitmapdata = AppKit.NSBitmapImageRep.alloc()
bitmapdata.initWithFocusedViewRect_(bounds)
view.unlockFocus()
return bitmapdata
# what happens when the page has finished loading
def webView_didFinishLoadForFrame_(self, webview, frame):
# don't care about subframes
if (frame == webview.mainFrame()):
scriptobject = webview.windowScriptObject()
if self.options.js:
scriptobject.evaluateWebScript_(self.options.js)
bridge = scriptobject.valueForKey_('webkit2png')
def doGrab():
Foundation.NSTimer.\
scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
self.options.delay, self, self.doGrab, webview, False)
if bridge.is_stopped:
bridge.start_callback = doGrab
else:
doGrab()
def doGrab(self, timer):
webview = timer.userInfo()
frame = webview.mainFrame()
view = frame.frameView().documentView()
URL = webview.mainFrame().dataSource().initialRequest().URL()\
.absoluteString()
filename = self.makeFilename(URL, self.options)
bitmapdata = self.captureView(view)
if self.options.selector:
doc = frame.DOMDocument()
el = doc.querySelector_(self.options.selector)
if not el:
print " ... no element matching %s found?" % \
self.options.selector
self.getURL(webview)
return
left, top = 0, 0
parent = el
while parent:
left += parent.offsetLeft()
top += parent.offsetTop()
parent = parent.offsetParent()
zoom = self.options.zoom
cropRect = view.window().convertRectToBacking_(Foundation.NSMakeRect(
zoom * left, zoom * top,
zoom * el.offsetWidth(), zoom * el.offsetHeight()))
cropped = Quartz.CGImageCreateWithImageInRect(
bitmapdata.CGImage(), cropRect)
bitmapdata = AppKit.NSBitmapImageRep.alloc().initWithCGImage_(
cropped)
Quartz.CGImageRelease(cropped)
self.saveImages(bitmapdata, filename, self.options)
print " ... done"
self.getURL(webview)
def main():
# parse the command line
usage = """%prog [options] [http://example.net/ ...]
Examples:
%prog http://google.com/ # screengrab google
%prog -W 1000 -H 1000 http://google.com/ # bigger screengrab of google
%prog -T http://google.com/ # just the thumbnail screengrab
%prog -TF http://google.com/ # just thumbnail and fullsize grab
%prog -o foo http://google.com/ # save images as "foo-thumb.png" etc
%prog - # screengrab urls from stdin
%prog /path/to/file.html # screengrab local html file
%prog -h | less # full documentation"""
cmdparser = optparse.OptionParser(usage,
version=("webkit2png " + __version__))
# TODO: add quiet/verbose options
cmdparser.add_option("--debug", action="store_true",
help=optparse.SUPPRESS_HELP)
# warning: setting these too high can crash your window server
cmdparser.add_option("--UNSAFE-max-height", type="int", default=30000,
help=optparse.SUPPRESS_HELP)
cmdparser.add_option("--UNSAFE-max-width", type="int", default=30000,
help=optparse.SUPPRESS_HELP)
group = optparse.OptionGroup(cmdparser, "Network Options")
group.add_option("--timeout", type="float", default=60.0,
help="page load timeout (default: 60)")
group.add_option("--user-agent", type="string", default=False,
help="set user agent header")
group.add_option("--ignore-ssl-check", action="store_true", default=False,
help="ignore SSL Certificate name mismatches")
cmdparser.add_option_group(group)
group = optparse.OptionGroup(cmdparser, "Browser Window Options")
group.add_option(
"-W", "--width", type="float", default=800.0,
help="initial (and minimum) width of browser (default: 800)")
group.add_option(
"-H", "--height", type="float", default=600.0,
help="initial (and minimum) height of browser (default: 600)")
group.add_option(
"-z", "--zoom", type="float", default=1.0,
help='zoom level of browser, equivalent to "Zoom In" and "Zoom Out" '
'in "View" menu (default: 1.0)')
group.add_option(
"--selector", type="string",
help="CSS selector for a single element to capture (first matching "
"element will be used)")
cmdparser.add_option_group(group)
group = optparse.OptionGroup(cmdparser, "Output size options")
group.add_option(
"-F", "--fullsize", action="store_true",
help="only create fullsize screenshot")
group.add_option(
"-T", "--thumb", action="store_true",
help="only create thumbnail sreenshot")
group.add_option(
"-C", "--clipped", action="store_true",
help="only create clipped thumbnail screenshot")
group.add_option(
"--clipwidth", type="float", default=200.0,
help="width of clipped thumbnail (default: 200)",
metavar="WIDTH")
group.add_option(
"--clipheight", type="float", default=150.0,
help="height of clipped thumbnail (default: 150)",
metavar="HEIGHT")
group.add_option(
"-s", "--scale", type="float", default=0.25,
help="scale factor for thumbnails (default: 0.25)")
cmdparser.add_option_group(group)
group = optparse.OptionGroup(cmdparser, "Output filename options")
group.add_option(
"-D", "--dir", type="string", default="./",
help="directory to place images into")
group.add_option(
"-o", "--filename", type="string", default="",
metavar="NAME", help="save images as NAME-full.png,NAME-thumb.png etc")
group.add_option(
"-m", "--md5", action="store_true",
help="use md5 hash for filename (like del.icio.us)")
group.add_option(
"-d", "--datestamp", action="store_true",
help="include date in filename")
cmdparser.add_option_group(group)
group = optparse.OptionGroup(cmdparser, "Web page functionality")
group.add_option(
"--delay", type="float", default=0,
help="delay between page load finishing and screenshot")
group.add_option(
"--js", type="string", default=None,
help="JavaScript to execute when the window finishes loading"
"(example: --js='document.bgColor=\"red\";'). "
"If you need to wait for asynchronous code to finish before "
"capturing the screenshot, call webkit2png.stop() before the "
"async code runs, then webkit2png.start() to capture the image.")
group.add_option(
"--noimages", action="store_true",
help=optparse.SUPPRESS_HELP)
group.add_option(
"--no-images", action="store_true",
help="don't load images")
group.add_option(
"--nojs", action="store_true",
help=optparse.SUPPRESS_HELP)
group.add_option(
"--no-js", action="store_true",
help="disable JavaScript support")
group.add_option(
"--transparent", action="store_true",
help="render output on a transparent background (requires a web "
"page with a transparent background)", default=False)
cmdparser.add_option_group(group)
(options, args) = cmdparser.parse_args()
if len(args) == 0:
cmdparser.print_usage()
return
if options.filename:
if len(args) != 1 or args[0] == "-":
print "--filename option requires exactly one url"
return
# deprecated options
if options.nojs:
print >> sys.stderr, 'Warning: --nojs will be removed in ' \
'webkit2png 1.0. Please use --no-js.'
options.no_js = True
if options.noimages:
print >> sys.stderr, 'Warning: --noimages will be removed in ' \
'webkit2png 1.0. Please use --no-images.'
options.no_images = True
if options.scale == 0:
cmdparser.error("scale cannot be zero")
# make sure we're outputing something
if not (options.fullsize or options.thumb or options.clipped):
options.fullsize = True
options.thumb = True
options.clipped = True
# work out the initial size of the browser window
# (this might need to be larger so clipped image is right size)
options.initWidth = (options.clipwidth / options.scale)
options.initHeight = (options.clipheight / options.scale)
options.width *= options.zoom
if options.width > options.initWidth:
options.initWidth = options.width
if options.height > options.initHeight:
options.initHeight = options.height
# Hide the dock icon (needs to run before NSApplication.sharedApplication)
AppKit.NSBundle.mainBundle().infoDictionary()['LSBackgroundOnly'] = '1'
app = AppKit.NSApplication.sharedApplication()
# create an app delegate
delegate = AppDelegate.alloc().init()
delegate.timeout = options.timeout
AppKit.NSApp().setDelegate_(delegate)
# create a window
rect = Foundation.NSMakeRect(0, 0, 100, 100)
win = AppKit.NSWindow.alloc()
win.initWithContentRect_styleMask_backing_defer_(
rect, AppKit.NSBorderlessWindowMask, 2, 0)
if options.debug:
win.orderFrontRegardless()
# create a webview object
webview = WebKit.WebView.alloc()
webview.initWithFrame_(rect)
# turn off scrolling so the content is actually x wide and not x-15
webview.mainFrame().frameView().setAllowsScrolling_(objc.NO)
if options.user_agent:
webview.setCustomUserAgent_(options.user_agent)
else:
webkit_version = Foundation.NSBundle.bundleForClass_(WebKit.WebView)\
.objectForInfoDictionaryKey_(WebKit.kCFBundleVersionKey)[1:]
webview.setApplicationNameForUserAgent_(
"Like-Version/6.0 Safari/%s webkit2png/%s" % (webkit_version, __version__))
webview.setPreferencesIdentifier_('webkit2png')
webview.preferences().setLoadsImagesAutomatically_(not options.no_images)
webview.preferences().setJavaScriptEnabled_(not options.no_js)
if options.zoom != 1.0:
webview._setZoomMultiplier_isTextOnly_(options.zoom, False)
# add the webview to the window
win.setContentView_(webview)
# create a LoadDelegate
loaddelegate = WebkitLoad.alloc().init()
loaddelegate.options = options
loaddelegate.urls = args
webview.setFrameLoadDelegate_(loaddelegate)
app.run()
if __name__ == '__main__':
main()

212
old/webrender.py Normal file
View File

@@ -0,0 +1,212 @@
#!/usr/bin/env python
# webrender.py - recursively render web pages to a gif+imagemap of clickable links
# caveat: this script requires to be run as a regular user and cannot run as a daemon
# from apache cgi-bin, you can use python built in http server instead
# usage:
# create cgi-bin directory, copy webrender.py to cgi-bin and chmod 755
# python -m CGIHTTPServer 8000
# navigate web browser to http://x.x.x.x:8000/cgi-bin/webrender.py
# the webrender-xxx.gif images are created in the CWD of the http server
__version__ = "1.0"
#
# This program is based on the software picidae.py 1.0 from http://www.picidae.net
# It was modified by Antoni Sawicki
#
# This program is based on the software webkit2png 0.4 from Paul Hammond.
# It was extended by picidae.net
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
try:
import sys
import os
import glob
import random
import Foundation
import WebKit
import AppKit
import objc
import string
import urllib
import socket
import cgi
import cgitb; cgitb.enable() # for trubleshooting
except ImportError:
print "Cannot find pyobjc library files. Are you sure it is installed?"
sys.exit()
from optparse import OptionParser
class AppDelegate (Foundation.NSObject):
# what happens when the app starts up
def applicationDidFinishLaunching_(self, aNotification):
webview = aNotification.object().windows()[0].contentView()
webview.frameLoadDelegate().getURL(webview)
class WebkitLoad (Foundation.NSObject, WebKit.protocols.WebFrameLoadDelegate):
# what happens if something goes wrong while loading
def webView_didFailLoadWithError_forFrame_(self,webview,error,frame):
print " ... something went wrong 1: " + error.localizedDescription()
self.getURL(webview)
def webView_didFailProvisionalLoadWithError_forFrame_(self,webview,error,frame):
print " ... something went wrong 2: " + error.localizedDescription()
self.getURL(webview)
def getURL(self,webview):
if self.urls:
if self.urls[0] == '-':
url = sys.stdin.readline().rstrip()
if not url: AppKit.NSApplication.sharedApplication().terminate_(None)
else:
url = self.urls.pop(0)
else:
AppKit.NSApplication.sharedApplication().terminate_(None)
self.resetWebview(webview)
webview.mainFrame().loadRequest_(Foundation.NSURLRequest.requestWithURL_(Foundation.NSURL.URLWithString_(url)))
if not webview.mainFrame().provisionalDataSource():
print "<nosuccess />"
self.getURL(webview)
def resetWebview(self,webview):
rect = Foundation.NSMakeRect(0,0,1024,768)
webview.window().setContentSize_((1024,768))
webview.setFrame_(rect)
def resizeWebview(self,view):
view.window().display()
view.window().setContentSize_(view.bounds().size)
view.setFrame_(view.bounds())
def captureView(self,view):
view.lockFocus()
bitmapdata = AppKit.NSBitmapImageRep.alloc()
bitmapdata.initWithFocusedViewRect_(view.bounds())
view.unlockFocus()
return bitmapdata
# what happens when the page has finished loading
def webView_didFinishLoadForFrame_(self,webview,frame):
# don't care about subframes
if (frame == webview.mainFrame()):
view = frame.frameView().documentView()
self.resizeWebview(view)
URL = frame.dataSource().initialRequest().URL().absoluteString()
for fl in glob.glob("webrender-*.gif"):
os.remove(fl)
GIF = "webrender-%s.gif" % (random.randrange(0,1000))
bitmapdata = self.captureView(view)
bitmapdata.representationUsingType_properties_(AppKit.NSGIFFileType,None).writeToFile_atomically_(GIF,objc.YES)
myurl = "http://%s:%s%s" % (socket.gethostbyname(socket.gethostname()), os.getenv("SERVER_PORT"), os.getenv("SCRIPT_NAME"))
print "Content-type: text/html\r\n\r\n"
print "<!-- webrender.py by Antoni Sawicki -->"
print "<html><head><title>Webrender - %s</title></head><body><table border=\"0\"><tr>" % (URL)
print "<td><form action=\"%s\">" % (myurl)
print "<input type=\"text\" name=\"url\" value=\"%s\" size=\"80\">" % (URL)
print "<input type=\"submit\" value=\"go\">"
print "</form></td><td>"
print "<form action=\"%s\">" % (myurl)
print "<input type=\"text\" name=\"search\" value=\"\" size=\"20\">"
print "<input type=\"submit\" value=\"search\">"
print "</form></td></tr></table>"
print "<img src=\"../%s\" alt=\"webrender\" usemap=\"#map\" border=\"0\">" % (GIF)
# Analyse HTML and get links
print "<map name=\"map\">";
domdocument = frame.DOMDocument()
domnodelist = domdocument.getElementsByTagName_('A')
i = 0
while i < domnodelist.length():
# linkvalue
value = domnodelist.item_(i).valueForKey_('href')
# position-rect
myrect = domnodelist.item_(i).boundingBox()
xmin = Foundation.NSMinX(myrect)
ymin = Foundation.NSMinY(myrect)
xmax = Foundation.NSMaxX(myrect)
ymax = Foundation.NSMaxY(myrect)
# print Link
escval = string.replace( string.replace(value, "?", "TNXQUE"), "&", "TNXAMP" )
print "<area shape=\"rect\" coords=\"%i,%i,%i,%i\" alt=\"\" href=\"%s?url=%s\"></area>" % (xmin, ymin, xmax, ymax, myurl, escval)
i += 1
print "</map>"
print "</body></html>"
self.getURL(webview)
def main():
# obtain url from cgi input
form = cgi.FieldStorage()
rawurl = form.getfirst("url", "http://www.google.com")
rawsearch = form.getfirst("search")
if rawsearch:
url = "http://www.google.com/search?q=%s" % (rawsearch)
else:
url = string.replace( string.replace(rawurl, "TNXAMP", "&"), "TNXQUE", "?")
AppKit.NSApplicationLoad();
app = AppKit.NSApplication.sharedApplication()
# create an app delegate
delegate = AppDelegate.alloc().init()
AppKit.NSApp().setDelegate_(delegate)
# create a window
rect = Foundation.NSMakeRect(-16000,-16000,100,100)
win = AppKit.NSWindow.alloc()
win.initWithContentRect_styleMask_backing_defer_ (rect, AppKit.NSBorderlessWindowMask, 2, 0)
# create a webview object
webview = WebKit.WebView.alloc()
webview.initWithFrame_(rect)
# turn off scrolling so the content is actually x wide and not x-15
webview.mainFrame().frameView().setAllowsScrolling_(objc.NO)
# add the webview to the window
win.setContentView_(webview)
# create a LoadDelegate
loaddelegate = WebkitLoad.alloc().init()
loaddelegate.options = [""]
loaddelegate.urls = [url]
webview.setFrameLoadDelegate_(loaddelegate)
app.run()
if __name__ == '__main__' : main()

360
wrp.go
View File

@@ -12,105 +12,137 @@ import (
"context"
"flag"
"fmt"
_ "image"
"image/gif"
"image/png"
"log"
"math/rand"
"net/http"
"net/url"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
"time"
"github.com/chromedp/cdproto/emulation"
"github.com/chromedp/cdproto/runtime"
"github.com/chromedp/cdproto/cdp"
"github.com/chromedp/cdproto/dom"
"github.com/chromedp/chromedp"
"github.com/ericpauley/go-quantize/quantize"
)
// Ismap for server side processing
type Ismap struct {
xmin int64
ymin int64
xmax int64
ymax int64
url string
}
var (
version = "3.0"
version = "4.2"
srv http.Server
ctx context.Context
cancel context.CancelFunc
gifmap = make(map[string]bytes.Buffer)
ismap = make(map[string][]Ismap)
ismap = make(map[string]wrpReq)
)
func pageServer(out http.ResponseWriter, req *http.Request) {
type wrpReq struct {
U string // url
W int64 // width
H int64 // height
S float64 // scale
C int64 // #colors
X int64 // mouseX
Y int64 // mouseY
K string // keys to send
F string // Fn buttons
}
func (w *wrpReq) parseForm(req *http.Request) {
req.ParseForm()
u := req.FormValue("url")
var istr string
var ion string
var i bool
if req.FormValue("i") == "on" {
istr = "CHECKED"
i = true
ion = "&i=on"
} else {
istr = ""
i = false
w.U = req.FormValue("url")
if len(w.U) > 1 && !strings.HasPrefix(w.U, "http") {
w.U = fmt.Sprintf("http://www.google.com/search?q=%s", url.QueryEscape(w.U))
}
p, _ := strconv.ParseInt(req.FormValue("p"), 10, 64)
if req.FormValue("pg") == "Dn" {
p++
} else if req.FormValue("pg") == "Up" {
p--
} else {
p = 0
w.W, _ = strconv.ParseInt(req.FormValue("w"), 10, 64)
if w.W < 10 {
w.W = 1152
}
w, _ := strconv.ParseInt(req.FormValue("w"), 10, 64)
if w < 10 {
w = 1024
w.H, _ = strconv.ParseInt(req.FormValue("h"), 10, 64)
if w.H < 10 {
w.H = 600
}
h, _ := strconv.ParseInt(req.FormValue("h"), 10, 64)
if h < 10 {
h = 768
w.S, _ = strconv.ParseFloat(req.FormValue("s"), 64)
if w.S < 0.1 {
w.S = 1.0
}
s, _ := strconv.ParseFloat(req.FormValue("s"), 64)
if s < 0.1 {
s = 1.0
w.C, _ = strconv.ParseInt(req.FormValue("c"), 10, 64)
if w.C < 2 || w.C > 256 {
w.C = 256
}
c, _ := strconv.ParseInt(req.FormValue("c"), 10, 64)
if c < 2 || c > 256 {
c = 256
}
log.Printf("%s Page Reqest for url=\"%s\" [%s]\n", req.RemoteAddr, u, req.URL.Path)
w.K = req.FormValue("k")
w.F = req.FormValue("Fn")
log.Printf("%s WrpReq from Form: %+v\n", req.RemoteAddr, w)
}
func (w wrpReq) printPage(out http.ResponseWriter) {
out.Header().Set("Cache-Control", "max-age=0")
out.Header().Set("Expires", "-1")
out.Header().Set("Pragma", "no-cache")
out.Header().Set("Content-Type", "text/html")
fmt.Fprintf(out, "<!-- Web Rendering Proxy Version %s -->\n", version)
fmt.Fprintf(out, "<HTML>\n<HEAD><TITLE>WRP %s</TITLE></HEAD>\n<BODY BGCOLOR=\"#F0F0F0\">\n", u)
fmt.Fprintf(out, "<FORM ACTION=\"/\"><INPUT TYPE=\"TEXT\" NAME=\"url\" VALUE=\"%s\" SIZE=\"20\">", u)
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" VALUE=\"Go\"> \n")
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"pg\" VALUE=\"Up\"> \n")
fmt.Fprintf(out, "<INPUT TYPE=\"TEXT\" NAME=\"p\" VALUE=\"%d\" SIZE=\"2\"> \n", p)
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"pg\" VALUE=\"Dn\"> \n")
fmt.Fprintf(out, "I <INPUT TYPE=\"CHECKBOX\" NAME=\"i\" %s> \n", istr)
fmt.Fprintf(out, "W <INPUT TYPE=\"TEXT\" NAME=\"w\" VALUE=\"%d\" SIZE=\"4\"> \n", w)
fmt.Fprintf(out, "H <INPUT TYPE=\"TEXT\" NAME=\"h\" VALUE=\"%d\" SIZE=\"4\"> \n", h)
fmt.Fprintf(out, "S <INPUT TYPE=\"TEXT\" NAME=\"s\" VALUE=\"%1.2f\" SIZE=\"3\"> \n", s)
fmt.Fprintf(out, "C <INPUT TYPE=\"TEXT\" NAME=\"c\" VALUE=\"%d\" SIZE=\"3\"> \n", c)
fmt.Fprintf(out, "<HTML>\n<HEAD><TITLE>WRP %s</TITLE></HEAD>\n<BODY BGCOLOR=\"#F0F0F0\">\n", w.U)
fmt.Fprintf(out, "<FORM ACTION=\"/\" METHOD=\"POST\">\n")
fmt.Fprintf(out, "<INPUT TYPE=\"TEXT\" NAME=\"url\" VALUE=\"%s\" SIZE=\"10\">", w.U)
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" VALUE=\"Go\">\n")
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"Fn\" VALUE=\"Bk\">\n")
fmt.Fprintf(out, "W <INPUT TYPE=\"TEXT\" NAME=\"w\" VALUE=\"%d\" SIZE=\"4\"> \n", w.W)
fmt.Fprintf(out, "H <INPUT TYPE=\"TEXT\" NAME=\"h\" VALUE=\"%d\" SIZE=\"4\"> \n", w.H)
fmt.Fprintf(out, "S <INPUT TYPE=\"TEXT\" NAME=\"s\" VALUE=\"%1.2f\" SIZE=\"3\"> \n", w.S)
fmt.Fprintf(out, "C <INPUT TYPE=\"TEXT\" NAME=\"c\" VALUE=\"%d\" SIZE=\"3\">\n", w.C)
fmt.Fprintf(out, "K <INPUT TYPE=\"TEXT\" NAME=\"k\" VALUE=\"\" SIZE=\"4\"> \n")
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"Fn\" VALUE=\"Bs\">\n")
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"Fn\" VALUE=\"Rt\">\n")
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"Fn\" VALUE=\"&lt;\">\n")
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"Fn\" VALUE=\"^\">\n")
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"Fn\" VALUE=\"v\">\n")
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"Fn\" VALUE=\"&gt;\" SIZE=\"1\">\n")
fmt.Fprintf(out, "</FORM><BR>\n")
if len(u) > 1 {
if strings.HasPrefix(u, "http") {
capture(u, w, h, s, int(c), p, i, req.RemoteAddr, out)
} else {
capture(fmt.Sprintf("http://www.google.com/search?q=%s", url.QueryEscape(u)), w, h, s, int(c), p, i, req.RemoteAddr, out)
}
}
func (w wrpReq) printFooter(out http.ResponseWriter) {
fmt.Fprintf(out, "\n<P><A HREF=\"/?url=https://github.com/tenox7/wrp/&w=%d&h=%d&s=%1.2f&c=%d\">"+
"Web Rendering Proxy Version %s</A> | <A HREF=\"/shutdown/\">Shutdown WRP</A></BODY>\n</HTML>\n", w.W, w.H, w.S, w.C, version)
}
func pageServer(out http.ResponseWriter, req *http.Request) {
log.Printf("%s Page Request for %s [%+v]\n", req.RemoteAddr, req.URL.Path, req.URL.RawQuery)
var w wrpReq
w.parseForm(req)
if len(w.U) > 4 {
w.capture(req.RemoteAddr, out)
} else {
fmt.Fprintf(out, "No URL or search query specified")
w.printPage(out)
w.printFooter(out)
}
}
func mapServer(out http.ResponseWriter, req *http.Request) {
log.Printf("%s ISMAP Request for %s [%+v]\n", req.RemoteAddr, req.URL.Path, req.URL.RawQuery)
w, ok := ismap[req.URL.Path]
if !ok {
fmt.Fprintf(out, "Unable to find map %s\n", req.URL.Path)
log.Printf("Unable to find map %s\n", req.URL.Path)
return
}
defer delete(ismap, req.URL.Path)
n, err := fmt.Sscanf(req.URL.RawQuery, "%d,%d", &w.X, &w.Y)
if err != nil || n != 2 {
fmt.Fprintf(out, "n=%d, err=%s\n", n, err)
log.Printf("%s ISMAP n=%d, err=%s\n", req.RemoteAddr, n, err)
return
}
log.Printf("%s WrpReq from ISMAP: %+v\n", req.RemoteAddr, w)
if len(w.U) > 4 {
w.capture(req.RemoteAddr, out)
} else {
w.printPage(out)
w.printFooter(out)
}
fmt.Fprintf(out, "\n<P><A HREF=\"/?url=https://github.com/tenox7/wrp/&w=%d&h=%d&s=%1.2f&c=%d%s\">Web Rendering Proxy Version %s</A></BODY>\n</HTML>\n", w, h, s, c, ion, version)
}
func imgServer(out http.ResponseWriter, req *http.Request) {
@@ -124,70 +156,76 @@ func imgServer(out http.ResponseWriter, req *http.Request) {
defer delete(gifmap, req.URL.Path)
out.Header().Set("Content-Type", "image/gif")
out.Header().Set("Content-Length", strconv.Itoa(len(gifbuf.Bytes())))
out.Header().Set("Cache-Control", "max-age=0")
out.Header().Set("Expires", "-1")
out.Header().Set("Pragma", "no-cache")
out.Write(gifbuf.Bytes())
out.(http.Flusher).Flush()
}
func mapServer(out http.ResponseWriter, req *http.Request) {
log.Printf("%s ISMAP Request for %s [%+v]\n", req.RemoteAddr, req.URL.Path, req.URL.RawQuery)
var loc string
var x, y int64
n, err := fmt.Sscanf(req.URL.RawQuery, "%d,%d", &x, &y)
if err != nil || n != 2 {
fmt.Fprintf(out, "n=%d, err=%s\n", n, err)
log.Printf("%s ISMAP n=%d, err=%s\n", req.RemoteAddr, n, err)
return
}
is, ok := ismap[req.URL.Path]
if !ok || is == nil {
fmt.Fprintf(out, "Unable to find map %s\n", req.URL.Path)
log.Printf("Unable to find map %s\n", req.URL.Path)
return
}
defer delete(ismap, req.URL.Path)
for _, i := range is {
if x >= i.xmin && x <= i.xmax && y >= i.ymin && y <= i.ymax {
loc = i.url
}
}
if len(loc) < 1 {
loc = is[0].url
}
log.Printf("%s ISMAP Redirect to: http://%s%s\n", req.RemoteAddr, req.Context().Value(http.LocalAddrContextKey), loc)
http.Redirect(out, req, fmt.Sprintf("http://%s%s", req.Context().Value(http.LocalAddrContextKey), loc), 301)
}
func capture(gourl string, w int64, h int64, s float64, co int, p int64, i bool, c string, out http.ResponseWriter) {
var nodes []*cdp.Node
ctxx := chromedp.FromContext(ctx)
func (w wrpReq) capture(c string, out http.ResponseWriter) {
var pngbuf []byte
var gifbuf bytes.Buffer
var loc string
var res *runtime.RemoteObject
is := make([]Ismap, 0)
var ion string
var err error
log.Printf("%s Processing Caputure Request for %s\n", c, gourl)
// Run ChromeDP Magic
err := chromedp.Run(ctx,
emulation.SetDeviceMetricsOverride(int64(float64(w)/s), int64(float64(h)/s), s, false),
chromedp.Navigate(gourl),
chromedp.Evaluate(fmt.Sprintf("window.scrollTo(0, %d);", p*int64(float64(h)*float64(0.9))), &res),
chromedp.Sleep(time.Second*1),
chromedp.CaptureScreenshot(&pngbuf),
chromedp.Location(&loc),
chromedp.Nodes("a", &nodes, chromedp.ByQueryAll))
if w.X > 0 && w.Y > 0 {
log.Printf("%s Mouse Click %d,%d\n", c, w.X, w.Y)
err = chromedp.Run(ctx, chromedp.MouseClickXY(int64(float64(w.X)/w.S), int64(float64(w.Y)/w.S)))
} else if len(w.F) > 0 {
log.Printf("%s Button %v\n", c, w.F)
switch w.F {
case "Bk":
err = chromedp.Run(ctx, chromedp.NavigateBack())
case "Bs":
err = chromedp.Run(ctx, chromedp.KeyEvent("\b"))
case "Rt":
err = chromedp.Run(ctx, chromedp.KeyEvent("\r"))
case "<":
err = chromedp.Run(ctx, chromedp.KeyEvent("\u0302"))
case "^":
err = chromedp.Run(ctx, chromedp.KeyEvent("\u0304"))
case "v":
err = chromedp.Run(ctx, chromedp.KeyEvent("\u0301"))
case ">":
err = chromedp.Run(ctx, chromedp.KeyEvent("\u0303"))
}
} else if len(w.K) > 0 {
log.Printf("%s Sending Keys: %#v\n", c, w.K)
err = chromedp.Run(ctx, chromedp.KeyEvent(w.K))
} else {
log.Printf("%s Processing Capture Request for %s\n", c, w.U)
err = chromedp.Run(ctx,
emulation.SetDeviceMetricsOverride(int64(float64(w.W)/w.S), int64(float64(w.H)/w.S), w.S, false),
chromedp.Navigate(w.U),
)
}
if err != nil {
log.Printf("%s %s", c, err)
fmt.Fprintf(out, "<BR>%s<BR>", err)
if err.Error() == "context canceled" {
log.Printf("%s Contex cancelled, try again", c)
fmt.Fprintf(out, "<BR>%s<BR> -- restarting, try again", err)
ctx, cancel = chromedp.NewContext(context.Background())
} else {
log.Printf("%s %s", c, err)
fmt.Fprintf(out, "<BR>%s<BR>", err)
}
return
}
log.Printf("%s Landed on: %s, Nodes: %d\n", c, loc, len(nodes))
chromedp.Run(
ctx, chromedp.Sleep(time.Second*2),
chromedp.Location(&w.U),
)
log.Printf("%s Landed on: %s\n", c, w.U)
w.printPage(out)
// Process Screenshot Image
err = chromedp.Run(ctx, chromedp.CaptureScreenshot(&pngbuf))
if err != nil {
log.Printf("%s Failed to capture screenshot: %s\n", c, err)
fmt.Fprintf(out, "<BR>Unable to capture screenshot:<BR>%s<BR>\n", err)
return
}
bytes.NewReader(pngbuf).Seek(0, 0)
img, err := png.Decode(bytes.NewReader(pngbuf))
if err != nil {
@@ -196,60 +234,37 @@ func capture(gourl string, w int64, h int64, s float64, co int, p int64, i bool,
return
}
gifbuf.Reset()
err = gif.Encode(&gifbuf, img, &gif.Options{NumColors: co})
err = gif.Encode(&gifbuf, img, &gif.Options{NumColors: int(w.C), Quantizer: quantize.MedianCutQuantizer{}})
if err != nil {
log.Printf("%s Failed to encode GIF: %s\n", c, err)
fmt.Fprintf(out, "<BR>Unable to encode GIF:<BR>%s<BR>\n", err)
return
}
// Compose map and gif
seq := rand.Intn(9999)
imgpath := fmt.Sprintf("/img/%04d.gif", seq)
mappath := fmt.Sprintf("/map/%04d.map", seq)
log.Printf("%s Encoded GIF image: %s, Size: %dKB\n", c, imgpath, len(gifbuf.Bytes())/1024)
gifmap[imgpath] = gifbuf
ismap[mappath] = w
log.Printf("%s Encoded GIF image: %s, Size: %dKB, Colors: %d\n", c, imgpath, len(gifbuf.Bytes())/1024, w.C)
fmt.Fprintf(out, "<A HREF=\"%s\"><IMG SRC=\"%s\" BORDER=\"0\" ISMAP></A>", mappath, imgpath)
w.printFooter(out)
log.Printf("%s Done with caputure for %s\n", c, w.U)
}
// Process Nodes
base, _ := url.Parse(loc)
if i {
fmt.Fprintf(out, "<A HREF=\"%s\"><IMG SRC=\"%s\" ALT=\"wrp\" BORDER=\"0\" ISMAP></A>", mappath, imgpath)
is = append(is, Ismap{xmin: -1, xmax: -1, ymin: -1, ymax: -1, url: fmt.Sprintf("/?url=%s&w=%d&h=%d&s=%1.2f&c=%d&i=on", loc, w, h, s, co)})
ion = "&i=on"
} else {
fmt.Fprintf(out, "<IMG SRC=\"%s\" ALT=\"wrp\" BORDER=\"0\" USEMAP=\"#map\">\n<MAP NAME=\"map\">\n", imgpath)
}
for _, n := range nodes {
b, err := dom.GetBoxModel().WithNodeID(n.NodeID).Do(cdp.WithExecutor(ctx, ctxx.Target))
if err != nil {
continue
}
tgt, err := base.Parse(n.AttributeValue("href"))
if err != nil {
continue
}
target := fmt.Sprintf("/?url=%s&w=%d&h=%d&s=%1.2f&c=%d%s", tgt, w, h, s, co, ion) // no page# here
if len(b.Content) > 6 && len(target) > 7 {
if i {
is = append(is, Ismap{
xmin: int64(b.Content[0] * s), ymin: int64(b.Content[1] * s),
xmax: int64(b.Content[4] * s), ymax: int64(b.Content[5] * s),
url: target})
} else {
fmt.Fprintf(out, "<AREA SHAPE=\"RECT\" COORDS=\"%.f,%.f,%.f,%.f\" ALT=\"%s\" TITLE=\"%s\" HREF=\"%s\">\n",
b.Content[0]*s, b.Content[1]*s, b.Content[4]*s, b.Content[5]*s, n.AttributeValue("href"), n.AttributeValue("href"), target)
}
}
}
if i {
log.Printf("%s Encoded ISMAP %s\n", c, mappath)
} else {
fmt.Fprintf(out, "</MAP>\n")
}
func haltServer(out http.ResponseWriter, req *http.Request) {
log.Printf("%s Shutdown Request for %s\n", req.RemoteAddr, req.URL.Path)
out.Header().Set("Cache-Control", "max-age=0")
out.Header().Set("Expires", "-1")
out.Header().Set("Pragma", "no-cache")
out.Header().Set("Content-Type", "text/text")
fmt.Fprintf(out, "Shutting down WRP...\n")
out.(http.Flusher).Flush()
log.Printf("%s Done with caputure for %s\n", c, gourl)
ismap[mappath] = is
time.Sleep(time.Second * 2)
cancel()
srv.Shutdown(context.Background())
os.Exit(1)
}
func main() {
@@ -267,9 +282,10 @@ func main() {
}
opts := append(chromedp.DefaultExecAllocatorOptions[:],
chromedp.Flag("headless", headless),
chromedp.Flag("hide-scrollbars", false),
)
actx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
defer cancel()
actx, acancel := chromedp.NewExecAllocator(context.Background(), opts...)
defer acancel()
if debug {
ctx, cancel = chromedp.NewContext(actx, chromedp.WithDebugf(log.Printf))
} else {
@@ -277,11 +293,25 @@ func main() {
}
defer cancel()
rand.Seed(time.Now().UnixNano())
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
log.Printf("Interrupt - shutting down.")
cancel()
srv.Shutdown(context.Background())
os.Exit(1)
}()
http.HandleFunc("/", pageServer)
http.HandleFunc("/img/", imgServer)
http.HandleFunc("/map/", mapServer)
http.HandleFunc("/img/", imgServer)
http.HandleFunc("/shutdown/", haltServer)
http.HandleFunc("/favicon.ico", http.NotFound)
log.Printf("Web Rendering Proxy Version %s\n", version)
log.Printf("Starting WRP http server on %s\n", addr)
http.ListenAndServe(addr, nil)
srv.Addr = addr
err := srv.ListenAndServe()
if err != nil {
log.Fatal(err)
}
}

BIN
wrp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB