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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
#![feature(async_closure)]
#![feature(decl_macro)]
use lazy_static::lazy_static;
use structopt::StructOpt;

/// Command line arguments for the program
#[derive(Debug, StructOpt, Clone, PartialEq)]
#[structopt(
    author = "Anonymous",
    about = "Serve files in the specified directory and subdirectories"
)]
struct Arguments {
    /// The port that the server should bind to
    #[structopt(long, short, default_value = "8787")]
    pub port: u16,

    /// Whether additional data should be logged to the console
    #[structopt(long, short)]
    pub verbose: bool,

    /// Deploy on localhost, rather than attempting to bind to the external interface
    #[structopt(long, short)]
    pub localhost: bool,

    /// The root directory that should be served by the program
    #[structopt(default_value = ".")]
    pub folder: String,
}

lazy_static! {
  /// The command line arguments passed into the program
  static ref ARGUMENTS: Arguments = Arguments::from_args();
}

#[allow(warnings)]
pub mod file_show_server{
  use std::borrow::Cow;
  use std::fs;
  use std::net::{IpAddr, SocketAddr};
  use std::path::PathBuf;
  use rocket::http::hyper::Request;
  use rocket::response::content;
  use rocket::{get, Response, routes, Config, catch, catchers, Responder, post, UriDisplayPath};
  use rocket::http::ContentType;
  use rocket_contrib::json::{Json as JSON, JsonValue};
  use rocket::Data;
  use tokio::io::AsyncReadExt;
  use uuid::Uuid;
  use std::io::{self, Write};
  use rocket::http::Status;
  use rocket::request::{FromRequest, Outcome};
  use crate::ARGUMENTS;
  use crate::file_show_routes::routes;
  use log::info;

  pub async fn start(){
    let ip_addr: IpAddr =  match local_ipaddress::get() {
      Some(x)=>{
          if ARGUMENTS.localhost{
              IpAddr::from([127, 0, 0, 1])
          }else{
          x.parse().expect("Found local IP Address is invalid")
        }},
        None=>IpAddr::from([127, 0, 0, 1])
    };

    let socket_addr = SocketAddr::from((ip_addr, ARGUMENTS.port));
    info!("Server listening on http://{}", socket_addr);

    warp::serve(routes()).bind(socket_addr).await;
  }
}

pub mod file_upload_server{
use log::info;
use rocket::{Data, post, routes, Rocket, get, response::content::Json};
use rocket_contrib::serve::StaticFiles;
use std::{fs, path::PathBuf, net::{IpAddr, SocketAddr}};

#[allow(warnings)]
#[post("/", data = "<data>")]
fn upload(data: Data) -> Result<Json<&'static str>, std::io::Error> {
    let path = "./stream.data";
    let pathbuf = PathBuf::from(path);
    if pathbuf.exists() {
        fs::remove_file(pathbuf.clone()).unwrap();
    }
    let mut file = fs::File::create(path)?;

    data.stream_to_file(path)
        .map(|n| format!("Wrote {} bytes to stream.data", n)).unwrap();

    if let Ok(buf) = fs::read(path) {
        if !buf.starts_with(&[0x2D,0x2D,0x2D,0x2D,0x2D,0x2D]){
        if let Some(ex) = get_file_extension(&buf) {
            fs::copy(path, format!("./stream.{}", ex)).unwrap();
        } else {
            fs::copy(path, format!("./stream")).unwrap();
        }
        }else{
          use doe::*;
        let mut buf = buf.clone();
        let remove_last = buf.split_at(buf.len()-46).0.to_vec();
        let remove_first_contet = remove_last.split_at(97).1;
        let (head,other) = remove_first_contet.split_at(200);
        let s = vec_element_to_string!(head).join(":");
        let file_name = vec_element_clone!(split_to_vec!(String::from_utf8_lossy(head),"\""),0);
        let s = split_to_vec!(s,":13:10:13:10:").last().unwrap().to_string();
        let mut s_vec = split_to_vec!(s,":").into_iter().filter(|s|!s.is_empty()).map(|s|s.trim().to_string().parse::<u8>().unwrap()).collect::<Vec<u8>>();
        s_vec.extend(other.iter());
        std::fs::write(file_name, s_vec).unwrap();
        }
    }
    if pathbuf.exists() {
        fs::remove_file(pathbuf).unwrap();
    }
    Ok(Json("{\"status\":\"Ok\"}"))
}

  #[allow(warnings)]
  use rocket::response::{content, Content};
  use rocket::http::ContentType;

  #[get("/")]
  fn index() -> Content<&'static str> {
      let content_type = ContentType::HTML;
      let body = include_str!("./html/index.html");
      Content(content_type, body)
  }

  #[allow(warnings)]
  fn get_file_extension(data: &[u8]) -> Option<&'static str> {
    let ext = match data[..] {
        // Windows PE 可执行文件
        [b'M', b'Z', ..] => Some("exe"),
        // Linux ELF 可执行文件
        [0x7f, b'E', b'L', b'F', ..] => Some("elf"),
        // PNG 图像文件
        [b'\x89', b'P', b'N', b'G', ..] => Some("png"),
        // HTML 文档
        [b'<', b'!', b'D', b'O', b'C', b'T', ..] => Some("html"),
        // PDF 文件
        [b'%', b'P', b'D', b'F', ..] => Some("pdf"),
        // ZIP 压缩文件
        [b'P', b'K', 3, 4, ..] => Some("zip"),
        // 7z 压缩文件
        [0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c, ..] => Some("7z"),
        // RAR 压缩文件
        [b'R', b'a', b'r', b'!', ..] => Some("rar"),
        // GZIP 压缩文件
        [b'\x1f', b'\x8b', 8, 0, ..] => Some("gz"),
        // BZIP2 压缩文件
        [b'B', b'Z', b'h', ..] => Some("bz2"),
        // CPIO 归档文件
        [b'I', b's', b'c', b'(', ..] => Some("cpio"),
        // TAR 归档文件
        [b'u', b's', b't', b'a', b'r', ..] => Some("tar"),
        // CHM 帮助文件
        [b'I', b'T', b'S', b'F', ..] => Some("chm"),
        // MP3 音频文件
        [b'I', b'D', b'3', ..] => Some("mp3"),
        // AVI 视频文件
        [b'R', b'I', b'F', b'F', ..] => Some("avi"),
        // JPEG/JFIF 图像文件
        [b'\xff', b'\xd8', b'\xff', ..] => Some("jpg"),
        // BMP 图像文件
        [b'B', b'M', ..] => Some("bmp"),
        // ICO 图标文件
        [0, 1, ..] => Some("ico"),
        // WAV 音频文件
        [b'R', b'I', b'F', b'F', ..] => Some("wav"),
        // Ogg 容器
        [b'O', b'g', b'g', b'S', ..] => Some("ogg"),
        // FLAC 音频文件
        [b'f', b'L', b'a', b'C', ..] => Some("flac"),
        // Impress 演示文稿
        [b'I', b'M', b'P', b'S', ..] => Some("impress"),
        // MIDI 文件
        [b'M', b'T', b'h', b'd', ..] => Some("midi"),
        // TIFF 图像文件
        [b'M', b'M', 0, 42, ..] => Some("tiff"),
        // CR2 图像文件
        [b'I', b'I', 0x2a, 0, 0x10, 0, 0, 0, b'C', b'R', ..] => Some("cr2"),
        // NEF 图像文件
        [b'M', b'M', 0, 0x2a, ..] => Some("nef"),
        // WebP 图像文件
        [b'W', b'E', b'B', b'P', ..] => Some("webp"),
        // Cab 归档文件
        [b'M', b'S', b'C', b'F', ..] => Some("cab"),
        // Microsoft Office Open XML 文档
        [b'P', b'K', 3, 4, ..] => Some("xlsx"),
        // XZ 压缩格式
        [b'\xfd', b'7', b'z', b'X', b'Z', 0, ..] => Some("xz"),
        // iCalendar 文档
        [b'B', b'E', b'G', b'I', b'N', b':', b'V', b'C', ..] => Some("ics"),
        // vCard 文档
        [b'B', b'E', b'G', b'I', b'N', b':', b'V', b'C', b'A', b'R', b'D', ..] => Some("vcf"),
        // SQLite 数据库文件
        [b'S', b'Q', b'L', b'i', b't', b'e', b' ', b'f', b'o', b'r', b'm', b'a', b't', b' ', b'3', ..] => Some("sqlite"),
        // GIF 图像文件
        [b'G', b'I', b'F', b'8', b'7', b'a', ..] |
        [b'G', b'I', b'F', b'8', b'9', b'a', ..] => Some("gif"),

        _ => None,
    };
    ext
}
  pub async fn start() {
    let ip_addr: IpAddr =  match local_ipaddress::get() {
      Some(x)=>{
          x.parse().expect("Found local IP Address is invalid")
        },
        None=>IpAddr::from([127, 0, 0, 1])
    };
    use rocket::config::{Config, Environment};

    let socket_addr = SocketAddr::from((ip_addr, 8585));
    info!("Server listening on http://{}", socket_addr);
    let config = Config::build(Environment::Production)
    .address(socket_addr.ip().to_string())  // 设置应用程序监听地址
    .port(socket_addr.port())            // 设置应用程序监听端口
    .finalize()
    .unwrap();

    Rocket::custom(config)
    .mount("/", StaticFiles::from("static"))
    .mount("/", routes![upload,index])
    .launch();
  }

}
#[allow(warnings)]
pub mod start_all_server{
  use log::{info, LevelFilter};
  use std::net::{IpAddr, SocketAddr};
  use structopt::StructOpt;
  use crate::ARGUMENTS;

  fn initialize_logger() {
    let level = if ARGUMENTS.verbose {
        LevelFilter::Debug
    } else {
        LevelFilter::Info
    };
    env_logger::builder()
        .format_module_path(false)
        .filter(Some("serve"), level)
        .init()
  }
  pub async fn start() {
    use tokio::signal::ctrl_c;
    initialize_logger();

    let handle1 =  tokio::spawn(async move {
      crate::file_show_server::start().await;
    });
    let handle2 = tokio::spawn(async move {
      crate::file_upload_server::start().await;
    });

    ctrl_c().await.expect("Unalbe to get Ctrl+C signal");
    info!("Ctrl+C received. Shutting down");
    handle1.abort();
    handle2.abort();
    std::process::exit(1);

    handle1.await.unwrap_or(());
    handle2.await.unwrap_or(());
  }

}

pub mod file_show_routes{
  use build_html::*;
  use log::debug;
  use std::fs::read_dir;
  use std::path::{Path, PathBuf};
  use warp::filters::BoxedFilter;
  use warp::path::FullPath;
  use warp::reject::not_found;
  use warp::reply::{html, Reply};
  use warp::Filter;
  use crate::ARGUMENTS;

/// The set of routes used by the program
pub fn routes() -> BoxedFilter<(impl Reply,)> {
    let logging = warp::log::custom(|info| {
        debug!("Request: '{}',\tStatus: '{}'", info.path(), info.status())
    });
    let handle_files = warp::fs::dir(&ARGUMENTS.folder);
    let handle_directories = warp::get()
        .and(warp::path::full())
        .and_then(path_to_html)
        .map(html);

    handle_files.or(handle_directories).with(logging).boxed()
}

/// Converts the URL route of a folder to an HTML string of the contents
async fn path_to_html(route: FullPath) -> Result<String, warp::reject::Rejection> {
    let path = PathBuf::from(&ARGUMENTS.folder).join(&route.as_str()[1..]);
    let content = HtmlPage::new()
        .with_style(include_str!("styles.css"))
        .with_container(
            Container::new(ContainerType::Main)
                .with_attributes([("class", "border-box")])
                .with_preformatted_attr(route.as_str(), [("id", "header")])
                .with_container(links_container(path.as_path(), &route).ok_or_else(not_found)?),
        )
        .to_html_string();

    Ok(content)
}

/// Get the container that the links will be contained within
fn links_container(path: &Path, route: &FullPath) -> Option<Container> {
    let content_attrs = [("class", "content")];
    let mut links = Container::new(ContainerType::Div).with_attributes([("id", "wrapper")]);

    if route.as_str() != "/" {
        let parent = path
            .parent()
            .and_then(|path| path.strip_prefix(&ARGUMENTS.folder).ok())
            .and_then(Path::to_str)
            .map(|s| format!("/{}", s))?;
        links.add_link_attr(parent, "..", content_attrs);
    }
    let mut entries: Vec<(String, String, &'static str)> = read_dir(&path)
        .ok()?
        .filter_map(|res| res.ok().map(|x| x.path()))
        .filter_map(format_path)
        .collect();
    entries.sort_by_cached_key(|(_, name, _)| name.to_string());
    for (path, name, icon) in entries {
        let link_text = format!("{}<p class=\"text\">{}</p>", icon, name);
        links.add_link_attr(path, link_text, content_attrs);
    }
    Some(links)
}

/// Converts the provided `PathBuf` into the partial path off of the root, and the filename
fn format_path(path: PathBuf) -> Option<(String, String, &'static str)> {
    let net_path = format!("/{}", path.strip_prefix(&ARGUMENTS.folder).ok()?.to_str()?);
    let file_name = path.file_name()?.to_str()?.into();
    let icon = if path.is_dir() {
        include_str!("./folder_icon.svg")
    } else {
        include_str!("./file_icon.svg")
    };
    Some((net_path, file_name, icon))
}
}