49 |
49 |
// a soundtrack in a zip file
|
50 |
50 |
// This is a static download, so it works without this script.
|
51 |
51 |
// https://app.box.com/s/vysdh2u78yih3c8leetgq82il954a3g3
|
52 |
|
// some gambling add
|
|
52 |
// some gambling ad
|
53 |
53 |
// pptx
|
54 |
54 |
// https://app.box.com/s/nnlplkmjhimau404qohh9my10pwmo8es
|
55 |
55 |
// a list of books(?)
|
56 |
56 |
// txt
|
57 |
57 |
// https://ucla.app.box.com/s/mv32q624ojihohzh8d0mhhj0b3xluzbz
|
58 |
58 |
// "COVID-19 Pivot Plan Decision Matrix"
|
59 |
|
// 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"
|
60 |
59 |
// If you load the proprietary scripts on this page, you'll see that there is no download button
|
61 |
60 |
// TODO: find a public folder link (the private links I have seem to work)
|
62 |
61 |
// TODO: find a (preferably public) link with a folder inside a folder, as these may need to be handled differently
|
... | ... | |
87 |
86 |
postStreamData = JSON.parse(match[1]);
|
88 |
87 |
}
|
89 |
88 |
|
90 |
|
// empty the initial document body
|
91 |
|
[...document.body.childNodes].forEach(n => n.remove());
|
|
89 |
// get domain from URL
|
|
90 |
const domain = document.location.href.split("/")[2];
|
92 |
91 |
|
93 |
|
// create div container
|
94 |
|
const divContainer = document.createElement("div");
|
95 |
|
document.body.append(divContainer);
|
|
92 |
/* Replace current page contents. */
|
|
93 |
const replacement_html = `\
|
|
94 |
<!DOCTYPE html>
|
|
95 |
<html>
|
|
96 |
<head>
|
|
97 |
<style>
|
|
98 |
button, .button {
|
|
99 |
border-radius: 10px;
|
|
100 |
padding: 20px;
|
|
101 |
color: #333;
|
|
102 |
background-color:
|
|
103 |
lightgreen;
|
|
104 |
text-decoration: none;
|
|
105 |
display: inline-block;
|
|
106 |
}
|
|
107 |
button:hover, .button:hover {
|
|
108 |
box-shadow: -4px 8px 8px #888;
|
|
109 |
}
|
96 |
110 |
|
97 |
|
const loadingIcon = document.createElement("h1");
|
98 |
|
loadingIcon.innerText = "loading...";
|
99 |
|
loadingIcon.style.display = "none";
|
|
111 |
.hide {
|
|
112 |
display: none;
|
|
113 |
}
|
100 |
114 |
|
101 |
|
const error_msg = document.createElement("h1");
|
102 |
|
error_msg.innerText = "error occured :(";
|
103 |
|
error_msg.style.display = "none";
|
|
115 |
#download_button .unofficial, #download_button .red_note {
|
|
116 |
display: none;
|
|
117 |
}
|
|
118 |
#download_button.unofficial .unofficial {
|
|
119 |
display: inline;
|
|
120 |
}
|
|
121 |
#download_button.unofficial .red_note {
|
|
122 |
display: block;
|
|
123 |
}
|
|
124 |
.red_note {
|
|
125 |
font-size: 75%;
|
|
126 |
color: #c55;
|
|
127 |
font-style: italic;
|
|
128 |
text-align: center;
|
|
129 |
}
|
|
130 |
</style>
|
|
131 |
</head>
|
|
132 |
<body>
|
|
133 |
<h1 id="loading" class="hide">loading...</h1>
|
|
134 |
<h1 id="error" class="hide">error occured :(</h1>
|
|
135 |
<h1 id="title" class="hide"></h1>
|
|
136 |
<div id="single_file_section" class="hide">
|
|
137 |
<a id="download_button" class="button">
|
|
138 |
<span class="unofficial">unofficial</span> download
|
|
139 |
<aside class="red_note">(officially disallowed)</aside>
|
|
140 |
</a>
|
|
141 |
<aside></aside>
|
|
142 |
<h2>File info</h2>
|
|
143 |
<div id="file_info"></div>
|
|
144 |
</div>
|
|
145 |
</body>
|
|
146 |
</html>
|
|
147 |
`;
|
104 |
148 |
|
105 |
|
divContainer.append(loadingIcon, error_msg);
|
|
149 |
/*
|
|
150 |
* We could instead use document.write(), but browser's debugging tools would
|
|
151 |
* not see the new page contents.
|
|
152 |
*/
|
|
153 |
const parser = new DOMParser();
|
|
154 |
const alt_doc = parser.parseFromString(replacement_html, "text/html");
|
|
155 |
document.documentElement.replaceWith(alt_doc.documentElement);
|
106 |
156 |
|
107 |
|
// get domain from URL
|
108 |
|
const domain = document.location.href.split("/")[2];
|
|
157 |
const nodes = {};
|
|
158 |
document.querySelectorAll('[id]').forEach(node => nodes[node.id] = node);
|
|
159 |
|
|
160 |
function show_error() {
|
|
161 |
nodes.loading.classList.add("hide");
|
|
162 |
nodes.error.classList.remove("hide");
|
|
163 |
}
|
|
164 |
|
|
165 |
function show_title(text) {
|
|
166 |
nodes.title.innerText = text;
|
|
167 |
nodes.loading.classList.add("hide");
|
|
168 |
nodes.title.classList.remove("hide");
|
|
169 |
}
|
109 |
170 |
|
110 |
171 |
async function hack_file() {
|
111 |
|
loadingIcon.style.display = "initial";
|
|
172 |
nodes.loading.classList.remove("hide");
|
112 |
173 |
|
113 |
174 |
const tokens_url = "/app-api/enduserapp/elements/tokens";
|
114 |
175 |
const file_nr = postStreamData["/app-api/enduserapp/shared-item"].itemID;
|
... | ... | |
140 |
201 |
const fields = [
|
141 |
202 |
"permissions", "shared_link", "sha1", "file_version", "name", "size",
|
142 |
203 |
"extension", "representations", "watermark_info",
|
143 |
|
"authenticated_download_url", "is_download_available"
|
|
204 |
"authenticated_download_url", "is_download_available",
|
|
205 |
"content_created_at", "content_modified_at", "created_at", "created_by",
|
|
206 |
"modified_at", "modified_by", "owned_by", "description",
|
|
207 |
"metadata.global.boxSkillsCards", "expires_at", "version_limit",
|
|
208 |
"version_number", "is_externally_owned", "restored_from",
|
|
209 |
"uploader_display_name"
|
144 |
210 |
];
|
145 |
211 |
|
146 |
212 |
const file_info_url =
|
... | ... | |
181 |
247 |
`${file_info.authenticated_download_url}?${params.toString()}`;
|
182 |
248 |
console.log("download_url", download_url);
|
183 |
249 |
|
184 |
|
const downloadButton = document.createElement("a");
|
185 |
|
downloadButton.innerText = "download";
|
186 |
|
downloadButton.href = download_url;
|
187 |
|
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;");
|
188 |
|
|
189 |
|
const file_info_header = document.createElement("h2");
|
190 |
|
file_info_header.innerText = "File info";
|
|
250 |
show_title(file_info.name);
|
191 |
251 |
|
192 |
|
divContainer.append(downloadButton, file_info_header,
|
193 |
|
JSON.stringify(file_info));
|
194 |
|
|
195 |
|
loadingIcon.style.display = "none";
|
196 |
|
}
|
197 |
|
|
198 |
|
function show_error() {
|
199 |
|
loadingIcon.style.display = "none";
|
200 |
|
error_msg.style.display = "initial";
|
|
252 |
nodes.download_button.href = download_url;
|
|
253 |
if (!file_info.permissions.can_download)
|
|
254 |
nodes.download_button.classList.add("unofficial");
|
|
255 |
nodes.file_info.innerText = JSON.stringify(file_info);
|
|
256 |
nodes.single_file_section.classList.remove("hide");
|
201 |
257 |
}
|
202 |
258 |
|
203 |
259 |
if (postStreamData["/app-api/enduserapp/shared-item"].itemType == "file") {
|
... | ... | |
207 |
263 |
*/
|
208 |
264 |
hack_file().then(() => {}, show_error);
|
209 |
265 |
} else if (postStreamData["/app-api/enduserapp/shared-item"].itemType == "folder") {
|
210 |
|
const folderHeader = document.createElement("h1");
|
211 |
|
folderHeader.innerText = postStreamData["/app-api/enduserapp/shared-folder"].currentFolderName;
|
212 |
|
divContainer.appendChild(folderHeader);
|
213 |
|
//console.log(postStreamData["/app-api/enduserapp/shared-folder"]);
|
214 |
|
postStreamData["/app-api/enduserapp/shared-folder"].items.forEach(function(element) {
|
215 |
|
console.log(element);
|
216 |
|
const folderButton = document.createElement("a");
|
217 |
|
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
|
218 |
|
if (element.type == "file") {
|
219 |
|
folderButton.innerText = "loading...";
|
220 |
|
// craft request
|
221 |
|
var downloadLinkGet = new XMLHttpRequest();
|
222 |
|
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);
|
223 |
|
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?
|
224 |
|
downloadLinkGet.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8");
|
225 |
|
downloadLinkGet.setRequestHeader("Accept-Language", "en-US,en;q=0.5"); // TODO: find a test case in another language
|
226 |
|
downloadLinkGet.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
227 |
|
downloadLinkGet.setRequestHeader("Upgrade-Insecure-Requests", "1");
|
228 |
|
downloadLinkGet.onreadystatechange = function() {
|
229 |
|
if (downloadLinkGet.readyState === 4) {
|
230 |
|
//console.log(downloadLinkGet.status);
|
231 |
|
// configure download button and add it
|
232 |
|
folderButton.setAttribute("href", downloadLinkGet.responseURL);
|
233 |
|
folderButton.innerText = element.name; // show the name of the file
|
234 |
|
}
|
235 |
|
};
|
236 |
|
downloadLinkGet.send("request_token="+config.requestToken);
|
237 |
|
} else {
|
238 |
|
folderButton.innerText = "[folders inside folders not yet supported]";
|
239 |
|
}
|
240 |
|
divContainer.appendChild(folderButton);
|
241 |
|
})
|
|
266 |
show_title(postStreamData["/app-api/enduserapp/shared-folder"].currentFolderName);
|
|
267 |
|
|
268 |
// TODO: implement a download folder button (included in proprietary app)
|
|
269 |
/*
|
|
270 |
The original download folder button sends a GET request that gets 2 URLs
|
|
271 |
in the response. 1 of those URLs downloads the file, and a POST request
|
|
272 |
is sent after (or maybe while in some cases?) a file is downloaded, to
|
|
273 |
let the server know how much is downloaded.
|
|
274 |
*/
|
|
275 |
// for each item in the folder, show a button with a link to download it
|
|
276 |
postStreamData["/app-api/enduserapp/shared-folder"].items.forEach(function(item) {
|
|
277 |
console.log("item", item);
|
|
278 |
|
|
279 |
const file_direct_url = "https://"+domain+"/index.php?rm=box_download_shared_file&shared_name="+postStreamData["/app-api/enduserapp/shared-item"].sharedName+"&file_id="+item.typedID;
|
|
280 |
|
|
281 |
const folderButton = nodes.download_button.cloneNode(false);
|
|
282 |
folderButton.removeAttribute("id");
|
|
283 |
|
|
284 |
if (item.type == "file") {
|
|
285 |
folderButton.href = file_direct_url;
|
|
286 |
folderButton.innerText = item.name; // show the name of the file
|
|
287 |
} else if (item.type == "folder") {
|
|
288 |
folderButton.innerText = "[folders inside folders not yet supported]";
|
|
289 |
} else {
|
|
290 |
folderButton.innerText = "[this item type is not supported]";
|
|
291 |
}
|
|
292 |
|
|
293 |
document.body.appendChild(folderButton);
|
|
294 |
});
|
242 |
295 |
} else {
|
243 |
|
console.log("Error: not implemented");
|
244 |
|
// TODO: also display an error on the page
|
|
296 |
console.log('expected "folder" or "file" as the item type (postStreamData["/app-api/enduserapp/shared-item"].itemType) but got ' + postStreamData["/app-api/enduserapp/shared-item"].itemType + ' instead; this item type is not implemented');
|
|
297 |
show_error();
|
245 |
298 |
}
|
Jacobk's and Koszko's improvements to app.box.com