2019-02-16 23:47:09 -05:00
#![ feature(proc_macro_hygiene, decl_macro) ]
2018-08-07 23:23:15 -04:00
mod paste_id ;
2019-02-16 23:47:09 -05:00
//mod mpu;
2018-08-07 23:23:15 -04:00
#[ cfg(test) ] mod tests ;
use std ::io ;
2018-08-16 22:25:53 -04:00
use std ::fs ::{ self , File } ;
2018-08-07 23:23:15 -04:00
use std ::path ::{ Path , PathBuf } ;
2018-08-16 22:25:53 -04:00
use std ::thread ;
2018-08-16 23:04:57 -04:00
use std ::net ::{ TcpListener , TcpStream } ;
2018-08-16 22:25:53 -04:00
use std ::io ::{ Write , Read } ;
use std ::time ::{ Duration , SystemTime } ;
2018-08-07 23:23:15 -04:00
2019-02-17 00:17:32 -05:00
use serde ::{ Deserialize , Serialize } ;
2018-08-07 23:23:15 -04:00
2019-02-17 00:17:32 -05:00
use rocket ::{ post , put , get , patch , delete , FromForm , routes } ;
use rocket ::response ::NamedFile ;
2018-08-07 23:23:15 -04:00
use rocket ::Data ;
use rocket ::response ::content ;
2018-08-16 22:25:53 -04:00
use rocket ::request ::{ self , Request , FromRequest , State , LenientForm } ;
use rocket ::outcome ::Outcome ::* ;
2018-08-07 23:23:15 -04:00
use paste_id ::PasteID ;
2019-02-16 23:47:09 -05:00
//use mpu::MultipartUpload;
2018-08-07 23:23:15 -04:00
const HOST : & 'static str = " http://localhost:8000 " ;
2018-08-16 23:33:37 -04:00
type Result < T > = ::std ::result ::Result < T , Error > ;
2018-08-07 23:23:15 -04:00
2018-08-16 22:25:53 -04:00
#[ derive(Debug) ]
pub enum Error {
Io ( io ::Error ) ,
Toml ( toml ::ser ::Error ) ,
TomlDe ( toml ::de ::Error ) ,
/// The uinput file could not be found.
NotFound ,
/// error reading input_event
ShortRead ,
}
impl From < io ::Error > for Error {
fn from ( value : io ::Error ) -> Self {
Error ::Io ( value )
}
}
impl From < toml ::ser ::Error > for Error {
fn from ( value : toml ::ser ::Error ) -> Self {
Error ::Toml ( value )
}
}
impl From < toml ::de ::Error > for Error {
fn from ( value : toml ::de ::Error ) -> Self {
Error ::TomlDe ( value )
}
}
2018-08-16 23:33:37 -04:00
#[ derive(Deserialize, Serialize, Debug) ]
2018-08-16 22:25:53 -04:00
struct PasteInfo < ' a > {
// these never change
key : Option < PasteID < ' a > > , // key to update/delete paste with
delete_after : Option < SystemTime > , // delete after this date regardless
delete_after_num_views : Option < u32 > , // delete after this many views
delete_if_not_viewed_in_last_seconds : Option < Duration > , // delete if last_viewed is longer than this many seconds ago
// these are updated if the above values require it
last_viewed : Option < SystemTime > , // Only Some if delete_if_not_viewed_in_last_seconds is Some
num_views : u32 , // Only incremented if delete_after_num_views is Some, otherwise 0
2018-08-07 23:23:15 -04:00
}
2018-08-16 23:33:37 -04:00
impl < ' a > Default for PasteInfo < ' a > {
fn default ( ) -> Self {
PasteInfo {
key : None ,
delete_after : Some ( SystemTime ::now ( ) + Duration ::from_secs ( 2592000 ) ) , // default to 30 days
delete_after_num_views :None ,
delete_if_not_viewed_in_last_seconds : None ,
last_viewed : None ,
num_views : 0 ,
}
}
}
2018-08-16 22:25:53 -04:00
impl < ' a > PasteInfo < ' a > {
2018-08-16 23:33:37 -04:00
fn read < P : AsRef < Path > > ( path : P ) -> Result < PasteInfo < 'static > > {
2018-08-16 22:25:53 -04:00
let mut f = File ::open ( path ) ? ;
let mut input = String ::new ( ) ;
f . read_to_string ( & mut input ) ? ;
let paste_info = toml ::from_str ( & input ) ? ;
Ok ( paste_info )
}
2018-08-16 23:33:37 -04:00
fn write < P : AsRef < Path > > ( & self , path : P ) -> Result < ( ) > {
2018-08-16 22:25:53 -04:00
let toml = toml ::to_string ( & self ) ? ;
fs ::write ( path , toml ) ? ;
Ok ( ( ) )
}
2018-08-07 23:23:15 -04:00
2018-08-16 22:25:53 -04:00
fn should_delete ( & self ) -> bool {
self . delete_after . map ( | s | s > = SystemTime ::now ( ) ) . unwrap_or ( false )
| | self . delete_after_num_views . map ( | n | n > = self . num_views ) . unwrap_or ( false )
// self.last_viewed.unwrap() is safe because this is always Some if delete_if_not_viewed_in_last_seconds is
| | self . delete_if_not_viewed_in_last_seconds . map ( | n | ( SystemTime ::now ( ) - n ) > self . last_viewed . unwrap ( ) ) . unwrap_or ( false )
}
2018-08-16 23:33:37 -04:00
fn mark_viewed_and_write < P : AsRef < Path > > ( & mut self , path : P ) -> Result < ( ) > {
2018-08-16 22:25:53 -04:00
let mut must_write = false ;
if self . delete_after_num_views . is_some ( ) {
must_write = true ;
self . num_views + = 1 ;
}
if self . delete_if_not_viewed_in_last_seconds . is_some ( ) {
must_write = true ;
self . last_viewed = Some ( SystemTime ::now ( ) ) ;
}
if must_write {
self . write ( & path ) ? ;
}
Ok ( ( ) )
}
2018-08-07 23:23:15 -04:00
}
2018-08-16 22:25:53 -04:00
2018-08-17 00:39:27 -04:00
trait Backend : Sync + Send {
fn new_paste ( & self ) -> ( String , String , String ) ;
2018-08-07 23:23:15 -04:00
2018-08-17 00:39:27 -04:00
fn file_paths ( & self , id : PasteID ) -> ( String , String , String ) ;
2018-08-16 22:25:53 -04:00
2018-08-17 00:39:27 -04:00
fn upload ( & self , paste : Data , _key : Option < PasteID > ) -> Result < String > ;
2018-08-16 22:25:53 -04:00
2019-02-16 23:47:09 -05:00
// fn upload_multipart(&self, paste: MultipartUpload) -> Result<String>;
2018-08-16 22:25:53 -04:00
2018-08-17 00:39:27 -04:00
fn upload_string ( & self , paste : & PasteForm ) -> Result < String > ;
2018-08-16 22:25:53 -04:00
2018-08-17 00:39:27 -04:00
fn upload_tcp_stream ( & self , mut stream : TcpStream ) -> Result < ( ) > ;
2018-08-16 22:25:53 -04:00
2018-08-17 00:39:27 -04:00
fn get ( & self , id : PasteID ) -> Result < content ::Plain < File > > ;
2018-08-16 22:25:53 -04:00
}
2018-08-17 00:39:27 -04:00
#[ derive(Default) ]
struct DefaultBackend { }
/*
2018-08-16 22:25:53 -04:00
enum Backend {
PlainFile
}
2018-08-17 00:39:27 -04:00
* /
impl Backend for DefaultBackend {
//impl Backend {
2018-08-16 22:25:53 -04:00
fn new_paste ( & self ) -> ( String , String , String ) {
loop {
let id = PasteID ::new ( ) ;
let filename = format! ( " upload/ {id} " , id = id ) ;
if ! Path ::new ( & filename ) . exists ( ) & & fs ::create_dir_all ( filename ) . is_ok ( ) {
let url = format! ( " {host} / {id} \n " , host = HOST , id = id ) ;
return ( format! ( " upload/ {id} /file " , id = id ) , format! ( " upload/ {id} /info " , id = id ) , url )
}
}
}
fn file_paths ( & self , id : PasteID ) -> ( String , String , String ) {
( format! ( " upload/ {id} /file " , id = id ) , format! ( " upload/ {id} /info " , id = id ) , format! ( " upload/ {id} " , id = id ) )
}
2018-08-16 23:33:37 -04:00
fn upload ( & self , paste : Data , _key : Option < PasteID > ) -> Result < String > {
2018-08-16 22:25:53 -04:00
let ( filename , info_filename , url ) = self . new_paste ( ) ;
2018-08-16 23:33:37 -04:00
PasteInfo ::default ( ) . write ( info_filename ) ? ;
2018-08-16 22:25:53 -04:00
paste . stream_to_file ( Path ::new ( & filename ) ) ? ;
Ok ( url )
}
2019-02-16 23:47:09 -05:00
/*
2018-08-16 23:33:37 -04:00
fn upload_multipart ( & self , paste : MultipartUpload ) -> Result < String > {
2018-08-16 22:25:53 -04:00
let ( filename , info_filename , url ) = self . new_paste ( ) ;
2018-08-16 23:33:37 -04:00
PasteInfo ::default ( ) . write ( info_filename ) ? ;
2018-08-16 22:25:53 -04:00
paste . stream_to_file ( Path ::new ( & filename ) ) ? ;
Ok ( url )
}
2019-02-16 23:47:09 -05:00
* /
2018-08-16 23:33:37 -04:00
fn upload_string ( & self , paste : & PasteForm ) -> Result < String > {
2018-08-16 22:25:53 -04:00
let ( filename , info_filename , url ) = self . new_paste ( ) ;
PasteInfo ::from ( paste ) . write ( info_filename ) ? ;
fs ::write ( filename , & paste . content ) ? ;
Ok ( url )
}
2018-08-17 00:39:27 -04:00
fn upload_tcp_stream ( & self , mut stream : TcpStream ) -> Result < ( ) > {
2018-08-16 23:33:37 -04:00
let ( filename , info_filename , url ) = self . new_paste ( ) ;
PasteInfo ::default ( ) . write ( info_filename ) ? ;
let mut paste_file = File ::create ( & filename ) ? ;
let timeout = Some ( Duration ::new ( 5 , 0 ) ) ; // todo: make this config store in struct
stream . set_read_timeout ( timeout ) ? ;
stream . set_write_timeout ( timeout ) ? ;
stream . write ( & url . into_bytes ( ) ) ? ;
stream . flush ( ) ? ;
let upload_max_size : u64 = 8 * 1024 * 1024 ; // todo: make this config store in struct
copy ( & mut stream , & mut paste_file , upload_max_size ) ? ;
Ok ( ( ) )
}
fn get ( & self , id : PasteID ) -> Result < content ::Plain < File > > {
2018-08-16 22:25:53 -04:00
let ( filename , info_filename , foldername ) = self . file_paths ( id ) ;
let mut paste_info = PasteInfo ::read ( & info_filename ) ? ;
// first check if we should delete this
if paste_info . should_delete ( ) {
fs ::remove_dir_all ( foldername ) ? ;
return Err ( Error ::NotFound ) ;
}
// now check if we need to modify+write this
paste_info . mark_viewed_and_write ( info_filename ) ? ;
let file = File ::open ( & filename ) . map ( | f | content ::Plain ( f ) ) ? ;
Ok ( file )
}
}
2018-08-17 00:39:27 -04:00
impl < ' a , ' r > FromRequest < ' a , ' r > for & ' a dyn Backend {
2018-08-16 22:25:53 -04:00
type Error = ( ) ;
fn from_request ( req : & ' a Request < ' r > ) -> request ::Outcome < Self , ( ) > {
2018-08-17 00:39:27 -04:00
let backend = req . guard ::< State < Box < Backend > > > ( ) ? ;
let backend = backend . inner ( ) ;
Success ( backend . as_ref ( ) )
2018-08-16 22:25:53 -04:00
}
2018-08-07 23:23:15 -04:00
}
#[ derive(FromForm) ]
struct PasteForm {
content : String ,
2018-08-16 22:25:53 -04:00
_extension : Option < String > ,
key : Option < String > , // key to update/delete paste with // todo: use PasteId here for validation if you can figure out lifetime shit
delete_after : Option < String > , // delete after this date regardless // todo: use custom type here for validation
delete_after_num_views : Option < u32 > , // delete after this many views
delete_if_not_viewed_in_last_seconds : Option < u64 > , // delete if last_viewed is longer than this many seconds ago // todo: use Duration here for validation
}
impl < ' a > From < & ' a PasteForm > for PasteInfo < ' a > {
fn from ( value : & ' a PasteForm ) -> PasteInfo < ' a > {
PasteInfo {
key : None , //value.key.map(|s| PasteID::of(&s)),
delete_after : None ,
delete_after_num_views : value . delete_after_num_views ,
delete_if_not_viewed_in_last_seconds : value . delete_if_not_viewed_in_last_seconds . map ( | s | Duration ::from_secs ( s ) ) ,
last_viewed : value . delete_if_not_viewed_in_last_seconds . map ( | _s | SystemTime ::now ( ) ) ,
num_views : 0 ,
}
}
2018-08-07 23:23:15 -04:00
}
// todo: change /w to /, shouldn't conflict because of format, but it does currently
#[ post( " /w " , format = " application/x-www-form-urlencoded " , data = " <paste> " ) ]
2018-08-16 23:33:37 -04:00
fn web_post ( backend : & Backend , paste : LenientForm < PasteForm > ) -> Result < String > {
2019-02-16 23:47:09 -05:00
backend . upload_string ( & paste . into_inner ( ) )
2018-08-07 23:23:15 -04:00
}
2019-02-16 23:47:09 -05:00
/*
2018-08-07 23:23:15 -04:00
// todo: change /w to /, shouldn't conflict because of format, but it does currently
#[ post( " /m " , format = " multipart/form-data " , data = " <paste> " ) ]
2018-08-16 23:33:37 -04:00
fn mpu_post ( backend : & Backend , paste : MultipartUpload ) -> Result < String > {
2018-08-16 22:25:53 -04:00
backend . upload_multipart ( paste )
2018-08-07 23:23:15 -04:00
}
2019-02-16 23:47:09 -05:00
* /
2018-08-07 23:23:15 -04:00
#[ put( " / " , data = " <paste> " ) ]
2018-08-16 23:33:37 -04:00
fn upload_put ( backend : & Backend , paste : Data ) -> Result < String > {
2018-08-16 22:25:53 -04:00
backend . upload ( paste , None )
2018-08-07 23:23:15 -04:00
}
#[ post( " / " , data = " <paste> " ) ]
2018-08-16 23:33:37 -04:00
fn upload_post ( backend : & Backend , paste : Data ) -> Result < String > {
2018-08-16 22:25:53 -04:00
backend . upload ( paste , None )
2018-08-07 23:23:15 -04:00
}
#[ patch( " / " , data = " <paste> " ) ]
2018-08-16 23:33:37 -04:00
fn upload_patch ( backend : & Backend , paste : Data ) -> Result < String > {
2018-08-16 22:25:53 -04:00
backend . upload ( paste , None )
2018-08-07 23:23:15 -04:00
}
#[ put( " /<key> " , data = " <paste> " ) ]
2018-08-16 23:33:37 -04:00
fn upload_put_key ( backend : & Backend , paste : Data , key : PasteID ) -> Result < String > {
2018-08-16 22:25:53 -04:00
backend . upload ( paste , Some ( key ) )
2018-08-07 23:23:15 -04:00
}
#[ post( " /<key> " , data = " <paste> " ) ]
2018-08-16 23:33:37 -04:00
fn upload_post_key ( backend : & Backend , paste : Data , key : PasteID ) -> Result < String > {
2018-08-16 22:25:53 -04:00
backend . upload ( paste , Some ( key ) )
2018-08-07 23:23:15 -04:00
}
#[ patch( " /<key> " , data = " <paste> " ) ]
2018-08-16 23:33:37 -04:00
fn upload_patch_key ( backend : & Backend , paste : Data , key : PasteID ) -> Result < String > {
2018-08-16 22:25:53 -04:00
backend . upload ( paste , Some ( key ) )
2018-08-07 23:23:15 -04:00
}
#[ get( " /<id> " ) ]
2018-08-16 22:25:53 -04:00
fn get ( backend : & Backend , id : PasteID ) -> Option < content ::Plain < File > > {
backend . get ( id ) . ok ( )
2018-08-07 23:23:15 -04:00
}
2018-08-16 22:25:53 -04:00
#[ delete( " /<id>/<_key> " ) ]
fn delete ( id : PasteID , _key : PasteID ) -> Option < content ::Plain < File > > {
2018-08-07 23:23:15 -04:00
let filename = format! ( " upload/ {id} " , id = id ) ;
File ::open ( & filename ) . map ( | f | content ::Plain ( f ) ) . ok ( )
}
#[ get( " / " ) ]
2018-08-16 23:33:37 -04:00
fn index ( ) -> Result < NamedFile > {
let index = NamedFile ::open ( " static/index.html " ) ? ;
Ok ( index )
2018-08-07 23:23:15 -04:00
}
#[ get( " /static/<file..> " ) ]
fn files ( file : PathBuf ) -> Option < NamedFile > {
NamedFile ::open ( Path ::new ( " static/ " ) . join ( file ) ) . ok ( )
}
fn rocket ( ) -> rocket ::Rocket {
rocket ::ignite ( ) . mount ( " / " , routes! [
index , files ,
web_post ,
2019-02-16 23:47:09 -05:00
// mpu_post,
2018-08-07 23:23:15 -04:00
upload_post , upload_put , upload_patch ,
upload_post_key , upload_put_key , upload_patch_key ,
2018-08-16 22:25:53 -04:00
get , delete
2018-08-07 23:23:15 -04:00
] )
}
// adapted from io::copy
2018-08-16 23:33:37 -04:00
fn copy < R : ? Sized , W : ? Sized > ( reader : & mut R , writer : & mut W , upload_max_size : u64 ) -> io ::Result < u64 >
2018-08-07 23:23:15 -04:00
where R : Read , W : Write
{
let mut buf : [ u8 ; 8192 ] = [ 0 ; 8192 ] ;
let mut written = 0 ;
loop {
let len = match reader . read ( & mut buf ) {
Ok ( 0 ) = > return Ok ( written ) ,
Ok ( len ) = > len ,
Err ( ref e ) if e . kind ( ) = = std ::io ::ErrorKind ::Interrupted = > continue ,
Err ( e ) = > return Err ( e ) ,
} ;
writer . write_all ( & buf [ .. len ] ) ? ;
written + = len as u64 ;
2018-08-16 23:33:37 -04:00
if written > upload_max_size {
2018-08-07 23:23:15 -04:00
return Err ( std ::io ::Error ::from ( std ::io ::ErrorKind ::InvalidData ) )
}
}
}
2018-08-17 00:39:27 -04:00
/*
//fn run_tcp() {
fn run_tcp < T : Send + Sync + 'static > ( backend : T )
where T : Backend
{
2018-08-07 23:23:15 -04:00
// Bind the server's socket
2018-08-17 00:39:27 -04:00
thread ::spawn ( move | | {
//let backendbla = DefaultBackend::default();
//let backend = &backendbla;
//let backend = backend.as_ref();
let backend = & backend as & 'static Backend ;
2018-08-16 22:25:53 -04:00
let listener = TcpListener ::bind ( " 127.0.0.1:12345 " ) . unwrap ( ) ;
2018-08-07 23:23:15 -04:00
loop {
match listener . accept ( ) {
2018-08-16 22:25:53 -04:00
Ok ( ( mut stream , _addr ) ) = > {
2018-08-07 23:23:15 -04:00
thread ::spawn ( move | | {
2018-08-16 23:33:37 -04:00
backend . upload_tcp_stream ( stream ) . is_ok ( ) ; // again we don't care about this error
2018-08-16 22:25:53 -04:00
} ) ;
2018-08-07 23:23:15 -04:00
} ,
2018-08-16 22:25:53 -04:00
Err ( _e ) = > {
// just ignore this I guess? could log?
2018-08-07 23:23:15 -04:00
} ,
} ;
} ;
2018-08-16 23:04:57 -04:00
2018-08-07 23:23:15 -04:00
} ) ;
}
2018-08-17 00:39:27 -04:00
* /
2018-08-07 23:23:15 -04:00
fn main ( ) {
2018-08-17 00:39:27 -04:00
let backend = Box ::new ( DefaultBackend ::default ( ) ) ;
//let tcp_backend = DefaultBackend::default();
//run_tcp(tcp_backend);
//run_tcp();
rocket ( ) . manage ( backend as Box < Backend + 'static > ) . launch ( ) ;
2018-08-07 23:23:15 -04:00
}