Skip to content

Commit f3474fc

Browse files
committed
deaddrop: add users
Co-developed-by: Gemini 2.5 Pro
1 parent df713f4 commit f3474fc

File tree

2 files changed

+86
-111
lines changed

2 files changed

+86
-111
lines changed

plugins/deaddrop/assets/deaddrop.js

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,20 @@
180180
ts = d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' +
181181
pad(d.getDate()) + '_' + pad(d.getHours()) + '-' +
182182
pad(d.getMinutes()) + '-' + pad(d.getSeconds()),
183-
generated_filename = ts + (username ? '_' + username : '') + '.txt',
183+
generated_filename, // to be created below
184184
formData = new FormData(), blob;
185185

186186
e.preventDefault();
187+
188+
if (!username) { // Do not allow unauthenticated text uploads
189+
alert("You must be logged in to upload text.");
190+
return;
191+
}
187192
clear_errors();
188193

194+
// Filename now prefixed with username
195+
generated_filename = username + '_' + ts + '.txt';
196+
189197
blob = new Blob([content.value], { type: "text/plain" });
190198
formData.append("file", blob, generated_filename);
191199

@@ -292,24 +300,35 @@
292300

293301
s += "<table class=\"nb\">";
294302
for (n = 0; n < j.files.length; n++) {
303+
var fullName = j.files[n].name;
304+
var displayName = fullName;
305+
var isOwner = username &&
306+
fullName.startsWith(username + "_");
307+
308+
// Strip username prefix for display if owner
309+
if (isOwner)
310+
displayName = fullName.substring(
311+
username.length + 1);
312+
295313
var date = new Date(j.files[n].mtime * 1000);
296314
s += "<tr><td class=\"dow r\">" +
297315
humanize(j.files[n].size) +
298316
"</td><td class=\"dow\">" +
299317
date.toDateString() + " " +
300318
date.toLocaleTimeString() + "</td><td>";
301319

302-
if (username) /* any authenticated user can delete */
320+
// Only show delete button if authenticated and owner
321+
if (isOwner)
303322
s += "<img id=\"d" + n +
304323
"\" class=\"delbtn\" file=\"" +
305-
san(j.files[n].name) + "\">";
324+
san(fullName) + "\">";
306325
else
307326
s += " ";
308327

309328
s += "</td><td class=\"ogn\"><a href=\"get/" +
310-
lws_urlencode(san(j.files[n].name)) +
311-
"\" download>" +
312-
san(j.files[n].name) + "</a></td></tr>";
329+
lws_urlencode(san(fullName)) +
330+
"\" download=\"" + san(displayName) + "\">" +
331+
san(displayName) + "</a></td></tr>";
313332
}
314333
s += "</table>";
315334

plugins/deaddrop/protocol_lws_deaddrop.c

Lines changed: 61 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -135,102 +135,65 @@ start_sending_dir(struct pss_deaddrop *pss)
135135
static int
136136
scan_upload_dir(struct vhd_deaddrop *vhd)
137137
{
138-
char filepath[256], subdir[3][128], *p;
138+
char filepath[512], *p_owner_end;
139139
struct lwsac *lwsac_head = NULL;
140140
lws_list_ptr sorted_head = NULL;
141141
struct dir_entry *dire;
142142
struct dirent *de;
143-
size_t initial, m;
144-
int i, sp = 0;
143+
size_t m;
145144
struct stat s;
146-
DIR *dir[3];
145+
DIR *dir;
147146

148-
initial = strlen(vhd->upload_dir) + 1;
149-
lws_strncpy(subdir[sp], vhd->upload_dir, sizeof(subdir[sp]));
150-
dir[sp] = opendir(vhd->upload_dir);
151-
if (!dir[sp]) {
147+
dir = opendir(vhd->upload_dir);
148+
if (!dir) {
152149
lwsl_err("%s: Unable to walk upload dir '%s'\n", __func__,
153150
vhd->upload_dir);
154151
return -1;
155152
}
156153

157-
do {
158-
de = readdir(dir[sp]);
159-
if (!de) {
160-
closedir(dir[sp]);
161-
#if !defined(__COVERITY__)
162-
if (!sp)
163-
#endif
164-
break;
165-
#if !defined(__COVERITY__)
166-
sp--;
154+
while ((de = readdir(dir))) {
155+
/* ignore temp files */
156+
if (de->d_name[strlen(de->d_name) - 1] == '~' ||
157+
!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
167158
continue;
168-
#endif
169-
}
170159

171-
p = filepath;
160+
lws_snprintf(filepath, sizeof(filepath), "%s/%s",
161+
vhd->upload_dir, de->d_name);
172162

173-
for (i = 0; i <= sp; i++)
174-
p += lws_snprintf(p, lws_ptr_diff_size_t((filepath + sizeof(filepath)), p),
175-
"%s/", subdir[i]);
176-
177-
lws_snprintf(p, lws_ptr_diff_size_t((filepath + sizeof(filepath)), p), "%s",
178-
de->d_name);
179-
180-
/* ignore temp files */
181-
if (de->d_name[strlen(de->d_name) - 1] == '~')
182-
continue;
183-
#if defined(__COVERITY__)
184-
s.st_size = 0;
185-
s.st_mtime = 0;
186-
#else
187-
/* coverity[toctou] */
188163
if (stat(filepath, &s))
189164
continue;
190165

191-
if (S_ISDIR(s.st_mode)) {
192-
if (!strcmp(de->d_name, ".") ||
193-
!strcmp(de->d_name, ".."))
194-
continue;
195-
sp++;
196-
if (sp == LWS_ARRAY_SIZE(dir)) {
197-
lwsl_err("%s: Skipping too-deep subdir %s\n",
198-
__func__, filepath);
199-
sp--;
200-
continue;
201-
}
202-
lws_strncpy(subdir[sp], de->d_name, sizeof(subdir[sp]));
203-
dir[sp] = opendir(filepath);
204-
if (!dir[sp]) {
205-
lwsl_err("%s: Unable to open subdir '%s'\n",
206-
__func__, filepath);
207-
goto bail;
208-
}
166+
if (S_ISDIR(s.st_mode))
209167
continue;
210-
}
211-
#endif
212168

213-
m = strlen(filepath + initial) + 1;
169+
m = strlen(de->d_name) + 1;
214170
dire = lwsac_use(&lwsac_head, sizeof(*dire) + m, 0);
215171
if (!dire) {
216172
lwsac_free(&lwsac_head);
217-
218-
goto bail;
173+
closedir(dir);
174+
return -1;
219175
}
220176

221177
dire->next = NULL;
222178
dire->size = (unsigned long long)s.st_size;
223179
dire->mtime = s.st_mtime;
224180
dire->user[0] = '\0';
225-
#if !defined(__COVERITY__)
226-
if (sp)
227-
lws_strncpy(dire->user, subdir[1], sizeof(dire->user));
228-
#endif
229181

230-
memcpy(&dire[1], filepath + initial, m);
182+
p_owner_end = strchr(de->d_name, '_');
183+
if (p_owner_end) {
184+
size_t owner_len = (size_t)(p_owner_end - de->d_name);
185+
if (owner_len < sizeof(dire->user)) {
186+
memcpy(dire->user, de->d_name, owner_len);
187+
dire->user[owner_len] = '\0';
188+
}
189+
}
190+
191+
memcpy(&dire[1], de->d_name, m);
231192

232193
lws_list_ptr_insert(&sorted_head, &dire->next, de_mtime_sort);
233-
} while (1);
194+
}
195+
196+
closedir(dir);
234197

235198
/* the old lwsac continues to live while someone else is consuming it */
236199
if (vhd->lwsac_head)
@@ -251,12 +214,6 @@ scan_upload_dir(struct vhd_deaddrop *vhd)
251214
} lws_end_foreach_llp(ppss, pss_list);
252215

253216
return 0;
254-
255-
bail:
256-
while (sp >= 0)
257-
closedir(dir[sp--]);
258-
259-
return -1;
260217
}
261218

262219
static int
@@ -272,24 +229,22 @@ file_upload_cb(void *data, const char *name, const char *filename,
272229

273230
switch (state) {
274231
case LWS_UFS_OPEN:
232+
/* Require an authenticated user to upload */
233+
if (!pss->user[0]) {
234+
pss->response_code = HTTP_STATUS_FORBIDDEN;
235+
lwsl_warn("%s: unauthenticated upload forbidden\n",
236+
__func__);
237+
return -1;
238+
}
239+
275240
lws_urldecode(filename2, filename, sizeof(filename2) - 1);
276241
lws_filename_purify_inplace(filename2);
277-
if (pss->user[0]) {
278-
lws_filename_purify_inplace(pss->user);
279-
lws_snprintf(pss->filename, sizeof(pss->filename),
280-
"%s/%s", pss->vhd->upload_dir, pss->user);
281-
if (mkdir(pss->filename
282-
#if !defined(WIN32)
283-
, 0700
284-
#endif
285-
) < 0)
286-
lwsl_debug("%s: mkdir failed\n", __func__);
287-
lws_snprintf(pss->filename, sizeof(pss->filename),
288-
"%s/%s/%s~", pss->vhd->upload_dir,
289-
pss->user, filename2);
290-
} else
291-
lws_snprintf(pss->filename, sizeof(pss->filename),
292-
"%s/%s~", pss->vhd->upload_dir, filename2);
242+
lws_filename_purify_inplace(pss->user);
243+
244+
/* New filename format: upload_dir/user_originalfilename~ */
245+
lws_snprintf(pss->filename, sizeof(pss->filename),
246+
"%s/%s_%s~", pss->vhd->upload_dir,
247+
pss->user, filename2);
293248
lwsl_notice("%s: filename '%s'\n", __func__, pss->filename);
294249

295250
pss->fd = (lws_filefd_type)(long long)lws_open(pss->filename,
@@ -464,30 +419,31 @@ callback_deaddrop(struct lws *wsi, enum lws_callback_reasons reason,
464419
return 0;
465420

466421
case LWS_CALLBACK_RECEIVE:
467-
/* we get this kind of thing {"del":"agreen/no-entry.svg"} */
422+
/* we get this kind of thing {"del":"user_agreen.txt"} */
468423
if (!pss || len < 10)
469424
break;
470425

471426
if (strncmp((const char *)in, "{\"del\":\"", 8))
472427
break;
473428

474-
/*
475-
* NOTE: any authenticated user can delete any file.
476-
* To restrict to owner, uncomment the following check.
477-
*/
478-
// cp = strchr((const char *)in, '/');
479-
// if (cp) {
480-
// n = (int)(((uint8_t *)cp - (uint8_t *)in)) - 8;
481-
//
482-
// if ((int)strlen(pss->user) != n ||
483-
// memcmp(pss->user, ((const char *)in) + 8, (unsigned int)n)) {
484-
// lwsl_notice("%s: del: auth mismatch "
485-
// " '%s' '%s' (%d)\n",
486-
// __func__, pss->user,
487-
// ((const char *)in) + 8, n);
488-
// break;
489-
// }
490-
// }
429+
cp = strchr((const char *)in + 8, '_');
430+
if (!cp) {
431+
lwsl_warn("%s: del: no owner in filename\n", __func__);
432+
break;
433+
}
434+
435+
/* Check if the authenticated user matches the file owner prefix */
436+
n = (int)(cp - (((const char *)in) + 8));
437+
438+
if ((int)strlen(pss->user) != n ||
439+
strncmp(pss->user, ((const char *)in) + 8, (unsigned int)n)) {
440+
lwsl_notice("%s: del: auth mismatch "
441+
" user '%s' tried to delete file with "
442+
"owner '%.*s'\n",
443+
__func__, pss->user, n,
444+
((const char *)in) + 8);
445+
break;
446+
}
491447

492448
lws_strncpy(fname, ((const char *)in) + 8, sizeof(fname));
493449
wp = strchr((const char *)fname, '\"');

0 commit comments

Comments
 (0)