1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//! A *Pastebin* server library.
//!
//! This library implements a very simple *pastebin* service, which is basically a web-interface
//! between a user and a database (be it MongoDB, or hash table, or MariaDB, or anything else).
//! The library is database-agnostic, which means a database wrapper has to be implemented for a
//! desired DB kind my implementing a quite simple interface `DbInterface`.
//!
//! [Iron](https://github.com/iron/iron) is used as a web-backend, so all its features could be
//! utilized (at least theoretically). The actual code is in the [web](web/index.html) module,
//! useful examples are also there.

extern crate base64;
extern crate chrono;
#[macro_use]
extern crate iron;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate mime_guess;
#[macro_use]
extern crate quick_error;
extern crate rand;
extern crate serde;
#[macro_use]
extern crate serde_json;
extern crate tera;
extern crate tree_magic;

pub mod web;

mod error;
mod id;
mod mime;
mod pastebin;
mod read;
mod request;
#[cfg(test)]
mod test;

#[cfg(test)]
extern crate reqwest;

use chrono::{DateTime, Utc};
pub use error::Error;
use iron::error::HttpResult;

/// A paste representation. As simple as that.
#[derive(Debug, Clone)]
pub struct PasteEntry {
    /// Raw paste data.
    pub data: Vec<u8>,
    /// File name associated with the pate, if any.
    pub file_name: Option<String>,
    /// Mime type of the paste.
    pub mime_type: String,
    /// Expiration date, if any.
    pub best_before: Option<DateTime<Utc>>,
}

/// Interface to a database.
///
/// To store and retrieve pastes from a database we only need several functions. And we can
/// describe them to be abstract enough to be easily used with just any kind of database, be it
/// SQL, NoSQL or just a hash table or whatever.
///
/// # Thread safety
///
/// This trait is required to be thread safe (`Send + Sync`) since it will be used from multiple
/// threads.
///
/// # Errors handling
///
/// An implementation must provide an `Error` type, which must be thread safe as well and also
/// have a `'static` lifetime.
///
/// Should some method return an error it will be logged by the web server, but will not be send to
/// an http client, it will just receive an internal server error:
/// [500](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500).
pub trait DbInterface: Send + Sync {
    type Error: Send + Sync + std::error::Error + 'static;

    /// Stores the data into the database and returns a unique ID that should be used later to
    /// access the data.
    ///
    /// # Return value
    ///
    /// The function is expected to return a unique ID.
    fn store_data(&self,
                  data: Vec<u8>,
                  file_name: Option<String>,
                  mime_type: String,
                  best_before: Option<DateTime<Utc>>)
                  -> Result<u64, Self::Error>;

    /// Loads data from the database.
    ///
    /// Returns corresponding data if found, `None` otherwise.
    fn load_data(&self, id: u64) -> Result<Option<PasteEntry>, Self::Error>;

    /// Gets a file name of a paste (if any).
    fn get_file_name(&self, id: u64) -> Result<Option<String>, Self::Error>;

    /// Removes data from the database.
    ///
    /// Normally we don't care whether an object exists in the database or not, so an
    /// implementation doesn't have to check that fact, and usually databases are okay with
    /// attempts to remove something that doesn't exist.
    fn remove_data(&self, id: u64) -> Result<(), Self::Error>;

    /// Returns the maximum data size that could be handled.
    ///
    /// This is useful, for example, for MongoDB which has a limit on a BSON document size.
    fn max_data_size(&self) -> usize;
}