@@ -2,25 +2,26 @@ use std::{borrow::Borrow, fmt::Display, str};
22
33use actix_multipart:: Multipart ;
44use actix_web:: {
5- cookie:: Cookie ,
5+ cookie:: { Cookie , CookieJar } ,
66 dev:: HttpServiceFactory ,
77 error:: ErrorInternalServerError ,
88 get,
9+ guard:: GuardContext ,
910 http:: {
1011 header:: { self , ContentDisposition , DispositionParam , DispositionType } ,
1112 StatusCode ,
1213 } ,
13- post,
14- web:: Data ,
14+ post, routes ,
15+ web:: { Bytes , Data , Payload } ,
1516 HttpResponse , Responder , ResponseError , Result ,
16- routes
1717} ;
1818use actix_web_lab:: extract:: Path ;
1919use askama:: Template ;
2020use askama_actix:: TemplateToResponse ;
2121use chrono:: { Duration , Utc } ;
2222use 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 } ;
2425use rand:: distributions:: { Alphanumeric , DistString } ;
2526use serde:: Deserialize ;
2627use syntect:: { html:: ClassedHTMLGenerator , parsing:: SyntaxSet , util:: LinesWithEndings } ;
@@ -44,6 +45,7 @@ pub fn scope() -> impl HttpServiceFactory {
4445 download,
4546 get_ext,
4647 post_form,
48+ post_raw,
4749 index,
4850 redir_down,
4951 )
@@ -358,16 +360,12 @@ impl ResponseError for UploadError {
358360 }
359361}
360362
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 > {
371369 let owner = if let Some ( owner) = cookies. get ( OWNER_COOKIE ) {
372370 owner. value ( ) . to_owned ( )
373371 } else {
@@ -381,9 +379,80 @@ async fn post_form(
381379 . await
382380 . map_err ( ErrorInternalServerError ) ?;
383381
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+
384455 while let Some ( mut field) = multipart. try_next ( ) . await ? {
385- const FILE_LIMIT : usize = 10_000_000 ;
386- let mut limit: usize = FILE_LIMIT ;
387456 match field. name ( ) {
388457 "data" => {
389458 if let Some ( file_name) = field. content_disposition ( ) . get_filename ( ) {
@@ -398,15 +467,7 @@ async fn post_form(
398467 }
399468 }
400469 }
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 ?) ;
410471 }
411472 "extension" => {
412473 let mut buf = String :: new ( ) ;
@@ -424,34 +485,15 @@ async fn post_form(
424485 }
425486 name => {
426487 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 ;
435488 return Err ( UploadError :: InvalidField ( name) . into ( ) ) ;
436489 }
437490 }
438491 }
439492
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+ ) )
457499}
0 commit comments