We CORS now. At least on sys.4chan.org, so ban detection on non Chrom* browsers is still inaccurate. Close #368

This commit is contained in:
Nicolas Stepien 2012-04-02 11:58:30 +02:00
parent f1ef6dfd2b
commit 8a0e70b19c
4 changed files with 109 additions and 437 deletions

View File

@ -9,7 +9,6 @@
// @include http*://boards.4chan.org/*
// @include http*://images.4chan.org/*
// @include http*://sys.4chan.org/*
// @include http*://www.4chan.org/*
// @run-at document-start
// @updateURL https://raw.github.com/MayhemYDG/4chan-x/stable/4chan_x.user.js
// @icon http://mayhemydg.github.com/4chan-x/favicon.gif
@ -1226,46 +1225,17 @@
return setTimeout(this.asyncInit);
},
asyncInit: function() {
var form, iframe, link, loadChecking, script, src;
form = $('form[name=post]');
var link, script;
if (Conf['Hide Original Post Form']) {
link = $.el('h1', {
innerHTML: "<a href=javascript:;>" + (g.REPLY ? 'Quick Reply' : 'New Thread') + "</a>"
});
$.on($('a', link), 'click', function() {
$.on(link.firstChild, 'click', function() {
QR.open();
if (!g.REPLY) $('select', QR.el).value = 'new';
return $('textarea', QR.el).focus();
});
$.before(form, link);
}
if (/chrome/i.test(navigator.userAgent)) {
QR.status({
ready: true
});
} else {
src = "http" + (/^https/.test(form.action) ? 's' : '') + "://sys.4chan.org/robots.txt";
iframe = $.el('iframe', {
id: 'iframe',
src: src
});
$.on(iframe, 'error', function() {
return this.src = this.src;
});
loadChecking = function(iframe) {
if (!QR.status.ready) {
iframe.src = 'about:blank';
return setTimeout((function() {
return iframe.src = src;
}), 100);
}
};
$.on(iframe, 'load', function() {
if (this.src !== 'about:blank') {
return setTimeout(loadChecking, 500, this);
}
});
$.add(d.head, iframe);
$.before($('form[name=post]'), link);
}
script = $.el('script', {
textContent: 'Recaptcha.focus_response_field=function(){}'
@ -1294,9 +1264,7 @@
close: function() {
var i, spoiler, _i, _len, _ref;
QR.el.hidden = true;
QR.message.send({
req: 'abort'
});
QR.abort();
d.activeElement.blur();
$.removeClass(QR.el, 'dump');
_ref = QR.replies;
@ -1341,23 +1309,12 @@
status: function(data) {
var disabled, input, value;
if (data == null) data = {};
if (data.ready) {
QR.status.ready = true;
QR.status.banned = data.banned;
} else if (!QR.status.ready) {
value = 'Loading';
disabled = true;
}
if (g.dead) {
value = 404;
disabled = true;
QR.cooldown.auto = false;
} else if (QR.status.banned) {
value = 'Banned';
disabled = true;
} else {
value = QR.cooldown.seconds || data.progress || value;
}
value = QR.cooldown.seconds || data.progress || value;
if (!QR.el) return;
input = QR.status.input;
input.value = QR.cooldown.auto && Conf['Cooldown'] ? value ? "Auto " + value : 'Auto' : value || 'Submit';
@ -1823,16 +1780,14 @@
return QR.el.dispatchEvent(e);
},
submit: function(e) {
var captcha, captchas, challenge, err, file, m, post, reader, reply, response, threadID;
var callbacks, captcha, captchas, challenge, err, form, m, name, opts, post, reply, response, threadID, val;
if (e != null) e.preventDefault();
if (QR.cooldown.seconds) {
QR.cooldown.auto = !QR.cooldown.auto;
QR.status();
return;
}
QR.message.send({
req: 'abort'
});
QR.abort();
reply = QR.replies[0];
if (!(reply.com || reply.file)) {
err = 'No file selected.';
@ -1865,8 +1820,10 @@
if (Conf['Thread Watcher'] && Conf['Auto Watch Reply'] && threadID !== 'new') {
Watcher.watch(threadID);
}
QR.status({
progress: '...'
});
post = {
postURL: $('form[name=post]').action,
resto: threadID,
name: reply.name,
email: reply.email,
@ -1879,37 +1836,51 @@
recaptcha_challenge_field: challenge,
recaptcha_response_field: response + ' '
};
QR.status({
progress: '...'
});
if ($.engine === 'gecko' && reply.file) {
file = {};
reader = new FileReader();
reader.onload = function() {
file.buffer = this.result;
file.name = reply.file.name;
file.type = reply.file.type;
post.upfile = file;
return QR.message.send(post);
};
reader.readAsBinaryString(reply.file);
return;
form = new FormData();
for (name in post) {
val = post[name];
if (val) form.append(name, val);
}
if (/chrome/i.test(navigator.userAgent)) {
QR.message.post(post);
return;
}
return QR.message.send(post);
callbacks = {
onload: function() {
return QR.response(this.response);
},
onerror: function() {
return QR.error('_', $.el('a', {
href: '//www.4chan.org/banned',
target: '_blank',
textContent: 'Connection error, or you are banned.'
}));
}
};
opts = {
form: form,
type: 'POST',
upCallbacks: {
onload: function() {
return QR.status({
progress: '...'
});
},
onprogress: function(e) {
return QR.status({
progress: "" + (Math.round(e.loaded / e.total * 100)) + "%"
});
}
}
};
return QR.ajax = $.ajax($('form[name=post]').action, callbacks, opts);
},
response: function(html) {
var b, doc, err, node, persona, postNumber, reply, thread, _, _ref;
doc = $.el('a', {
innerHTML: html
});
if ($('title', doc).textContent === '4chan - Banned') {
QR.message.receive({
req: 'banned'
});
doc = d.implementation.createHTMLDocument(null);
doc.documentElement.innerHTML = html;
if (doc.title === '4chan - Banned') {
QR.error('_', $.el('a', {
href: '//www.4chan.org/banned',
target: '_blank',
textContent: 'You are banned.'
}));
return;
}
if (!(b = $('td b', doc))) {
@ -1967,146 +1938,10 @@
QR.status();
return QR.resetFileInput();
},
message: {
send: function(data) {
var host, window;
if (/chrome/i.test(navigator.userAgent)) {
QR.message.receive(data);
return;
}
data.QR = true;
host = location.hostname;
window = host === 'boards.4chan.org' ? $.id('iframe').contentWindow : parent;
return window.postMessage(data, '*');
},
receive: function(data) {
var req, _ref;
req = data.req;
delete data.req;
delete data.QR;
switch (req) {
case 'abort':
if ((_ref = QR.ajax) != null) _ref.abort();
return QR.message.send({
req: 'status'
});
case 'response':
return QR.response(data.html);
case 'status':
return QR.status(data);
case 'connection error':
return QR.error('_', $.el('a', {
href: '//www.4chan.org/banned',
target: '_blank',
textContent: 'Connection error, or you are banned.'
}));
case 'banned':
QR.error('_', $.el('a', {
href: '//www.4chan.org/banned',
target: '_blank',
textContent: 'You are banned.'
}));
return QR.status({
ready: true,
banned: true
});
default:
return QR.message.post(data);
}
},
post: function(data) {
var boundary, callbacks, form, i, name, opts, parts, toBin, url, val;
url = data.postURL;
delete data.postURL;
if ($.engine === 'gecko' && data.upfile) {
if (!data.binary) {
toBin = function(data, name, val) {
var bb, r;
bb = new MozBlobBuilder();
bb.append(val);
r = new FileReader();
r.onload = function() {
data[name] = r.result;
if (!--i) return QR.message.post(data);
};
return r.readAsBinaryString(bb.getBlob('text/plain'));
};
i = Object.keys(data).length;
for (name in data) {
val = data[name];
if (typeof val === 'object') {
toBin(data.upfile, 'name', data.upfile.name);
} else if (typeof val === 'boolean') {
if (val) {
toBin(data, name, String(val));
} else {
i--;
}
} else {
toBin(data, name, val);
}
}
data.postURL = url;
data.binary = true;
return;
}
delete data.binary;
boundary = '-------------SMCD' + Date.now();
parts = [];
parts.push('Content-Disposition: form-data; name="upfile"; filename="' + data.upfile.name + '"\r\n' + 'Content-Type: ' + data.upfile.type + '\r\n\r\n' + data.upfile.buffer + '\r\n');
delete data.upfile;
for (name in data) {
val = data[name];
if (val) {
parts.push('Content-Disposition: form-data; name="' + name + '"\r\n\r\n' + val + '\r\n');
}
}
form = '--' + boundary + '\r\n' + parts.join('--' + boundary + '\r\n') + '--' + boundary + '--\r\n';
} else {
form = new FormData();
for (name in data) {
val = data[name];
if (val) form.append(name, val);
}
}
callbacks = {
onload: function() {
return QR.message.send({
req: 'response',
html: this.response
});
},
onerror: function() {
return QR.message.send({
req: 'connection error'
});
}
};
opts = {
form: form,
type: 'post',
upCallbacks: {
onload: function() {
return QR.message.send({
req: 'status',
progress: '...'
});
},
onprogress: function(e) {
return QR.message.send({
req: 'status',
progress: "" + (Math.round(e.loaded / e.total * 100)) + "%"
});
}
}
};
if (boundary) {
opts.headers = {
'Content-Type': 'multipart/form-data;boundary=' + boundary
};
}
return QR.ajax = $.ajax(url, callbacks, opts);
}
abort: function() {
var _ref;
if ((_ref = QR.ajax) != null) _ref.abort();
return QR.status();
}
};
@ -2566,10 +2401,7 @@
d.title = d.title.match(/^.+-/)[0] + ' 404';
}
Unread.update(true);
QR.message.send({
req: 'abort'
});
QR.status();
QR.abort();
return;
}
Updater.retryCoef = 10;
@ -3811,12 +3643,7 @@
$.on(window, 'message', Main.message);
switch (location.hostname) {
case 'sys.4chan.org':
if (path === '/robots.txt') {
QR.message.send({
req: 'status',
ready: true
});
} else if (/report/.test(location.search)) {
if (/report/.test(location.search)) {
$.ready(function() {
return $.on($.id('recaptcha_response_field'), 'keydown', function(e) {
if (e.keyCode === 8 && !e.target.value) {
@ -3826,13 +3653,6 @@
});
}
return;
case 'www.4chan.org':
if (path === '/banned') {
QR.message.send({
req: 'banned'
});
}
return;
case 'images.4chan.org':
$.ready(function() {
if (d.title === '4chan - 404') return Redirect.init();
@ -3993,13 +3813,8 @@
}
},
message: function(e) {
var data, version;
data = e.data;
if (data.QR) {
QR.message.receive(data);
return;
}
version = data.version;
var version;
version = e.data.version;
if (version && version !== Main.version && confirm('An updated version of 4chan X is available, would you like to install it now?')) {
return window.location = "https://raw.github.com/mayhemydg/4chan-x/" + version + "/4chan_x.user.js";
}

View File

@ -16,7 +16,6 @@ HEADER = """
// @include http*://boards.4chan.org/*
// @include http*://images.4chan.org/*
// @include http*://sys.4chan.org/*
// @include http*://www.4chan.org/*
// @run-at document-start
// @updateURL https://raw.github.com/MayhemYDG/4chan-x/stable/4chan_x.user.js
// @icon http://mayhemydg.github.com/4chan-x/favicon.gif

View File

@ -1,4 +1,6 @@
master
- Mayhem
Update posting method.
2.29.2
- Mayhem

View File

@ -1010,34 +1010,17 @@ QR =
setTimeout @asyncInit
asyncInit: ->
form = $ 'form[name=post]'
if Conf['Hide Original Post Form']
link = $.el 'h1', innerHTML: "<a href=javascript:;>#{if g.REPLY then 'Quick Reply' else 'New Thread'}</a>"
$.on $('a', link), 'click', ->
$.on link.firstChild, 'click', ->
QR.open()
$('select', QR.el).value = 'new' unless g.REPLY
$('textarea', QR.el).focus()
$.before form, link
# CORS is ignored for content script on Chrome, but not Safari/Oprah/Firefox.
if /chrome/i.test navigator.userAgent
QR.status ready: true
else
src = "http#{if /^https/.test form.action then 's' else ''}://sys.4chan.org/robots.txt"
iframe = $.el 'iframe',
id: 'iframe'
src: src
$.on iframe, 'error', -> @src = @src
# Greasemonkey ghetto fix
loadChecking = (iframe) ->
unless QR.status.ready
iframe.src = 'about:blank'
setTimeout (-> iframe.src = src), 100
$.on iframe, 'load', -> if @src isnt 'about:blank' then setTimeout loadChecking, 500, @
$.add d.head, iframe
$.before $('form[name=post]'), link
# Prevent original captcha input from being focused on reload.
script = $.el 'script', textContent: 'Recaptcha.focus_response_field=function(){}'
script = $.el 'script',
textContent: 'Recaptcha.focus_response_field=function(){}'
$.add d.head, script
$.rm script
@ -1059,7 +1042,7 @@ QR =
QR.dialog()
close: ->
QR.el.hidden = true
QR.message.send req: 'abort'
QR.abort()
d.activeElement.blur()
$.removeClass QR.el, 'dump'
for i in QR.replies
@ -1093,22 +1076,11 @@ QR =
$('.warning', QR.el).textContent = null
status: (data={}) ->
if data.ready
QR.status.ready = true
QR.status.banned = data.banned
else unless QR.status.ready
value = 'Loading'
disabled = true
if g.dead
value = 404
disabled = true
QR.cooldown.auto = false
else if QR.status.banned
value = 'Banned'
disabled = true
else
# do not cancel `value = 'Loading'` once the cooldown is over
value = QR.cooldown.seconds or data.progress or value
value = QR.cooldown.seconds or data.progress or value
return unless QR.el
{input} = QR.status
input.value =
@ -1509,7 +1481,7 @@ QR =
QR.cooldown.auto = !QR.cooldown.auto
QR.status()
return
QR.message.send req: 'abort'
QR.abort()
reply = QR.replies[0]
# prevent errors
@ -1549,8 +1521,11 @@ QR =
if Conf['Thread Watcher'] and Conf['Auto Watch Reply'] and threadID isnt 'new'
Watcher.watch threadID
# Starting to upload might take some time.
# Provide some feedback that we're starting to submit.
QR.status progress: '...'
post =
postURL: $('form[name=post]').action
resto: threadID
name: reply.name
email: reply.email
@ -1563,36 +1538,42 @@ QR =
recaptcha_challenge_field: challenge
recaptcha_response_field: response + ' '
# Starting to upload might take some time.
# Provide some feedback that we're starting to submit.
QR.status progress: '...'
form = new FormData()
for name, val of post
form.append name, val if val
if $.engine is 'gecko' and reply.file
# https://bugzilla.mozilla.org/show_bug.cgi?id=673742
# We plan to allow postMessaging Files and FileLists across origins,
# that just needs a more in depth security review.
file = {}
reader = new FileReader()
reader.onload = ->
file.buffer = @result
file.name = reply.file.name
file.type = reply.file.type
post.upfile = file
QR.message.send post
reader.readAsBinaryString reply.file
return
callbacks =
onload: ->
QR.response @response
onerror: ->
# Connection error, or
# CORS disabled error on www.4chan.org/banned
QR.error '_', $.el 'a',
href: '//www.4chan.org/banned'
target: '_blank'
textContent: 'Connection error, or you are banned.'
opts =
form: form
type: 'POST'
upCallbacks:
onload: ->
# Upload done, waiting for response.
QR.status progress: '...'
onprogress: (e) ->
# Uploading...
QR.status progress: "#{Math.round e.loaded / e.total * 100}%"
# CORS is ignored for content script on Chrome, but not Safari/Oprah/Firefox.
if /chrome/i.test navigator.userAgent
QR.message.post post
return
QR.message.send post
QR.ajax = $.ajax $('form[name=post]').action, callbacks, opts
response: (html) ->
doc = $.el 'a', innerHTML: html
doc = d.implementation.createHTMLDocument null
doc.documentElement.innerHTML = html
# Check for ban.
if $('title', doc).textContent is '4chan - Banned'
QR.message.receive req: 'banned'
if doc.title is '4chan - Banned'
QR.error '_', $.el 'a',
href: '//www.4chan.org/banned'
target: '_blank'
textContent: 'You are banned.'
return
unless b = $ 'td b', doc
err = 'Connection error with sys.4chan.org.'
@ -1650,123 +1631,9 @@ QR =
QR.status()
QR.resetFileInput()
message:
send: (data) ->
# CORS is ignored for content script on Chrome, but not Safari/Oprah/Firefox.
if /chrome/i.test navigator.userAgent
QR.message.receive data
return
data.QR = true
host = location.hostname
window =
if host is 'boards.4chan.org'
$.id('iframe').contentWindow
else
parent
window.postMessage data, '*'
receive: (data) ->
req = data.req
delete data.req
delete data.QR
switch req
when 'abort'
QR.ajax?.abort()
QR.message.send req: 'status'
when 'response' # xhr response
QR.response data.html
when 'status'
QR.status data
when 'connection error'
QR.error '_', $.el 'a',
href: '//www.4chan.org/banned'
target: '_blank'
textContent: 'Connection error, or you are banned.'
when 'banned'
QR.error '_', $.el 'a',
href: '//www.4chan.org/banned'
target: '_blank'
textContent: 'You are banned.'
# Disable iframe reloading
QR.status ready: true, banned: true
else
QR.message.post data # Reply object: we're posting
post: (data) ->
url = data.postURL
# Do not append these values to the form.
delete data.postURL
# File with filename upload fix from desuwa
if $.engine is 'gecko' and data.upfile
# All of this is fucking retarded.
unless data.binary
toBin = (data, name, val) ->
bb = new MozBlobBuilder()
bb.append val
r = new FileReader()
r.onload = ->
data[name] = r.result
unless --i
QR.message.post data
r.readAsBinaryString bb.getBlob 'text/plain'
i = Object.keys(data).length
for name, val of data
if typeof val is 'object' # File. toBin the filename.
toBin data.upfile, 'name', data.upfile.name
else if typeof val is 'boolean'
if val
toBin data, name, String val
else
i--
else
toBin data, name, val
data.postURL = url
data.binary = true
return
delete data.binary
boundary = '-------------SMCD' + Date.now();
parts = []
parts.push 'Content-Disposition: form-data; name="upfile"; filename="' + data.upfile.name + '"\r\n' + 'Content-Type: ' + data.upfile.type + '\r\n\r\n' + data.upfile.buffer + '\r\n'
delete data.upfile
for name, val of data
parts.push 'Content-Disposition: form-data; name="' + name + '"\r\n\r\n' + val + '\r\n' if val
form = '--' + boundary + '\r\n' + parts.join('--' + boundary + '\r\n') + '--' + boundary + '--\r\n'
else
form = new FormData()
for name, val of data
form.append name, val if val
callbacks =
onload: ->
QR.message.send
req: 'response'
html: @response
onerror: ->
# Connection error, or
# CORS disabled error: redirecting to banned page ;_;
QR.message.send req: 'connection error'
opts =
form: form
type: 'post'
upCallbacks:
onload: ->
QR.message.send
req: 'status'
progress: '...'
onprogress: (e) ->
QR.message.send
req: 'status'
progress: "#{Math.round e.loaded / e.total * 100}%"
if boundary
opts.headers =
'Content-Type': 'multipart/form-data;boundary=' + boundary
QR.ajax = $.ajax url, callbacks, opts
abort: ->
QR.ajax?.abort()
QR.status()
Options =
init: ->
@ -2179,8 +2046,7 @@ Updater =
else
d.title = d.title.match(/^.+-/)[0] + ' 404'
Unread.update true
QR.message.send req: 'abort'
QR.status()
QR.abort()
return
Updater.retryCoef = 10
@ -3178,17 +3044,11 @@ Main =
switch location.hostname
when 'sys.4chan.org'
if path is '/robots.txt'
QR.message.send req: 'status', ready: true
else if /report/.test location.search
if /report/.test location.search
$.ready ->
$.on $.id('recaptcha_response_field'), 'keydown', (e) ->
window.location = 'javascript:Recaptcha.reload()' if e.keyCode is 8 and not e.target.value
return
when 'www.4chan.org'
if path is '/banned'
QR.message.send req: 'banned'
return
when 'images.4chan.org'
$.ready -> Redirect.init() if d.title is '4chan - 404'
return
@ -3365,11 +3225,7 @@ Main =
$.on d, 'DOMNodeInserted', Main.addStyle
message: (e) ->
{data} = e
if data.QR
QR.message.receive data
return
{version} = data
{version} = e.data
if version and version isnt Main.version and confirm 'An updated version of 4chan X is available, would you like to install it now?'
window.location = "https://raw.github.com/mayhemydg/4chan-x/#{version}/4chan_x.user.js"