Project

General

Profile

« Previous | Next » 

Revision 9ceab682

Added by koszko over 1 year ago

Jacobk's and Koszko's improvements to app.box.com

View differences:

content/sbox/box-fix.js
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
}
content/sbox/index.json
1 1
{
2 2
"type" : "script",
3 3
"name" : "box-com-downloader",
4
"sha256" : "1274d2a8416b79acca117d90a3473c7affbb7d19dedc9004fffeca2f82283512",
4
"sha256" : "18d9d75f9a122c1cd3d92011699d0f292e97bf60b79169fa19fd240eb3043368",
5 5
"location" : "box-fix.js"
6 6
}

Also available in: Unified diff