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
|
}
|