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}