sbb_api/
lib.rs

1extern crate openssl;
2extern crate hex;
3extern crate uuid;
4extern crate chrono;
5extern crate percent_encoding;
6
7#[macro_use]
8extern crate serde;
9
10#[macro_use]
11extern crate simple_error;
12
13
14#[cfg(test)]
15#[macro_use]
16extern crate more_asserts;
17
18pub mod authenticator;
19pub mod connections;
20pub mod models;
21
22
23use std::str::FromStr;
24use reqwest::{Certificate, Method, Url, Response};
25use reqwest::header::{HeaderMap, USER_AGENT, HeaderValue, HeaderName};
26use openssl::rand::rand_bytes;
27use chrono::{DateTime, Local};
28
29pub const API_ENDPOINT : &str = "https://p1.sbbmobile.ch";
30//const API_ENDPOINT: &str = "http://127.0.0.1:3000";
31pub const SBB_UA: &str = "SBBmobile/flavorProdRelease-10.8.1 Android/10 (OnePlus;ONEPLUS A5010)";
32
33pub fn set_headers(headers: &mut HeaderMap, path: &str, date: &str){
34    headers.append(HeaderName::from_str("X-App-Token")
35                       .expect("Unable to parse HeaderName"),
36                   HeaderValue::from_str(&generate_token())
37                       .expect("Unable to parse HeaderValue"),
38    );
39    headers.append(HeaderName::from_str("X-API-AUTHORIZATION")
40                       .expect("Unable to parse HeaderName"),
41                   HeaderValue::from_str(
42                       &authenticator::get_authorization(&path, &date)
43                   )
44                       .expect("Unable to parse HeaderValue"),
45    );
46    headers.append(HeaderName::from_str("X-API-DATE")
47                       .expect("Unable to parse HeaderName"),
48                   HeaderValue::from_str(
49                       &date
50                   )
51                       .expect("Unable to parse HeaderValue"),
52    );
53}
54
55async fn make_request(path: &str) -> Result<Response, reqwest::Error> {
56    let mut headers = HeaderMap::new();
57    headers.append(USER_AGENT, HeaderValue::from_str(SBB_UA)
58        .expect("Unable to parse User-Agent as HeaderValue"));
59    let client = reqwest::Client::builder()
60        .add_root_certificate(
61            Certificate::from_der(
62                include_bytes!("../resources/ca_cert.crt"))
63                .expect("Unable to decode certificate")
64        )
65        .default_headers(headers)
66        .build().expect("Unable to create client");
67
68    let local: DateTime<Local> = Local::now();
69    let date = format!("{}", local.format("%Y-%m-%d"));
70
71    let url = Url::parse(&format!("{}{}", API_ENDPOINT, path))
72                    .expect("Unable to parse URL");
73    let mut request = reqwest::Request::new(Method::GET, url);
74    let headers: &mut HeaderMap = request.headers_mut();
75    set_headers(headers, &path, &date);
76    client.execute(request).await
77}
78
79pub fn generate_token() -> String {
80    let mut bytes = [0; 16];
81    let _ = rand_bytes(&mut bytes);
82    uuid::Builder::from_slice(&bytes)
83        .expect("Unable to get UUID from bytes")
84        .build()
85        .to_hyphenated().to_string()
86}
87
88#[cfg(test)]
89mod tests {
90    use super::authenticator;
91    #[test]
92    fn test_path_1() {
93        let path = "/unauth/ticketingservice/zvs/v0/features";
94        assert_eq!(authenticator::get_certificate_hash(), "WdfnzdQugRFUF5b812hZl3lAahM=");
95        assert_eq!(authenticator::get_authorization(path, "2019-09-05"), "wqhPBCfC9oc8gp62FVVIiNIADzg=");
96    }
97
98    /*#[test]
99    fn test_path_a() {
100        let path = "/unauth/fahrplanservice/v1/standorte/ba/";
101        assert_eq!(authenticator::get_authorization(path, "2019-09-23"), "qGkCalmIy1kRb4iJVBDQ/bhAnOQ=");
102    }
103
104
105
106    #[test]
107    fn test_path_b() {
108        let path = "/unauth/fahrplanservice/v1/verbindungen/a/8001 Zürich, Stampfenbachstrasse 1/s/Basel/ab/2019-09-23/22-42/";
109        assert_eq!(authenticator::get_authorization(path, "2019-09-23"), "isgBYXvgx3mlcBiqPWmTAk/2G3o=" );
110    }
111
112    #[test]
113    fn test_path_c() {
114        let path = "/unauth/fahrplanservice/v1/standorte/Basel/";
115        assert_eq!(authenticator::get_authorization(path, "2019-09-23"), "ZwdcDbK99hYBpZ3vhKB9gLOeazw=" );
116    }
117
118    #[test]
119    fn test_path_2() {
120        let path = "/unauth/ticketingservice/zvs/v0/ghettobox/";
121        assert_eq!(authenticator::get_authorization(path, "2019-09-05"), "3fgUyXQoMieevNYULWbo3OPsd4w=");
122    }
123
124    #[test]
125    fn test_path_3() {
126        let path = "/unauth/fahrplanservice/v1/standorte/a%20b/";
127        assert_eq!(authenticator::get_authorization(path, "2019-09-23"), "myZVEyS8WprlVrTIw2ZynzS0Z6I=");
128    }
129
130    #[test]
131    fn test_request_1() {
132        let path = "/unauth/ticketingservice/zvs/v0/features";
133        let mut resp = make_request(path).expect("Invalid request");
134
135        println!("{}", resp.text().expect("Unable to extract text"))
136    }
137
138    #[test]
139    fn test_request_2() {
140        let path = "/unauth/fahrplanservice/v1/standorte/lug/";
141        let mut resp = make_request(path).expect("Invalid request");
142
143        println!("{}", resp.text().expect("Unable to extract text"))
144    }
145
146    #[test]
147    fn test_request_3() {
148        let path = "/unauth/fahrplanservice/v1/standortenearby/47378177/8540193/";
149        let mut resp = make_request(path).expect("Invalid request");
150
151        println!("{}", resp.text().expect("Unable to extract text"))
152    }
153
154    #[test]
155    fn test_request_4() {
156        // yyyy-MM-dd HH-mm
157        let path = "/unauth/v1/shortlink/verbindung/008503000/2019-09-20 08:00/";
158        let mut resp = make_request(path).expect("Invalid request");
159
160        println!("{}", resp.text().expect("Unable to extract text"))
161    }
162
163    #[test]
164    fn test_request_5() {
165        // yyyy-MM-dd HH-mm
166        let path = "/unauth/fahrplanservice/v1/verbindungen/s/Zurich/s/Bern/ab/2019-09-20/10-14/";
167        let mut resp = make_request(path).expect("Invalid request");
168
169        println!("{}", resp.text().expect("Unable to extract text"))
170    }
171
172    #[test]
173    fn test_request_train_formation() {
174        // yyyy-MM-dd HH-mm
175        let path = "/unauth/fahrplanservice/v1/trains/~H4sIAAAAAAAAAN1U227aQBD9FWvhrYB2bYNtKiTAygWladQkSm-qqoUMsMKs0e46aXP5n1bqM099Ij_WWUy4VSlVHgPCuzvMnJ0zMz63hHcnCoSOU2ngmyF1ErMyfe1SFtEIn9RzXwfMd8Iq9SilDv4o81x7DnBLSkRBL5XaqKxnRCpXOLNfs2m71TmeTY3dHh51ZtPzYqvBmieNTw-_legNncN280MjrPp4m9f82PADLwhZLWy-aSzua_IGc8PmIqwNSmJA4HsRc10bUIv8ECPygGAtYJ3A6oB_FTuxwxz8FIusOJsexbNp4WKvQAvxfoFRXFp2f4yPs058XmAF67N02js9QNNh563dx0cFn9lU7taWWvi4UPyGVXsKI_-O3ZVd5gd-EFU9_44WsHIaBmPAcpH651tykw1IXWZJUsItDHLjiBsYpErgiXRiDJF8nO-RA7bFWrLxGBSp46lErkBd88SAitNL6zfvFkOvTPSs6SKVpH5L-qkacyNALT0X5b5fer7lveGTrkHuurwNc_2Cg4AdNZlEGuQA5MOPKyi3Hn6qdJIqgykkQs552HSM4lJb-9EGwa97liOIAUjdG_K-ATkvw4L1KWjDMwy1cKMM1A0kaH5_iseJEghjOODosRLpcoWgCvo420MQaOzzRMN9aQm2z8ci-e7EKRJ1roUZOpOEf3e4Ar6Bvt_aQvf_B72daSFBa-cmleAI6TBtnF7Ctd4Ab396Dvi7TIDZgXz6LGSsMKgrbl9kZ5JqLboJbMJuoYZPo365x5kY2vnQBpJks5UrAVgN567JHKWpuhSSz6filiToZzLr4gcVqxtBgEOWysHCGlZyXcFI3u3zobKiZJWgTKOyS89pWPfcOqUVRH9Fcy3jcpTJvnl8EQcJKiNGeS5ZHFojkyGZ3Lbebz6UCEpp-cygII5gFy3U0B2UWFTzNhkFlVoYeTV_nVGe6BN5_531Rs6opv9R_WBX9WsVK8JudSvXXKT_ketGM6K6G24145FF9W8aVWKHqwv9FNQlKBQd3Zr392TvAr2vcpl7WbOGdHJJfhHte06h_n0jCs4fcP5_Ns4IAAA%3D/formation?tnr=25459&cicle=0&puic=85&abDatum=2019-09-20";
176        let mut resp = make_request(path).expect("Invalid request");
177
178        println!("{}", resp.text().expect("Unable to extract text"))
179    }
180
181    #[test]
182    fn test_request_6() {
183        let path = "/unauth/v1/verbindungen/STATION/8503000/STATION/8505300/{abAn}/{datum}/{zeit}/";
184        let mut resp = make_request(path).expect("Invalid request");
185
186        println!("{}", resp.text().expect("Unable to extract text"))
187    }*/
188}