Project

General

Profile

Site script request/donation #101 » box-fix.js

jacobk, 01/19/2022 02:30 AM

 
1
/**
2
 * Copyright 2022 Jacob K
3
 *
4
 * This program is free software; you can redistribute it
5
 * and/or modify it under the terms of either:
6
 * - the GNU General Public License as published by the Free
7
 *   Software Foundation; either version 3 of the License, or (at
8
 *   your option) any later version, or
9
 * - the "A" license: <https://koszko.org/alicense.txt>; explained
10
 *   at: <https://koszko.org/en/articles/my-new-license.html>
11
 *
12
 * As additional permission under GNU GPL version 3 section 7, you
13
 * may distribute forms of that code without the copy of the GNU
14
 * GPL normally required by section 4, provided you include this
15
 * license notice and, in case of non-source distribution, a URL
16
 * through which recipients can access the Corresponding Source.
17
 * If you modify file(s) with this exception, you may extend this
18
 * exception to your version of the file(s), but you are not
19
 * obligated to do so. If you do not wish to do so, delete this
20
 * exception statement from your version. If you delete this
21
 * exception statement from all source files in the program, then
22
 * also delete it here.
23
 *
24
 * As a special exception to the GPL, any HTML file which merely
25
 * makes function calls to this code, and for that purpose
26
 * includes it by reference shall be deemed a separate work for
27
 * copyright law purposes. If you modify this code, you may extend
28
 * this exception to your version of the code, but you are not
29
 * obligated to do so. If you do not wish to do so, delete this
30
 * exception statement from your version.
31
 */
32
 // above license statement copied from here: https://api-demo.hachette-hydrilla.org/content/sgoogle_sheets_download/google_sheets_download.js
33

    
34
// meta: match should be https://***.app.box.com/s/* I think (*** instead of * for the first section because otherwise plain app.box.com URLs won't work)
35
// meta: some test cases (mostly found at https://old.reddit.com/search?q="box.com"&include_over_18=on&sort=new)
36
	// https://uwmadison.app.box.com/s/ydht2incbdmw1lhpjg5t40adguc0fm14
37
		// umadison's enrollment report
38
		// pdf
39
	// https://app.box.com/s/gc4ygloi4qtimeh98dq9mmydyuydawcn
40
		// password-protected 7z file (nsfw)
41
	// https://app.box.com/shared/static/su6xx6zx50cd68zdtbm3wfxhh9kwke8x.zip
42
		// a soundtrack in a zip file
43
		// This is a static download, so it works without this script.
44
	// https://app.box.com/s/vysdh2u78yih3c8leetgq82il954a3g3
45
		// some gambling add
46
		// pptx
47
	// https://app.box.com/s/nnlplkmjhimau404qohh9my10pwmo8es
48
		// a list of books(?)
49
		// txt
50
	// https://ucla.app.box.com/s/mv32q624ojihohzh8d0mhhj0b3xluzbz
51
		// "COVID-19 Pivot Plan Decision Matrix"
52
		// cannot be downloaded (403 Forbidden): "This user is not allowed to use direct links. Please email "[support address, changes depending on where you are logged in]" for support"
53
		// If you load the proprietary scripts on this page, you'll see that there is no download button, so the default script probably should not include one either (a functional download button could be part of an enhancement script, but the user should probably know that the uploader or an organization doesn't want them to save the file permanently)
54
		// TODO: implement file previews so URLs like this one can be viewed, and hide the download button as it'll be broken anyway
55
	// TODO: find a public folder link (the private links I have seem to work)
56
	// TODO: find a (preferably public) link with a folder inside a folder, as these may need to be handled differently
57

    
58
/* Extract data from a script that sets multiple variables. */ // from here: https://api-demo.hachette-hydrilla.org/content/sgoogle_sheets_download/google_sheets_download.js
59

    
60
let prefetchedData = null; // This variable isn't actually used.
61
for (const script of document.scripts) {
62
    const match = /Box.prefetchedData = ({([^;]|[^}];)+})/.exec(script.textContent); // looks for "Box.prefetchedData = " in the script files and then grabs the json text after that.
63
    if (!match)
64
	continue;
65
    prefetchedData = JSON.parse(match[1]);
66
}
67

    
68
let config = null;
69
for (const script of document.scripts) {
70
    const match = /Box.config = ({([^;]|[^}];)+})/.exec(script.textContent); // looks for "Box.config = " in the script files and then grabs the json text after that.
71
    if (!match)
72
	continue;
73
    config = JSON.parse(match[1]);
74
}
75

    
76
let postStreamData = null;
77
for (const script of document.scripts) {
78
    const match = /Box.postStreamData = ({([^;]|[^}];)+})/.exec(script.textContent); // looks for "Box.postStreamData = " in the script files and then grabs the json text after that.
79
    if (!match)
80
	continue;
81
    postStreamData = JSON.parse(match[1]);
82
}
83

    
84
// create div container
85
const divContainer = document.createElement("div");
86
document.body.insertBefore(divContainer, document.body.firstElementChild);
87

    
88
// get domain from URL
89
const domain = document.location.href.split("/")[2]; 
90

    
91
if (postStreamData["/app-api/enduserapp/shared-item"].itemType == "file") {
92
	// craft the request
93

    
94
	var downloadLinkGet = new XMLHttpRequest();
95
	downloadLinkGet.open("POST", "https://"+domain+"/index.php?rm=box_download_shared_file&shared_name="+postStreamData["/app-api/enduserapp/shared-item"].sharedName+"&file_id=f_"+postStreamData["/app-api/enduserapp/shared-item"].itemID);
96
	//downloadLinkGet.setRequestHeader("Host", "uwmadison.app.box.com"); // forbidden
97
	downloadLinkGet.setRequestHeader("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0"); // Would this be set automatically otherwise?
98
	downloadLinkGet.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8");
99
	downloadLinkGet.setRequestHeader("Accept-Language",  "en-US,en;q=0.5"); // TODO: find a test case in another language
100
	//downloadLinkGet.setRequestHeader("Accept-Encoding", "gzip, deflate, br"); // forbidden
101
	//downloadLinkGet.setRequestHeader("Referer", "https://uwmadison.app.box.com/s/ydht2incbdmw1lhpjg5t40adguc0fm14"); // forbidden
102
	downloadLinkGet.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
103
	//downloadLinkGet.setRequestHeader("Content-Length", "78"); // forbidden
104
	//downloadLinkGet.setRequestHeader("Origin", "https://uwmadison.app.box.com"); // forbidden
105
	//downloadLinkGet.setRequestHeader("DNT", "1"); // forbidden
106
	//downloadLinkGet.setRequestHeader("Connection", "keep-alive"); // forbidden
107
	//downloadLinkGet.setRequestHeader("Cookie", "[REDACTED (not sure if cookies contain login details)]"); // forbidden
108
	downloadLinkGet.setRequestHeader("Upgrade-Insecure-Requests", "1");
109
	//downloadLinkGet.setRequestHeader("Sec-Fetch-Dest", "iframe"); // forbidden
110
	//downloadLinkGet.setRequestHeader("Sec-Fetch-Mode", "navigate"); // forbidden
111
	//downloadLinkGet.setRequestHeader("Sec-Fetch-Site", "same-origin"); // forbidden
112
	//downloadLinkGet.setRequestHeader("Sec-Fetch-User", "?1"); // forbidden
113
	//downloadLinkGet.setRequestHeader("TE", "trailers"); // forbidden
114

    
115
	// create download button
116
	const downloadButton = document.createElement("a");
117
	downloadButton.innerText = "download";
118
	downloadButton.setAttribute("style", "border-radius: 10px; padding: 20px; color: #333; background-color: lightgreen; text-decoration: none; box-shadow: -4px 8px 8px #888; display: inline-block;"); // from https://api-demo.hachette-hydrilla.org/content/sgoogle_sheets_download/google_sheets_download.js
119

    
120
	// create loading icon
121
	const loadingIcon = document.createElement("h1");
122
	loadingIcon.innerText = "loading...";
123
	divContainer.appendChild(loadingIcon);
124

    
125
	downloadLinkGet.onreadystatechange = function() {
126
		if (downloadLinkGet.readyState === 4) {
127
			//console.log(downloadLinkGet.status);
128
			// configure download button and add it
129
			downloadButton.setAttribute("href", downloadLinkGet.responseURL);
130
			loadingIcon.remove(); // remove the loading icon, as we are pretty much done loading
131
			divContainer.appendChild(downloadButton);
132
		}
133
	};
134

    
135
	// send the request
136
	downloadLinkGet.send("request_token="+config.requestToken); // TODO: figure out a way to stop the initial request from following the location it gets and automatically downloading the file ahead of time, and then decicde if that method should be used instead (the current method might download the file twice (once when the page loads, and once when the user clicks), but maybe caching makes that irrelevent
137
} else if (postStreamData["/app-api/enduserapp/shared-item"].itemType == "folder") {
138
	const folderHeader = document.createElement("h1");
139
	folderHeader.innerText = postStreamData["/app-api/enduserapp/shared-folder"].currentFolderName;
140
	divContainer.appendChild(folderHeader);
141
	//console.log(postStreamData["/app-api/enduserapp/shared-folder"]);
142
	postStreamData["/app-api/enduserapp/shared-folder"].items.forEach(function(element) {
143
		console.log(element);
144
		const folderButton = document.createElement("a");
145
		folderButton.setAttribute("style", "border-radius: 10px; padding: 20px; color: #333; background-color: lightgreen; text-decoration: none; box-shadow: -4px 8px 8px #888; display: inline-block;"); // from https://api-demo.hachette-hydrilla.org/content/sgoogle_sheets_download/google_sheets_download.js
146
		if (element.type == "file") {
147
			folderButton.innerText = "loading...";
148
			// craft request
149
			var downloadLinkGet = new XMLHttpRequest();
150
			downloadLinkGet.open("POST", "https://"+domain+"/index.php?rm=box_download_shared_file&shared_name="+postStreamData["/app-api/enduserapp/shared-item"].sharedName+"&file_id="+element.typedID);
151
			downloadLinkGet.setRequestHeader("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0"); // Would this be set automatically otherwise?
152
			downloadLinkGet.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8");
153
			downloadLinkGet.setRequestHeader("Accept-Language",  "en-US,en;q=0.5"); // TODO: find a test case in another language
154
			downloadLinkGet.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
155
			downloadLinkGet.setRequestHeader("Upgrade-Insecure-Requests", "1");
156
			downloadLinkGet.onreadystatechange = function() {
157
				if (downloadLinkGet.readyState === 4) {
158
					//console.log(downloadLinkGet.status);
159
					// configure download button and add it
160
					folderButton.setAttribute("href", downloadLinkGet.responseURL);
161
					folderButton.innerText = element.name; // show the name of the file
162
				}
163
			};
164
			downloadLinkGet.send("request_token="+config.requestToken);
165
		} else {
166
			folderButton.innerText = "[folders inside folders not yet supported]";
167
		}
168
		divContainer.appendChild(folderButton);
169
	})
170
} else {
171
	console.log("Error: not implemented");
172
	// TODO: also display an error on the page
173
}
(1-1/3)