@@ -2,25 +2,26 @@ use std::{borrow::Borrow, fmt::Display, str};
2
2
3
3
use actix_multipart:: Multipart ;
4
4
use actix_web:: {
5
- cookie:: Cookie ,
5
+ cookie:: { Cookie , CookieJar } ,
6
6
dev:: HttpServiceFactory ,
7
7
error:: ErrorInternalServerError ,
8
8
get,
9
+ guard:: GuardContext ,
9
10
http:: {
10
11
header:: { self , ContentDisposition , DispositionParam , DispositionType } ,
11
12
StatusCode ,
12
13
} ,
13
- post,
14
- web:: Data ,
14
+ post, routes ,
15
+ web:: { Bytes , Data , Payload } ,
15
16
HttpResponse , Responder , ResponseError , Result ,
16
- routes
17
17
} ;
18
18
use actix_web_lab:: extract:: Path ;
19
19
use askama:: Template ;
20
20
use askama_actix:: TemplateToResponse ;
21
21
use chrono:: { Duration , Utc } ;
22
22
use futures:: { future:: ready, StreamExt , TryStreamExt } ;
23
- use mime_guess:: mime:: { APPLICATION_OCTET_STREAM , IMAGE } ;
23
+ use futures_util:: Stream ;
24
+ use mime_guess:: mime:: { self , APPLICATION_OCTET_STREAM , IMAGE } ;
24
25
use rand:: distributions:: { Alphanumeric , DistString } ;
25
26
use serde:: Deserialize ;
26
27
use syntect:: { html:: ClassedHTMLGenerator , parsing:: SyntaxSet , util:: LinesWithEndings } ;
@@ -44,6 +45,7 @@ pub fn scope() -> impl HttpServiceFactory {
44
45
download,
45
46
get_ext,
46
47
post_form,
48
+ post_raw,
47
49
index,
48
50
redir_down,
49
51
)
@@ -358,16 +360,12 @@ impl ResponseError for UploadError {
358
360
}
359
361
}
360
362
361
- #[ post( "/" ) ]
362
- async fn post_form (
363
- payload : Multipart ,
364
- database : Data < DB > ,
365
- Cookies ( mut cookies) : Cookies ,
366
- config : Data < Config > ,
367
- ) -> Result < impl Responder > {
368
- let mut multipart = payload;
369
- let mut extension = None ;
370
-
363
+ async fn create_file < E : ResponseError + ' static > (
364
+ mut data : impl Stream < Item = Result < Bytes , E > > + Unpin ,
365
+ database : & Data < DB > ,
366
+ cookies : & mut CookieJar ,
367
+ config : & Data < Config > ,
368
+ ) -> Result < String > {
371
369
let owner = if let Some ( owner) = cookies. get ( OWNER_COOKIE ) {
372
370
owner. value ( ) . to_owned ( )
373
371
} else {
@@ -381,9 +379,80 @@ async fn post_form(
381
379
. await
382
380
. map_err ( ErrorInternalServerError ) ?;
383
381
382
+ const FILE_LIMIT : usize = 10_000_000 ;
383
+ let mut limit: usize = FILE_LIMIT ;
384
+ while let Some ( data) = data. try_next ( ) . await ? {
385
+ if let Some ( l) = limit. checked_sub ( data. len ( ) ) {
386
+ limit = l;
387
+ } else {
388
+ return Err ( UploadError :: FieldTooBig ( "file" , FILE_LIMIT ) . into ( ) ) ;
389
+ }
390
+ file. append ( & data) . await . map_err ( ErrorInternalServerError ) ?;
391
+ }
392
+
393
+ if file
394
+ . contents ( )
395
+ . await
396
+ . map_err ( ErrorInternalServerError ) ?
397
+ . is_empty ( )
398
+ {
399
+ file. delete ( ) . await . map_err ( ErrorInternalServerError ) ?;
400
+ return Err ( UploadError :: NoData . into ( ) ) ;
401
+ }
402
+
403
+ Ok ( file. name ( ) . to_string ( ) )
404
+ }
405
+
406
+ fn response (
407
+ name : String ,
408
+ cookies : CookieJar ,
409
+ extension : Option < String > ,
410
+ config : & Data < Config > ,
411
+ ) -> impl Responder {
412
+ let name = name. clone ( ) + & extension. map ( |e| format ! ( ".{e}" ) ) . unwrap_or_default ( ) ;
413
+ HttpResponse :: Found ( )
414
+ . append_header ( ( header:: LOCATION , name. clone ( ) ) )
415
+ . cookie_delta ( & cookies)
416
+ . body ( format ! (
417
+ "{}{name}" ,
418
+ if !config. base_url. is_empty( ) && !config. base_url. ends_with( '/' ) {
419
+ format!( "{}{}" , config. base_url, "/" )
420
+ } else {
421
+ format!( "{}" , config. base_url)
422
+ }
423
+ ) )
424
+ }
425
+
426
+ #[ post( "/" ) ]
427
+ async fn post_raw (
428
+ payload : Payload ,
429
+ database : Data < DB > ,
430
+ Cookies ( mut cookies) : Cookies ,
431
+ config : Data < Config > ,
432
+ ) -> Result < impl Responder > {
433
+ create_file ( payload, & database, & mut cookies, & config)
434
+ . await
435
+ . map ( |it| response ( it, cookies, None , & config) )
436
+ }
437
+
438
+ fn is_form ( it : & GuardContext ) -> bool {
439
+ it. header :: < header:: ContentType > ( ) . map_or ( false , |it| {
440
+ it. 0 . type_ ( ) == mime:: MULTIPART && it. 0 . subtype ( ) == mime:: FORM_DATA
441
+ } )
442
+ }
443
+
444
+ #[ post( "/" , guard = "is_form" ) ]
445
+ async fn post_form (
446
+ payload : Multipart ,
447
+ database : Data < DB > ,
448
+ Cookies ( mut cookies) : Cookies ,
449
+ config : Data < Config > ,
450
+ ) -> Result < impl Responder > {
451
+ let mut multipart = payload;
452
+ let mut extension = None ;
453
+ let mut file = None ;
454
+
384
455
while let Some ( mut field) = multipart. try_next ( ) . await ? {
385
- const FILE_LIMIT : usize = 10_000_000 ;
386
- let mut limit: usize = FILE_LIMIT ;
387
456
match field. name ( ) {
388
457
"data" => {
389
458
if let Some ( file_name) = field. content_disposition ( ) . get_filename ( ) {
@@ -398,15 +467,7 @@ async fn post_form(
398
467
}
399
468
}
400
469
}
401
- while let Some ( data) = field. try_next ( ) . await ? {
402
- if let Some ( l) = limit. checked_sub ( data. len ( ) ) {
403
- limit = l;
404
- } else {
405
- field. for_each ( |_| ready ( ( ) ) ) . await ;
406
- return Err ( UploadError :: FieldTooBig ( "file" , FILE_LIMIT ) . into ( ) ) ;
407
- }
408
- file. append ( & data) . await . map_err ( ErrorInternalServerError ) ?;
409
- }
470
+ file = Some ( create_file ( field, & database, & mut cookies, & config) . await ?) ;
410
471
}
411
472
"extension" => {
412
473
let mut buf = String :: new ( ) ;
@@ -424,34 +485,15 @@ async fn post_form(
424
485
}
425
486
name => {
426
487
let name = name. to_string ( ) ;
427
- field. for_each ( |_| ready ( ( ) ) ) . await ;
428
- multipart
429
- . for_each ( |field| async {
430
- if let Ok ( field) = field {
431
- field. for_each ( |_| ready ( ( ) ) ) . await ;
432
- }
433
- } )
434
- . await ;
435
488
return Err ( UploadError :: InvalidField ( name) . into ( ) ) ;
436
489
}
437
490
}
438
491
}
439
492
440
- if file
441
- . contents ( )
442
- . await
443
- . map_err ( ErrorInternalServerError ) ?
444
- . is_empty ( )
445
- {
446
- file. delete ( ) . await . map_err ( ErrorInternalServerError ) ?;
447
- return Err ( UploadError :: NoData . into ( ) ) ;
448
- }
449
-
450
- Ok ( HttpResponse :: Found ( )
451
- . append_header ( (
452
- header:: LOCATION ,
453
- file. name ( ) . to_string ( ) + & extension. map ( |e| format ! ( ".{e}" ) ) . unwrap_or_default ( ) ,
454
- ) )
455
- . cookie_delta ( & cookies)
456
- . finish ( ) )
493
+ Ok ( response (
494
+ file. ok_or_else ( || UploadError :: NoData ) ?,
495
+ cookies,
496
+ extension,
497
+ & config
498
+ ) )
457
499
}
0 commit comments