Forum rules
Under no circumstances is spamming or advertising of any kind allowed. Do not post any abusive, obscene, vulgar, slanderous, hateful, threatening, sexually-orientated or any other material that may violate others security. Profanity or any kind of insolent behavior to other members (regardless of rank) will not be tolerated. Remember, what you don’t find offensive can be offensive to other members. Please treat each other with the kind of reverence you’d expect from other members.
Failure to comply with any of the above will result in users being banned without notice. If any further details are needed, contact: “The team” using the link at the bottom of the forum page. Thank you.
filmlos
Posts: 1
Joined: Thu Feb 25, 2016 1:58 am

JS XmlRpcRequest

Thu Feb 25, 2016 2:09 am

Hello,
This worked for me over 2 years until 3 days ago:
(Login with Mimic JS from webpage)

Code: Select all

<script type="text/javascript" src="http://mydomain.com/js/mimic.js"></script> <script> var loginRequest = new XmlRpcRequest("http://api.opensubtitles.org/xml-rpc", "LogIn"); loginRequest.params = (['', '', 'eng', 'FileBot']); var response1 = loginRequest.send(); var token = String(response1.parseXML().token); alert(token); console.log(token); </script>
But then, suddenly stopped 3 days ago. It's still works in IE, only.

Example:
This test code (listing subtitles): http://188.165.30.86/subtest.html work great on Microsoft Edge but not on Firefox/Chrome.

Whole example code:

Code: Select all

<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Untitled Document</title> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script> <script type="text/javascript" src="http://mastermov.com/cdn/js/mimic.js"></script> </head> <body> <div align="center">IMDBID: tt0462499, List English Subtitles</div> <br> <div id="subtitles"></div> <script> var loginRequest = new XmlRpcRequest("http://api.opensubtitles.org/xml-rpc", "LogIn"); loginRequest.params = (['', '', 'eng', 'FileBot']); var response1 = loginRequest.send(); var token = String(response1.parseXML().token); var searchRequest = new XmlRpcRequest("http://api.opensubtitles.org/xml-rpc", "SearchSubtitles"); searchRequest.addParam(token); searchRequest.addParam([{imdbid: '0462499', season: '', episode: '', sublanguageid: 'eng'}]); var results = searchRequest.send(); var xml = results.parseXML(); console.log(xml); $(xml.data).each( function( index, element ){ var downlink = xml.data[index].ZipDownloadLink; var filename = xml.data[index].SubFileName; $("#subtitles").append("<a href="+downlink+">"+filename+"</a><br>"); }); </script> </body> </html>
Error:
"NetworkError: 405 Method Not Allowed - https://api.opensubtitles.org/xml-rpc"
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://api.opensubtitles.org/xml-rpc. (Reason: CORS header 'Access-Control-Allow-Origin' missing).


Did you remove Access-Control-Allow-Origin and can you get it back?

Thank you.

User avatar
oss
Site Admin
Posts: 5890
Joined: Sat Feb 25, 2006 11:26 pm
Contact: Website

Re: JS XmlRpcRequest

Fri Feb 26, 2016 11:22 am

Hi,

we changed something, but it shouldnt be problem. I tested your script, for some reason it works on https and not on http (I really dont know what is going on there) - if you can debug it more, it would be great, so I can fix it.

User avatar
vankasteelj
Posts: 175
Joined: Sun Nov 15, 2015 1:09 am

Re: JS XmlRpcRequest

Fri Feb 26, 2016 4:31 pm

I can second it works with https but not with http. @oss should we consider http deprecated for OpenSubtitles?

User avatar
oss
Site Admin
Posts: 5890
Joined: Sat Feb 25, 2006 11:26 pm
Contact: Website

Re: JS XmlRpcRequest

Fri Feb 26, 2016 5:31 pm

nope. the thing is I want to know why it is not working with http, somebody can debug this please ?

User avatar
vankasteelj
Posts: 175
Joined: Sun Nov 15, 2015 1:09 am

Re: JS XmlRpcRequest

Fri Feb 26, 2016 11:24 pm

Here are the HTTPS headers sent back by OS:

Code: Select all

Strict-Transport-Security: max-age=63072000; includeSubdomains; preload Content-Encoding: gzip X-Content-Type-Options: nosniff Age: 0 X-Cache: MISS X-Cache-Backend: web2 Vary: Accept-Encoding Content-Length: 5535 Pragma: no-cache Server: lighttpd/1.4.39 Date: Fri, 26 Feb 2016 21:18:42 GMT X-Frame-Options: DENY Access-Control-Allow-Methods: GET, POST, OPTIONS X-Uncompressed-Content-Length: 163462 Access-Control-Allow-Origin: * Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Accept-Ranges: bytes Content-type: text/xml;charset=UTF-8 Access-Control-Allow-Headers: Origin,X-Requested-With,Content-Type,Accept,DNT,Keep-Alive,User-Agent,If-Modified-Since,Cache-Control Expires: Thu, 19 Nov 1981 08:52:00 GMT
And the HTTP ones:

Code: Select all

Date: Fri, 26 Feb 2016 21:23:34 GMT Content-Encoding: gzip Age: 0 X-Cache: MISS X-Cache-Backend: web3 Connection: keep-alive Content-Length: 5534 Pragma: no-cache Vary: Accept-Encoding Access-Control-Allow-Methods: GET, POST, OPTIONS X-Uncompressed-Content-Length: 163462 Access-Control-Allow-Origin: * Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Accept-Ranges: bytes Content-type: text/xml;charset=UTF-8 Access-Control-Allow-Headers: Origin,X-Requested-With,Content-Type,Accept,DNT,Keep-Alive,User-Agent,If-Modified-Since,Cache-Control Expires: Thu, 19 Nov 1981 08:52:00 GMT
But today it works again in FF and Chrome, yesterday it wasn't working. Something has changed between then and now about "Access-Control-Allow-Origin: *". Bad CDN distribution if you use cloudflare or similar?

PS: to debug, change mimic.js:

Code: Select all

XmlRpcResponse(e){this.xmlData=e}
to

Code: Select all

XmlRpcResponse(e){console.log(e.getAllResponseHeaders());this.xmlData=e.responseXML;}
and

Code: Select all

new XmlRpcResponse(t.responseXML)
to

Code: Select all

new XmlRpcResponse(t)

User avatar
oss
Site Admin
Posts: 5890
Joined: Sat Feb 25, 2006 11:26 pm
Contact: Website

Re: JS XmlRpcRequest

Sat Feb 27, 2016 9:58 am

for me it is not working today again, headers should be same.

User avatar
vankasteelj
Posts: 175
Joined: Sun Nov 15, 2015 1:09 am

Re: JS XmlRpcRequest

Sat Feb 27, 2016 5:01 pm

I've debugged quite a lot, and the error isn't on OpenSubtitles side anymore, it's on the browser.

I suspect the "popup" blocker to be an issue, or some other builtin protection system, for example restrictions on DOMParser.

How ? Well, it works in Chrome, in Firefox "incognito mode", and in Edge, but not with Firefox, it throws a NS_ERROR_FAILURE that's really hard to debug (just google that and be amazed :p)

For you to test: this html contains everything, just save code to test.html and open in a browser

Code: Select all

<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Test XML-RPC | OpenSubtitles.org</title> </head> <body> <div> IMDBID: <input id="imdbid" value="tt0462499"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List subtitles in <input id="langcode" value="eng"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <button onClick="search()">Search!</button> </div> <br> <!-- subtitle list --> <div id="subtitles"></div> <br> <!-- debug header --> <div id="headers" style="display:none"> <strong>headers:</strong> <br> <code id="heads"></code> </div> <br> <!-- errors --> <div id="error"></div> <script> // XML-RPC function (source: http://mastermov.com/cdn/js/mimic.js) function XmlRpc() {} function XmlRpcRequest(e, t) { this.serviceUrl = e, this.methodName = t, this.crossDomain = !1, this.withCredentials = !1, this.params = [], this.headers = {} } function XmlRpcResponse(e, method) { var headers = e.getAllResponseHeaders(); document.getElementById('heads').innerHTML += "<span style='text-decoration:underline'>"+method+":</underline>" + "<br>"; document.getElementById('heads').innerHTML += headers.replace(/\n/g, '<br>') + "<br>"; document.getElementById('headers').style.display = 'block'; this.xmlData = e.responseXML; } function Builder() {} function Base64(e) { this.bytes = e; } XmlRpc.PROLOG = '<?xml version="1.0" encoding="utf-8" ?>\n', XmlRpc.REQUEST = "<methodCall>\n<methodName>${METHOD}</methodName>\n<params>\n${DATA}</params>\n</methodCall>", XmlRpc.PARAM = "<param>\n<value>\n${DATA}</value>\n</param>\n", XmlRpc.ARRAY = "<array>\n<data>\n${DATA}</data>\n</array>\n", XmlRpc.STRUCT = "<struct>\n${DATA}</struct>\n", XmlRpc.MEMBER = "<member>\n${DATA}</member>\n", XmlRpc.NAME = "<name>${DATA}</name>\n", XmlRpc.VALUE = "<value>\n${DATA}</value>\n", XmlRpc.SCALAR = "<${TYPE}>${DATA}</${TYPE}>\n", XmlRpc.getDataTag = function(e) { try { var t = typeof e; switch (t.toLowerCase()) { case "number": t = Math.round(e) == e ? "int" : "double"; break; case "object": t = e.constructor == Base64 ? "base64" : e.constructor == String ? "string" : e.constructor == Boolean ? "boolean" : e.constructor == Array ? "array" : e.constructor == Date ? "dateTime.iso8601" : e.constructor == Number ? Math.round(e) == e ? "int" : "double" : "struct" } return t } catch (a) { return null } }, XmlRpc.getTagData = function(e) { var t = null; switch (e) { case "struct": t = new Object; break; case "array": t = new Array; break; case "datetime.iso8601": t = new Date; break; case "boolean": t = new Boolean; break; case "int": case "i4": case "double": t = new Number; break; case "string": t = new String; break; case "base64": t = new Base64 } return t }, XmlRpcRequest.prototype.addParam = function(e) { var t = typeof e; switch (t.toLowerCase()) { case "function": return; case "object": if (!e.constructor.name) return } this.params.push(e) }, XmlRpcRequest.prototype.clearParams = function() { this.params.splice(0, this.params.length) }, XmlRpcRequest.prototype.setHeader = function(e, t) { t ? this.headers[e] = t : delete this.headers[e] }, XmlRpcRequest.prototype.send = function() { var e, t, a = "", s = 0; for (s = 0; s < this.params.length; s++) { a += XmlRpc.PARAM.replace("${DATA}", this.marshal(this.params[s])); } e = XmlRpc.REQUEST.replace("${METHOD}", this.methodName); e = XmlRpc.PROLOG + e.replace("${DATA}", a); t = Builder.buildXHR(this.crossDomain); t.open("POST", this.serviceUrl, !1); for (s in this.headers) { if (this.headers.hasOwnProperty(s)) { t.setRequestHeader(s, this.headers[s]); } } return this.withCredentials && "withCredentials" in t && (t.withCredentials = !0), t.send(Builder.buildDOM(e)), new XmlRpcResponse(t, this.methodName); }, XmlRpcRequest.prototype.marshal = function(e) { var t, a, s, r = XmlRpc.getDataTag(e), n = XmlRpc.SCALAR.replace(/\$\{TYPE\}/g, r), i = ""; switch (r) { case "struct": s = ""; for (a in e) t = "", t += XmlRpc.NAME.replace("${DATA}", a), t += XmlRpc.VALUE.replace("${DATA}", this.marshal(e[a])), s += XmlRpc.MEMBER.replace("${DATA}", t); i = XmlRpc.STRUCT.replace("${DATA}", s); break; case "array": for (t = "", a = 0; a < e.length; a++) t += XmlRpc.VALUE.replace("${DATA}", this.marshal(e[a])); i = XmlRpc.ARRAY.replace("${DATA}", t); break; case "dateTime.iso8601": i = n.replace("${DATA}", e.toIso8601()); break; case "boolean": i = n.replace("${DATA}", 1 == e ? 1 : 0); break; case "base64": i = n.replace("${DATA}", e.encode()); break; default: i = n.replace("${DATA}", e) } return i }, XmlRpcResponse.prototype.isFault = function() { return this.faultValue }, XmlRpcResponse.prototype.parseXML = function() { var e, t; for (t = this.xmlData.childNodes.length, this.faultValue = void 0, this.currentIsName = !1, this.propertyName = "", this.params = [], e = 0; t > e; e++) this.unmarshal(this.xmlData.childNodes[e], 0); return this.params[0] }, XmlRpcResponse.prototype.unmarshal = function(e, t) { var a, s, r, n; if (1 == e.nodeType) { switch (a = null, s = e.tagName.toLowerCase()) { case "fault": this.faultValue = !0; break; case "name": this.currentIsName = !0; break; default: a = XmlRpc.getTagData(s) } if (null != a && (this.params.push(a), "struct" == s || "array" == s)) { if (this.params.length > 1) switch (XmlRpc.getDataTag(this.params[t])) { case "struct": this.params[t][this.propertyName] = this.params[this.params.length - 1]; break; case "array": this.params[t].push(this.params[this.params.length - 1]) } t = this.params.length - 1 } for (n = e.childNodes.length, r = 0; n > r; r++) this.unmarshal(e.childNodes[r], t) } if (3 == e.nodeType && /[^\t\n\r ]/.test(e.nodeValue)) if (1 == this.currentIsName) this.propertyName = e.nodeValue, this.currentIsName = !1; else { switch (XmlRpc.getDataTag(this.params[this.params.length - 1])) { case "dateTime.iso8601": this.params[this.params.length - 1] = Date.fromIso8601(e.nodeValue); break; case "boolean": this.params[this.params.length - 1] = "1" == e.nodeValue ? !0 : !1; break; case "int": case "double": this.params[this.params.length - 1] = new Number(e.nodeValue); break; case "string": this.params[this.params.length - 1] = new String(e.nodeValue); break; case "base64": this.params[this.params.length - 1] = new Base64(e.nodeValue) } if (this.params.length > 1) switch (XmlRpc.getDataTag(this.params[t])) { case "struct": this.params[t][this.propertyName] = this.params[this.params.length - 1]; break; case "array": this.params[t].push(this.params[this.params.length - 1]) } } }, Builder.buildXHR = function(e) { return e ? "undefined" != typeof XDomainRequest ? new XDomainRequest : new XMLHttpRequest : "undefined" != typeof XMLHttpRequest ? new XMLHttpRequest : new ActiveXObject("Microsoft.XMLHTTP") }, Builder.buildDOM = function(e) { var t, a, s, r; if ("undefined" != typeof DOMParser) { t = new DOMParser; r = t.parseFromString(e, "text/xml"); return r; } else { for (a = ["Microsoft.XMLDOM", "MSXML2.DOMDocument", "MSXML.DOMDocument"], s = 0; s < a.length; s++) { try { return t = new ActiveXObject(a[s]), t.loadXML(e), t; } catch (r) { throw r; } } } }, Date.prototype.toIso8601 = function() { var e = this.getYear(), t = this.getMonth() + 1, a = this.getDate(), s = this.toTimeString().substr(0, 8); return 1900 > e && (e += 1900), 10 > t && (t = "0" + t), 10 > a && (a = "0" + a), e + t + a + "T" + s }, Date.fromIso8601 = function(e) { var t = e.substr(0, 4), a = e.substr(4, 2), s = e.substr(6, 2), r = e.substr(9, 2), n = e.substr(12, 2), i = e.substr(15, 2); return new Date(t, a - 1, s, r, n, i, 0) }, Base64.CHAR_MAP = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", Base64.prototype.encode = function() { if ("function" == typeof btoa) return btoa(this.bytes); var e = [], t = [], a = [], s = 0, r = 0; for (r = 0; r < this.bytes.length; r += 3) e[0] = this.bytes.charCodeAt(r), e[1] = this.bytes.charCodeAt(r + 1), e[2] = this.bytes.charCodeAt(r + 2), t[0] = e[0] >> 2, t[1] = (3 & e[0]) << 4 | e[1] >> 4, t[2] = (15 & e[1]) << 2 | e[2] >> 6, t[3] = 63 & e[2], isNaN(e[1]) ? t[2] = t[3] = 64 : isNaN(e[2]) && (t[3] = 64), a[s++] = Base64.CHAR_MAP.charAt(t[0]) + Base64.CHAR_MAP.charAt(t[1]) + Base64.CHAR_MAP.charAt(t[2]) + Base64.CHAR_MAP.charAt(t[3]); return a.join("") }, Base64.prototype.decode = function() { if ("function" == typeof atob) return atob(this.bytes); for (var e = [], t = [], a = [], s = 0, r = 0; this.bytes.length % 4 != 0;) this.bytes += "="; for (r = 0; r < this.bytes.length; r += 4) t[0] = Base64.CHAR_MAP.indexOf(this.bytes.charAt(r)), t[1] = Base64.CHAR_MAP.indexOf(this.bytes.charAt(r + 1)), t[2] = Base64.CHAR_MAP.indexOf(this.bytes.charAt(r + 2)), t[3] = Base64.CHAR_MAP.indexOf(this.bytes.charAt(r + 3)), e[0] = t[0] << 2 | t[1] >> 4, e[1] = (15 & t[1]) << 4 | t[2] >> 2, e[2] = (3 & t[2]) << 6 | t[3], a[s++] = String.fromCharCode(e[0]), 64 != t[2] && (a[s++] = String.fromCharCode(e[1])), 64 != t[3] && (a[s++] = String.fromCharCode(e[2])); return a.join("") }; function searchSubtitles() { document.getElementById('subtitles').innerHTML = ''; document.getElementById('heads').innerHTML = ''; document.getElementById('error').innerHTML = ''; document.getElementById('headers').style.display = 'none'; // OS LogIn var loginRequest = new XmlRpcRequest("http://api.opensubtitles.org/xml-rpc", "LogIn"); loginRequest.params = (['', '', 'eng', 'FileBot']); var response1 = loginRequest.send(); var token = String(response1.parseXML().token); // OS SearchSubtitles var searchRequest = new XmlRpcRequest("http://api.opensubtitles.org/xml-rpc", "SearchSubtitles"); searchRequest.addParam(token); var imdbid = document.getElementById('imdbid').value || ''; var langcode = document.getElementById('langcode').value || 'all'; searchRequest.addParam([{imdbid: imdbid.replace('tt',''), season: '', episode: '', sublanguageid: langcode}]); // Parse response var results = searchRequest.send(); var xml = results.parseXML(); if (xml && xml.data) { for (var i = 0; i < xml.data.length; i++) { var downlink = xml.data[i].ZipDownloadLink; var filename = xml.data[i].SubFileName; document.getElementById('subtitles').innerHTML += "<a href="+downlink+">"+filename+"</a><br>"; } } else { console.debug('The xml object:', xml); } } function search() { try { searchSubtitles(); } catch (e) { document.getElementById('error').innerHTML = e.message || e; } } </script> </body> </html>

User avatar
oss
Site Admin
Posts: 5890
Joined: Sat Feb 25, 2006 11:26 pm
Contact: Website

Re: JS XmlRpcRequest

Sat Feb 27, 2016 5:41 pm

yeah, I didnt understood either. Thanks for looking on this. Maybe some CloudFlare problem, dont know. Strange

User avatar
vankasteelj
Posts: 175
Joined: Sun Nov 15, 2015 1:09 am

Re: JS XmlRpcRequest

Sat Feb 27, 2016 6:19 pm

No no, the issue isn't on OpenSubtitles or a CDN/cache, it's one of the following, on the user-side:
- synchronous XMLHttpRequest making the browser fail miserably
- hidden DOM can't be parsed because of context (this) issue
- new DOM is considered as a malicious popup
- Unfixed bug/issue in the browser core code

I can say that because there is no longer any Cross-Origin errors, the calls work on:
- Chrome/Chromium
- Firefox "incognito mode"
- Edge
- Node-Webkit

User avatar
oss
Site Admin
Posts: 5890
Joined: Sat Feb 25, 2006 11:26 pm
Contact: Website

Re: JS XmlRpcRequest

Sat Feb 27, 2016 6:28 pm

ok thanks for investigating this. If there is something how I can make this better (I inserted more CORS headers), let me know

Return to “Developing”

Who is online

Users browsing this forum: No registered users and 18 guests