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
116
117
118
119
120
121
122
123
124
125
126
use crate::config::Config;
use crate::error::{Error, Result};
use multipart::client::lazy::Multipart;
use std::io::Read;
use ureq::Error as UreqError;
use ureq::{Agent, AgentBuilder};
const DEFAULT_FILE_NAME: Option<&str> = Some("file");
const EXPIRATION_HEADER: &str = "expire";
#[derive(Debug)]
pub struct UploadResult<'a, T>(pub &'a str, pub Result<T>);
#[derive(Debug)]
pub struct Uploader<'a> {
client: Agent,
config: &'a Config,
}
impl<'a> Uploader<'a> {
pub fn new(config: &'a Config) -> Self {
Self {
client: AgentBuilder::new()
.user_agent(&format!(
"{}/{}",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_VERSION")
))
.build(),
config,
}
}
pub fn upload_file(&self, file: &'a str) -> UploadResult<'a, String> {
let field = if self.config.paste.oneshot == Some(true) {
"oneshot"
} else {
"file"
};
let mut multipart = Multipart::new();
multipart.add_file(field, file);
UploadResult(file, self.upload(multipart))
}
pub fn upload_url(&self, url: &'a str) -> UploadResult<'a, String> {
let mut multipart = Multipart::new();
multipart.add_stream::<_, &[u8], &str>("url", url.as_bytes(), None, None);
UploadResult(url, self.upload(multipart))
}
pub fn upload_remote_url(&self, url: &'a str) -> UploadResult<'a, String> {
let mut multipart = Multipart::new();
multipart.add_stream::<_, &[u8], &str>("remote", url.as_bytes(), None, None);
UploadResult(url, self.upload(multipart))
}
pub fn upload_stream<S: Read>(&self, stream: S) -> UploadResult<'a, String> {
let field = if self.config.paste.oneshot == Some(true) {
"oneshot"
} else {
"file"
};
let mut multipart = Multipart::new();
multipart.add_stream(field, stream, DEFAULT_FILE_NAME, None);
UploadResult("stream", self.upload(multipart))
}
fn upload(&self, mut multipart: Multipart<'static, '_>) -> Result<String> {
let multipart_data = multipart.prepare()?;
let mut request = self.client.post(&self.config.server.address).set(
"Content-Type",
&format!(
"multipart/form-data; boundary={}",
multipart_data.boundary()
),
);
if let Some(auth_token) = &self.config.server.auth_token {
request = request.set("Authorization", auth_token);
}
if let Some(expiration_time) = &self.config.paste.expire {
request = request.set(EXPIRATION_HEADER, expiration_time);
}
match request.send(multipart_data) {
Ok(response) => {
let status = response.status();
let response_text = response.into_string()?;
if response_text.lines().count() != 1 {
Err(Error::UploadError(format!(
"server returned invalid body (status code: {})",
status
)))
} else if status == 200 {
Ok(response_text)
} else {
Err(Error::UploadError(format!(
"unknown error (status code: {})",
status
)))
}
}
Err(UreqError::Status(code, response)) => Err(Error::UploadError(format!(
"{} (status code: {})",
response.into_string()?,
code
))),
Err(e) => Err(Error::RequestError(Box::new(e))),
}
}
}