1cc290419Sopenharmony_ci/*
2cc290419Sopenharmony_ci * Copyright (C) 2023 Huawei Device Co., Ltd.
3cc290419Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4cc290419Sopenharmony_ci * you may not use this file except in compliance with the License.
5cc290419Sopenharmony_ci * You may obtain a copy of the License at
6cc290419Sopenharmony_ci *
7cc290419Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8cc290419Sopenharmony_ci *
9cc290419Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10cc290419Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11cc290419Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12cc290419Sopenharmony_ci * See the License for the specific language governing permissions and
13cc290419Sopenharmony_ci * limitations under the License.
14cc290419Sopenharmony_ci */
15cc290419Sopenharmony_ciuse super::translate;
16cc290419Sopenharmony_ci
17cc290419Sopenharmony_ciuse hdc::config::{self, HdcCommand};
18cc290419Sopenharmony_ci
19cc290419Sopenharmony_ciuse hdc::utils;
20cc290419Sopenharmony_ciuse std::collections::HashMap;
21cc290419Sopenharmony_ciuse std::io::{self, Error, ErrorKind};
22cc290419Sopenharmony_ciuse std::str::FromStr;
23cc290419Sopenharmony_ci
24cc290419Sopenharmony_ci#[derive(Default, Debug, Clone)]
25cc290419Sopenharmony_cipub struct Parsed {
26cc290419Sopenharmony_ci    pub options: Vec<String>,
27cc290419Sopenharmony_ci    pub command: Option<HdcCommand>,
28cc290419Sopenharmony_ci    pub parameters: Vec<String>,
29cc290419Sopenharmony_ci}
30cc290419Sopenharmony_ci
31cc290419Sopenharmony_cilazy_static! {
32cc290419Sopenharmony_ci    static ref CMD_MAP: HashMap<&'static str, HdcCommand> = {
33cc290419Sopenharmony_ci        let mut map = HashMap::new();
34cc290419Sopenharmony_ci
35cc290419Sopenharmony_ci        map.insert("version", HdcCommand::ClientVersion);
36cc290419Sopenharmony_ci        map.insert("help", HdcCommand::KernelHelp);
37cc290419Sopenharmony_ci        map.insert("discover", HdcCommand::KernelTargetDiscover);
38cc290419Sopenharmony_ci        map.insert("start", HdcCommand::KernelServerStart);
39cc290419Sopenharmony_ci        map.insert("kill", HdcCommand::KernelServerKill);
40cc290419Sopenharmony_ci        map.insert("keygen", HdcCommand::ClientKeyGenerate);
41cc290419Sopenharmony_ci        map.insert("list targets", HdcCommand::KernelTargetList);
42cc290419Sopenharmony_ci        map.insert("checkserver", HdcCommand::KernelCheckServer);
43cc290419Sopenharmony_ci        map.insert("checkdevice", HdcCommand::KernelCheckDevice);
44cc290419Sopenharmony_ci        map.insert("wait", HdcCommand::KernelWaitFor);
45cc290419Sopenharmony_ci        map.insert("tconn", HdcCommand::KernelTargetConnect);
46cc290419Sopenharmony_ci        map.insert("any", HdcCommand::KernelTargetAny);
47cc290419Sopenharmony_ci        map.insert("shell", HdcCommand::UnityExecute);
48cc290419Sopenharmony_ci        map.insert("target boot", HdcCommand::UnityReboot);
49cc290419Sopenharmony_ci        map.insert("target mount", HdcCommand::UnityRemount);
50cc290419Sopenharmony_ci        map.insert("smode", HdcCommand::UnityRootrun);
51cc290419Sopenharmony_ci        map.insert("tmode", HdcCommand::UnityRunmode);
52cc290419Sopenharmony_ci        map.insert("bugreport", HdcCommand::UnityBugreportInit);
53cc290419Sopenharmony_ci        map.insert("hilog", HdcCommand::UnityHilog);
54cc290419Sopenharmony_ci        map.insert("file send", HdcCommand::FileInit);
55cc290419Sopenharmony_ci        map.insert("file recv", HdcCommand::FileRecvInit);
56cc290419Sopenharmony_ci        map.insert("fport", HdcCommand::ForwardInit);
57cc290419Sopenharmony_ci        map.insert("rport", HdcCommand::ForwardRportInit);
58cc290419Sopenharmony_ci        map.insert("rport ls", HdcCommand::ForwardRportList);
59cc290419Sopenharmony_ci        map.insert("fport ls", HdcCommand::ForwardList);
60cc290419Sopenharmony_ci        map.insert("fport rm", HdcCommand::ForwardRemove);
61cc290419Sopenharmony_ci        map.insert("rport rm", HdcCommand::ForwardRportRemove);
62cc290419Sopenharmony_ci        map.insert("install", HdcCommand::AppInit);
63cc290419Sopenharmony_ci        map.insert("uninstall", HdcCommand::AppUninstall);
64cc290419Sopenharmony_ci        map.insert("sideload", HdcCommand::AppSideload);
65cc290419Sopenharmony_ci        map.insert("jpid", HdcCommand::JdwpList);
66cc290419Sopenharmony_ci        map.insert("track-jpid", HdcCommand::JdwpTrack);
67cc290419Sopenharmony_ci        map.insert("alive", HdcCommand::KernelEnableKeepalive);
68cc290419Sopenharmony_ci        map.insert("update", HdcCommand::FlashdUpdateInit);
69cc290419Sopenharmony_ci        map.insert("flash", HdcCommand::FlashdFlashInit);
70cc290419Sopenharmony_ci        map.insert("erase", HdcCommand::FlashdErase);
71cc290419Sopenharmony_ci        map.insert("format", HdcCommand::FlashdFormat);
72cc290419Sopenharmony_ci
73cc290419Sopenharmony_ci        map
74cc290419Sopenharmony_ci    };
75cc290419Sopenharmony_ci}
76cc290419Sopenharmony_ci
77cc290419Sopenharmony_ciconst MAX_CMD_LEN: usize = 3;
78cc290419Sopenharmony_ciconst MAX_CONNECTKEY_SIZE: u16 = 32;
79cc290419Sopenharmony_ciconst COMBINED_COMMAND_LEN: usize = 2;
80cc290419Sopenharmony_ci
81cc290419Sopenharmony_ci// TODO: trial tree
82cc290419Sopenharmony_cipub fn split_opt_and_cmd(input: Vec<String>) -> Parsed {
83cc290419Sopenharmony_ci    let mut cmd_opt: Option<HdcCommand> = None;
84cc290419Sopenharmony_ci    let mut cmd_index = input.len();
85cc290419Sopenharmony_ci    for st in 0..input.len() {
86cc290419Sopenharmony_ci        for len in 1..MAX_CMD_LEN {
87cc290419Sopenharmony_ci            if st + len > input.len() {
88cc290419Sopenharmony_ci                break;
89cc290419Sopenharmony_ci            }
90cc290419Sopenharmony_ci            let cmd = input[st..st + len].join(" ");
91cc290419Sopenharmony_ci            if let Some(command) = CMD_MAP.get(cmd.as_str()) {
92cc290419Sopenharmony_ci                // if first command parsed is "fport", but next command parsed
93cc290419Sopenharmony_ci                // is not "fport ls" or "fport rm", discard the new command.
94cc290419Sopenharmony_ci                if cmd_opt.is_some()
95cc290419Sopenharmony_ci                    && (cmd_opt.unwrap() == HdcCommand::ForwardInit
96cc290419Sopenharmony_ci                        || cmd_opt.unwrap() == HdcCommand::ForwardRportInit)
97cc290419Sopenharmony_ci                    && (*command != HdcCommand::ForwardRemove
98cc290419Sopenharmony_ci                        && *command != HdcCommand::ForwardList
99cc290419Sopenharmony_ci                        && *command != HdcCommand::ForwardRportList
100cc290419Sopenharmony_ci                        && *command != HdcCommand::ForwardRportRemove)
101cc290419Sopenharmony_ci                {
102cc290419Sopenharmony_ci                    break;
103cc290419Sopenharmony_ci                }
104cc290419Sopenharmony_ci                cmd_index = st;
105cc290419Sopenharmony_ci                cmd_opt = Some(command.to_owned());
106cc290419Sopenharmony_ci                if *command == HdcCommand::ForwardInit || *command == HdcCommand::ForwardRportInit {
107cc290419Sopenharmony_ci                    continue;
108cc290419Sopenharmony_ci                } else {
109cc290419Sopenharmony_ci                    break;
110cc290419Sopenharmony_ci                }
111cc290419Sopenharmony_ci            }
112cc290419Sopenharmony_ci        }
113cc290419Sopenharmony_ci        if cmd_opt.is_some()
114cc290419Sopenharmony_ci            && cmd_opt.unwrap() != HdcCommand::ForwardInit
115cc290419Sopenharmony_ci            && cmd_opt.unwrap() != HdcCommand::ForwardRportInit
116cc290419Sopenharmony_ci        {
117cc290419Sopenharmony_ci            break;
118cc290419Sopenharmony_ci        }
119cc290419Sopenharmony_ci    }
120cc290419Sopenharmony_ci    Parsed {
121cc290419Sopenharmony_ci        options: input[..cmd_index].to_vec(),
122cc290419Sopenharmony_ci        command: cmd_opt,
123cc290419Sopenharmony_ci        parameters: input[cmd_index..].to_vec(),
124cc290419Sopenharmony_ci    }
125cc290419Sopenharmony_ci}
126cc290419Sopenharmony_ci
127cc290419Sopenharmony_cipub fn parse_command(args: std::env::Args) -> io::Result<ParsedCommand> {
128cc290419Sopenharmony_ci    let input = args.collect::<Vec<_>>()[1..].to_vec();
129cc290419Sopenharmony_ci    let parsed = split_opt_and_cmd(input);
130cc290419Sopenharmony_ci    match extract_global_params(parsed.options) {
131cc290419Sopenharmony_ci        Ok(parsed_cmd) => Ok(ParsedCommand {
132cc290419Sopenharmony_ci            command: parsed.command,
133cc290419Sopenharmony_ci            parameters: parsed.parameters,
134cc290419Sopenharmony_ci            ..parsed_cmd
135cc290419Sopenharmony_ci        }),
136cc290419Sopenharmony_ci        Err(e) => Err(e),
137cc290419Sopenharmony_ci    }
138cc290419Sopenharmony_ci}
139cc290419Sopenharmony_ci
140cc290419Sopenharmony_cipub fn exchange_parsed_for_daemon(mut parsed: Parsed) -> Parsed  {
141cc290419Sopenharmony_ci    if let Some(HdcCommand::UnityReboot) = parsed.command {
142cc290419Sopenharmony_ci        if parsed.parameters.len() > COMBINED_COMMAND_LEN {
143cc290419Sopenharmony_ci            let valid_boot_cmd = ["-bootloader", "-recovery", "-flashd"];
144cc290419Sopenharmony_ci            for str in valid_boot_cmd {
145cc290419Sopenharmony_ci                if str == parsed.parameters[COMBINED_COMMAND_LEN].as_str() {
146cc290419Sopenharmony_ci                    parsed.parameters.clear();
147cc290419Sopenharmony_ci                    parsed.parameters.push(str.to_string()[1..].to_string());
148cc290419Sopenharmony_ci                    return parsed;
149cc290419Sopenharmony_ci                }
150cc290419Sopenharmony_ci            }
151cc290419Sopenharmony_ci        }
152cc290419Sopenharmony_ci        parsed.parameters.clear();
153cc290419Sopenharmony_ci        hdc::info!("parsed parameter is {:?}", parsed.parameters);
154cc290419Sopenharmony_ci    }
155cc290419Sopenharmony_ci    parsed
156cc290419Sopenharmony_ci}
157cc290419Sopenharmony_ci
158cc290419Sopenharmony_ci#[derive(Default, Debug, PartialEq)]
159cc290419Sopenharmony_cipub struct ParsedCommand {
160cc290419Sopenharmony_ci    pub run_in_server: bool,
161cc290419Sopenharmony_ci    pub launch_server: bool,
162cc290419Sopenharmony_ci    pub spawned_server: bool,
163cc290419Sopenharmony_ci    pub connect_key: String,
164cc290419Sopenharmony_ci    pub log_level: usize,
165cc290419Sopenharmony_ci    pub server_addr: String,
166cc290419Sopenharmony_ci    pub command: Option<HdcCommand>,
167cc290419Sopenharmony_ci    pub parameters: Vec<String>,
168cc290419Sopenharmony_ci}
169cc290419Sopenharmony_ci
170cc290419Sopenharmony_cipub fn extract_global_params(opts: Vec<String>) -> io::Result<ParsedCommand> {
171cc290419Sopenharmony_ci    let mut parsed_command = ParsedCommand {
172cc290419Sopenharmony_ci        launch_server: true,
173cc290419Sopenharmony_ci        log_level: 3,
174cc290419Sopenharmony_ci        server_addr: format!("127.0.0.1:{}", config::SERVER_DEFAULT_PORT),
175cc290419Sopenharmony_ci        ..Default::default()
176cc290419Sopenharmony_ci    };
177cc290419Sopenharmony_ci    let len = opts.len();
178cc290419Sopenharmony_ci    for i in 0..len {
179cc290419Sopenharmony_ci        let opt = opts[i].as_str();
180cc290419Sopenharmony_ci        let arg = if opt.len() > 2 {
181cc290419Sopenharmony_ci            &opt[2..]
182cc290419Sopenharmony_ci        } else if i < len - 1 {
183cc290419Sopenharmony_ci            opts[i + 1].as_str()
184cc290419Sopenharmony_ci        } else {
185cc290419Sopenharmony_ci            ""
186cc290419Sopenharmony_ci        };
187cc290419Sopenharmony_ci        if opt.starts_with("-h") {
188cc290419Sopenharmony_ci            if arg == "verbose" {
189cc290419Sopenharmony_ci                return Err(utils::error_other(translate::verbose()));
190cc290419Sopenharmony_ci            } else {
191cc290419Sopenharmony_ci                return Err(utils::error_other(translate::usage()));
192cc290419Sopenharmony_ci            }
193cc290419Sopenharmony_ci        } else if opt.starts_with("-v") {
194cc290419Sopenharmony_ci            return Err(utils::error_other(config::get_version()));
195cc290419Sopenharmony_ci        } else if opt.starts_with("-l") {
196cc290419Sopenharmony_ci            if let Ok(level) = arg.parse::<usize>() {
197cc290419Sopenharmony_ci                if level < config::LOG_LEVEL_ORDER.len() {
198cc290419Sopenharmony_ci                    parsed_command.log_level = level;
199cc290419Sopenharmony_ci                } else {
200cc290419Sopenharmony_ci                    return Err(utils::error_other(format!(
201cc290419Sopenharmony_ci                        "-l content loglevel incorrect\n\n{}",
202cc290419Sopenharmony_ci                        translate::usage()
203cc290419Sopenharmony_ci                    )));
204cc290419Sopenharmony_ci                }
205cc290419Sopenharmony_ci            } else {
206cc290419Sopenharmony_ci                return Err(utils::error_other(format!(
207cc290419Sopenharmony_ci                    "-l content loglevel incorrect\n\n{}",
208cc290419Sopenharmony_ci                    translate::usage()
209cc290419Sopenharmony_ci                )));
210cc290419Sopenharmony_ci            }
211cc290419Sopenharmony_ci        } else if opt.starts_with("-m") {
212cc290419Sopenharmony_ci            parsed_command.run_in_server = true;
213cc290419Sopenharmony_ci        } else if opt.starts_with("-p") {
214cc290419Sopenharmony_ci            parsed_command.launch_server = false;
215cc290419Sopenharmony_ci        } else if opt.starts_with("-t") {
216cc290419Sopenharmony_ci            parsed_command.connect_key = arg.to_string();
217cc290419Sopenharmony_ci            if arg.len() > MAX_CONNECTKEY_SIZE.into() {
218cc290419Sopenharmony_ci                return Err(utils::error_other(format!(
219cc290419Sopenharmony_ci                    "Sizeo of of parament '-t' {} is too long\n",
220cc290419Sopenharmony_ci                    arg.len()
221cc290419Sopenharmony_ci                )));
222cc290419Sopenharmony_ci            }
223cc290419Sopenharmony_ci        } else if opt.starts_with("-s") {
224cc290419Sopenharmony_ci            match parse_server_listen_string(arg.to_string()) {
225cc290419Sopenharmony_ci                Ok(server_addr) => parsed_command.server_addr = server_addr,
226cc290419Sopenharmony_ci                Err(e) => {
227cc290419Sopenharmony_ci                    return Err(utils::error_other(format!(
228cc290419Sopenharmony_ci                        "{}\n\n{}",
229cc290419Sopenharmony_ci                        e,
230cc290419Sopenharmony_ci                        translate::usage()
231cc290419Sopenharmony_ci                    )));
232cc290419Sopenharmony_ci                }
233cc290419Sopenharmony_ci            }
234cc290419Sopenharmony_ci        } else if opt.starts_with("-b") {
235cc290419Sopenharmony_ci            // server spawned by client, no stdout
236cc290419Sopenharmony_ci            parsed_command.spawned_server = true;
237cc290419Sopenharmony_ci        }
238cc290419Sopenharmony_ci    }
239cc290419Sopenharmony_ci    Ok(parsed_command)
240cc290419Sopenharmony_ci}
241cc290419Sopenharmony_ci
242cc290419Sopenharmony_cifn check_port(port_str: String) -> io::Result<u16> {
243cc290419Sopenharmony_ci    if let Ok(port) = port_str.parse::<u16>() {
244cc290419Sopenharmony_ci        return Ok(port);
245cc290419Sopenharmony_ci    }
246cc290419Sopenharmony_ci    Err(Error::new(ErrorKind::Other, "-s content port incorrect"))
247cc290419Sopenharmony_ci}
248cc290419Sopenharmony_ci
249cc290419Sopenharmony_cifn parse_server_listen_string(arg: String) -> io::Result<String> {
250cc290419Sopenharmony_ci    let segments: Vec<&str> = arg.split(':').collect();
251cc290419Sopenharmony_ci    let port_str = match segments.last() {
252cc290419Sopenharmony_ci        Some(str) => str.to_string(),
253cc290419Sopenharmony_ci        None => {
254cc290419Sopenharmony_ci            return Err(Error::new(ErrorKind::Other, "-s content ip incorrect"));
255cc290419Sopenharmony_ci        }
256cc290419Sopenharmony_ci    };
257cc290419Sopenharmony_ci    let port_len = port_str.len();
258cc290419Sopenharmony_ci    let port = check_port(port_str)?;
259cc290419Sopenharmony_ci
260cc290419Sopenharmony_ci    if segments.len() == 1 {
261cc290419Sopenharmony_ci        return Ok(format!(
262cc290419Sopenharmony_ci            // "{}{}:{}",
263cc290419Sopenharmony_ci            // config::IPV4_MAPPING_PREFIX,
264cc290419Sopenharmony_ci            "{}:{}",
265cc290419Sopenharmony_ci            config::LOCAL_HOST,
266cc290419Sopenharmony_ci            port
267cc290419Sopenharmony_ci        ));
268cc290419Sopenharmony_ci    }
269cc290419Sopenharmony_ci
270cc290419Sopenharmony_ci    let ip_str = &arg[..arg.len() - port_len - 1];
271cc290419Sopenharmony_ci    match std::net::IpAddr::from_str(ip_str) {
272cc290419Sopenharmony_ci        Ok(ip_addr) => {
273cc290419Sopenharmony_ci            if ip_addr.is_ipv4() || ip_addr.is_ipv6() {
274cc290419Sopenharmony_ci                Ok(arg)
275cc290419Sopenharmony_ci            } else {
276cc290419Sopenharmony_ci                Err(Error::new(ErrorKind::Other, "-s content ip incorrect"))
277cc290419Sopenharmony_ci            }
278cc290419Sopenharmony_ci        }
279cc290419Sopenharmony_ci        _ => Err(Error::new(ErrorKind::Other, "-s content ip incorrect")),
280cc290419Sopenharmony_ci    }
281cc290419Sopenharmony_ci}
282