1pub mod accessory;
11pub mod ad;
12pub mod auth;
13pub mod bookmark;
14mod crypt;
15pub mod device;
16pub mod errors;
17pub mod music;
18pub mod station;
19pub mod test;
20pub mod track;
21pub mod user;
22
23use std::collections::HashMap;
24use std::fmt::Debug;
25
26use serde::{Deserialize, Serialize};
27use serde_json;
28
29use crate::errors::Error;
30use crate::json::auth::{PartnerLogin, PartnerLoginResponse};
31use crate::json::errors::{JsonError, JsonErrorKind};
32
33#[derive(Debug, Clone)]
35pub struct PandoraSession {
36 client: reqwest::Client,
37 endpoint_url: url::Url,
38 tokens: SessionTokens,
39 json: serde_json::value::Value,
40 args: std::collections::BTreeMap<String, String>,
41 encrypted: bool,
42}
43
44impl PandoraSession {
45 pub fn new<T: ToEncryptionTokens, E: ToEndpoint>(
47 client: Option<reqwest::Client>,
48 to_encryption_tokens: &T,
49 to_endpoint: &E,
50 ) -> Self {
51 Self {
52 client: client.unwrap_or_default(),
53 endpoint_url: to_endpoint.to_endpoint_url(),
54 tokens: SessionTokens::new(to_encryption_tokens),
55 json: serde_json::value::Value::Object(serde_json::map::Map::new()),
56 args: std::collections::BTreeMap::new(),
57 encrypted: false,
58 }
59 }
60
61 pub fn copy_session(&self) -> Self {
64 Self {
65 client: self.client.clone(),
66 endpoint_url: self.endpoint_url.clone(),
67 tokens: self.tokens.clone(),
68 json: serde_json::value::Value::Object(serde_json::map::Map::new()),
69 args: std::collections::BTreeMap::new(),
70 encrypted: false,
71 }
72 }
73
74 pub fn http_client(&self) -> &reqwest::Client {
76 &self.client
77 }
78
79 pub fn endpoint<E: ToEndpoint>(&mut self, to_endpoint: E) -> &mut Self {
81 self.endpoint_url = to_endpoint.to_endpoint_url();
82 self
83 }
84
85 pub fn endpoint_mut<E: ToEndpoint>(&mut self) -> &mut url::Url {
87 &mut self.endpoint_url
88 }
89
90 pub fn update_partner_tokens<T: ToPartnerTokens>(&mut self, to_partner_tokens: &T) {
92 self.tokens.update_partner_tokens(to_partner_tokens);
93 }
94
95 pub fn update_user_tokens<T: ToUserTokens>(&mut self, to_user_tokens: &T) {
97 self.tokens.update_user_tokens(to_user_tokens);
98 }
99
100 pub fn session_tokens(&self) -> &SessionTokens {
103 &self.tokens
104 }
105
106 pub fn session_tokens_mut(&mut self) -> &mut SessionTokens {
109 &mut self.tokens
110 }
111
112 pub fn json(&mut self, json: serde_json::value::Value) -> &mut Self {
117 self.json = json;
118 self
119 }
120
121 pub fn json_mut(&mut self) -> &mut serde_json::value::Value {
123 &mut self.json
124 }
125
126 pub fn arg(&mut self, key: &str, value: &str) -> &mut Self {
128 self.args.insert(key.to_string(), value.to_string());
129 self
130 }
131
132 pub fn encrypted(&mut self) -> &mut Self {
135 self.encrypted = true;
136 self
137 }
138
139 fn add_session_tokens_to_args(&mut self) {
141 if let Some(auth_token) = self
143 .tokens
144 .user_token
145 .clone()
146 .or_else(|| self.tokens.partner_token.clone())
147 {
148 self.arg("auth_token", &auth_token);
149 }
150 if let Some(partner_id) = self.tokens.partner_id.clone() {
151 self.arg("partner_id", &partner_id);
152 }
153 if let Some(user_id) = self.tokens.user_id.clone() {
154 self.arg("user_id", &user_id);
155 }
156 }
157
158 fn add_session_tokens_to_json(&mut self) {
160 let json_obj = self
161 .json
162 .as_object_mut()
163 .expect("Programming Error accessing API request json for modification.");
164 if let Some(partner_auth_token) = self.tokens.partner_token.clone() {
165 json_obj.insert(
166 "partnerAuthToken".to_string(),
167 serde_json::Value::String(partner_auth_token),
168 );
169 }
170 if let Some(user_auth_token) = self.tokens.user_token.clone() {
171 json_obj.insert(
172 "userAuthToken".to_string(),
173 serde_json::Value::String(user_auth_token),
174 );
175 }
176
177 if let Some(sync_time) = self.tokens.sync_time {
178 json_obj.insert("syncTime".to_string(), serde_json::Value::from(sync_time));
179 }
180 }
181
182 pub fn build(&mut self) -> reqwest::RequestBuilder {
185 self.add_session_tokens_to_args();
186 let mut url: url::Url = self.endpoint_url.clone();
187 url.query_pairs_mut().extend_pairs(&self.args);
188
189 self.add_session_tokens_to_json();
190 let mut body: String = self.json.to_string();
191 if cfg!(test) {
192 log::debug!("Request body: {:?}", body);
193 }
194 if self.encrypted {
195 body = self.tokens.encrypt(&body);
196 if cfg!(test) {
197 log::debug!("Encrypted body: {:?}", body);
198 }
199 }
200
201 self.client.post(url).body(body)
202 }
203}
204
205#[derive(Debug, Deserialize)]
209#[serde(rename_all = "camelCase")]
210pub struct PandoraResponse<T> {
211 pub stat: PandoraStatus,
213 pub result: Option<T>,
215 pub message: Option<String>,
217 pub code: Option<u32>,
219}
220
221impl<T: serde::de::DeserializeOwned> From<PandoraResponse<T>>
222 for std::result::Result<T, JsonError>
223{
224 fn from(pandora_resp: PandoraResponse<T>) -> Self {
225 match pandora_resp {
226 PandoraResponse {
227 stat: PandoraStatus::Ok,
228 result: Some(result),
229 ..
230 } => Ok(result),
231 PandoraResponse {
232 stat: PandoraStatus::Ok,
233 result: None,
234 ..
235 } => {
236 let default_value = serde_json::json!({});
237 let deser = serde_json::from_value(default_value);
238 deser.map_err(|_| JsonError::new(None, Some(String::from("Invalid JSON content."))))
239 }
240 PandoraResponse { code, message, .. } => Err(JsonError::new(code, message)),
241 }
242 }
243}
244
245#[derive(Debug, Deserialize)]
247#[serde(rename_all = "camelCase")]
248pub enum PandoraStatus {
249 Ok,
251 Fail,
253}
254
255#[async_trait::async_trait]
261pub trait PandoraJsonApiRequest: serde::ser::Serialize {
262 type Response: Debug + serde::de::DeserializeOwned;
264 type Error: Debug
266 + From<serde_json::error::Error>
267 + From<reqwest::Error>
268 + From<JsonError>
269 + Send;
270
271 fn get_method(&self) -> String;
274
275 fn get_json(&self) -> std::result::Result<serde_json::value::Value, Self::Error> {
278 serde_json::to_value(self).map_err(Self::Error::from)
279 }
280
281 fn encrypt_request(&self) -> bool {
284 false
285 }
286
287 fn request(
290 &self,
291 session: &mut PandoraSession,
292 ) -> std::result::Result<reqwest::RequestBuilder, Self::Error> {
293 let mut tmp_session = session.clone();
294 tmp_session
295 .arg("method", &self.get_method())
296 .json(self.get_json()?);
297 if self.encrypt_request() {
298 tmp_session.encrypted();
299 }
300 Ok(tmp_session.build())
301 }
302
303 async fn response(
306 &self,
307 session: &mut PandoraSession,
308 ) -> std::result::Result<Self::Response, Self::Error> {
309 let response = self
310 .request(session)?
311 .send()
312 .await
313 .map_err(Self::Error::from)?;
314
315 let response_obj: PandoraResponse<Self::Response> = if cfg!(test) {
316 let response_body = response.text().await?;
319 if cfg!(test) {
320 log::debug!("Full response: {:?}", response_body);
321 }
322 serde_json::from_slice(response_body.as_bytes())?
323 } else {
324 response.json().await?
326 };
327
328 if cfg!(test) {
329 log::debug!("Json response: {:?}", response_obj);
330 }
331
332 let result: std::result::Result<Self::Response, JsonError> = response_obj.into();
333 match result {
335 Err(JsonError {
336 kind: JsonErrorKind::InvalidAuthToken,
337 message,
338 }) => {
339 session.session_tokens_mut().clear_partner_tokens();
340 session.session_tokens_mut().clear_user_tokens();
341 Err(JsonError {
342 kind: JsonErrorKind::InvalidAuthToken,
343 message,
344 })
345 }
346 Err(JsonError {
347 kind: JsonErrorKind::InsufficientConnectivity,
348 message,
349 }) => {
350 session.session_tokens_mut().clear_partner_tokens();
351 session.session_tokens_mut().clear_user_tokens();
352 Err(JsonError {
353 kind: JsonErrorKind::InsufficientConnectivity,
354 message,
355 })
356 }
357 res => res,
358 }
359 .map_err(Self::Error::from)
360 }
361}
362
363pub trait ToEndpoint: serde::ser::Serialize {
366 fn to_endpoint(&self) -> String;
368 fn to_endpoint_url(&self) -> url::Url {
370 url::Url::parse(&self.to_endpoint()).expect("Error parsing Pandora endpoint url.")
371 }
372}
373
374impl ToEndpoint for String {
375 fn to_endpoint(&self) -> String {
377 self.clone()
378 }
379}
380
381pub trait ToEncryptionTokens {
384 fn to_encrypt_key(&self) -> String;
386 fn encrypt(&self, data: &str) -> String {
388 crypt::encrypt(&self.to_encrypt_key(), data)
389 }
390 fn to_decrypt_key(&self) -> String;
392 fn decrypt(&self, hex_data: &str) -> Vec<u8> {
394 crypt::decrypt(&self.to_decrypt_key(), hex_data)
395 }
396}
397
398pub trait ToPartnerTokens {
401 fn to_partner_id(&self) -> Option<String>;
405
406 fn to_partner_token(&self) -> Option<String>;
410
411 fn to_sync_time(&self) -> Option<String>;
415}
416
417pub trait ToUserTokens {
420 fn to_user_id(&self) -> Option<String>;
424
425 fn to_user_token(&self) -> Option<String>;
429}
430
431pub trait ToStationToken: serde::ser::Serialize {
433 fn to_station_token(&self) -> String;
435}
436
437impl ToStationToken for String {
438 fn to_station_token(&self) -> String {
440 self.clone()
441 }
442}
443
444impl ToStationToken for &str {
445 fn to_station_token(&self) -> String {
447 (*self).to_string()
450 }
451}
452
453pub trait ToTrackingToken: serde::ser::Serialize {
455 fn to_ad_tracking_tokens(&self) -> String;
457}
458
459impl ToTrackingToken for String {
460 fn to_ad_tracking_tokens(&self) -> String {
462 self.clone()
463 }
464}
465
466impl ToTrackingToken for &str {
467 fn to_ad_tracking_tokens(&self) -> String {
469 (*self).to_string()
472 }
473}
474
475#[derive(Debug, Clone, Serialize)]
478#[serde(rename_all = "camelCase")]
479pub struct Partner {
480 pub username: String,
483 pub password: String,
486 pub device_model: String,
488 pub version: String,
490 #[serde(skip)]
492 pub encrypt_password: String,
493 #[serde(skip)]
495 pub decrypt_password: String,
496 #[serde(skip)]
500 pub endpoint_host: String,
501}
502
503impl Partner {
504 pub fn new_android() -> Self {
506 Self {
507 username: "android".to_string(),
508 password: "AC7IBG09A3DTSYM4R41UJWL07VLN8JI7".to_string(),
509 device_model: "android-generic".to_string(),
510 version: "5".to_string(),
511 decrypt_password: "R=U!LH$O2B#".to_string(),
512 encrypt_password: "6#26FRL$ZWD".to_string(),
513 endpoint_host: "tuner.pandora.com".to_string(),
514 }
515 }
516
517 pub fn new_ios() -> Self {
519 Self {
520 username: "iphone".to_string(),
521 password: "P2E4FC0EAD3*878N92B2CDp34I0B1@388137C".to_string(),
522 device_model: "IP01".to_string(),
523 version: "5".to_string(),
524 decrypt_password: "20zE1E47BE57$51".to_string(),
525 encrypt_password: "721^26xE22776".to_string(),
526 endpoint_host: "tuner.pandora.com".to_string(),
527 }
528 }
529
530 pub fn new_palm() -> Self {
532 Self {
533 username: "palm".to_string(),
534 password: "IUC7IBG09A3JTSYM4N11UJWL07VLH8JP0".to_string(),
535 device_model: "pre".to_string(),
536 version: "5".to_string(),
537 decrypt_password: "E#U$MY$O2B=".to_string(),
538 encrypt_password: "%526CBL$ZU3".to_string(),
539 endpoint_host: "tuner.pandora.com".to_string(),
540 }
541 }
542
543 pub fn new_windows_mobile() -> Self {
545 Self {
546 username: "winmo".to_string(),
547 password: "ED227E10a628EB0E8Pm825Dw7114AC39".to_string(),
548 device_model: "VERIZON_MOTOQ9C".to_string(),
549 version: "5".to_string(),
550 decrypt_password: "7D671jt0C5E5d251".to_string(),
551 encrypt_password: "v93C8C2s12E0EBD".to_string(),
552 endpoint_host: "tuner.pandora.com".to_string(),
553 }
554 }
555
556 pub fn new_desktop_air() -> Self {
558 Self {
559 username: "pandora one".to_string(),
560 password: "TVCKIBGS9AO9TSYLNNFUML0743LH82D".to_string(),
561 device_model: "D01".to_string(),
562 version: "5".to_string(),
563 decrypt_password: "U#IO$RZPAB%VX2".to_string(),
564 encrypt_password: "2%3WCL*JU$MP]4".to_string(),
565 endpoint_host: "internal-tuner.pandora.com".to_string(),
566 }
567 }
568
569 pub fn new_vista_widget() -> Self {
571 Self {
572 username: "windowsgadget".to_string(),
573 password: "EVCCIBGS9AOJTSYMNNFUML07VLH8JYP0".to_string(),
574 device_model: "WG01".to_string(),
575 version: "5".to_string(),
576 decrypt_password: "E#IO$MYZOAB%FVR2".to_string(),
577 encrypt_password: "%22CML*ZU$8YXP[1".to_string(),
578 endpoint_host: "internal-tuner.pandora.com".to_string(),
579 }
580 }
581
582 pub fn init_session(&self) -> PandoraSession {
585 PandoraSession::new(None, self, self)
586 }
587
588 pub fn to_partner_login(&self) -> PartnerLogin {
591 PartnerLogin {
592 username: self.username.clone(),
593 password: self.password.clone(),
594 device_model: self.device_model.clone(),
595 version: self.version.clone(),
596 optional: HashMap::new(),
597 }
598 }
599
600 pub async fn login(&self, session: &mut PandoraSession) -> Result<PartnerLoginResponse, Error> {
603 let response = self.to_partner_login().response(session).await?;
604 session.update_partner_tokens(&response);
605 Ok(response)
606 }
607}
608
609impl Default for Partner {
610 fn default() -> Self {
612 Self::new_android()
613 }
614}
615
616impl ToEncryptionTokens for Partner {
617 fn to_encrypt_key(&self) -> String {
618 self.encrypt_password.clone()
619 }
620
621 fn to_decrypt_key(&self) -> String {
622 self.decrypt_password.clone()
623 }
624}
625
626impl ToEndpoint for Partner {
627 fn to_endpoint(&self) -> String {
629 format!("https://{}/services/json", self.endpoint_host)
630 }
631}
632
633#[derive(Debug, Clone)]
636pub struct SessionTokens {
637 pub encrypt_key: String,
639 pub decrypt_key: String,
642 pub partner_id: Option<String>,
644 pub partner_token: Option<String>,
646 sync_time: Option<u64>,
653 local_time_base: Option<std::time::Instant>,
657 pub user_id: Option<String>,
659 pub user_token: Option<String>,
661}
662
663impl SessionTokens {
664 pub fn new<T: ToEncryptionTokens>(to_encryption_tokens: &T) -> Self {
667 Self {
668 encrypt_key: to_encryption_tokens.to_encrypt_key(),
669 decrypt_key: to_encryption_tokens.to_decrypt_key(),
670 partner_id: None,
671 partner_token: None,
672 sync_time: None,
673 local_time_base: None,
674 user_id: None,
675 user_token: None,
676 }
677 }
678
679 pub fn update_partner_tokens<T: ToPartnerTokens>(&mut self, to_partner_tokens: &T) {
682 self.partner_id = to_partner_tokens.to_partner_id();
683 self.partner_token = to_partner_tokens.to_partner_token();
684 if let Some(sync_time) = to_partner_tokens.to_sync_time() {
688 let sync_time_bytes: Vec<u8> =
689 self.decrypt(&sync_time).iter().skip(4).cloned().collect();
690 let sync_time_str = std::str::from_utf8(&sync_time_bytes).unwrap_or("0");
691 self.set_sync_time(sync_time_str.parse::<u64>().unwrap_or(0));
692 }
693 }
694
695 pub fn update_user_tokens<T: ToUserTokens>(&mut self, to_user_tokens: &T) {
698 self.user_id = to_user_tokens.to_user_id();
699 self.user_token = to_user_tokens.to_user_token();
700 }
701
702 pub fn set_sync_time(&mut self, sync_time: u64) {
706 self.local_time_base = Some(std::time::Instant::now());
707 self.sync_time = Some(sync_time);
708 }
709
710 pub fn clear_sync_time(&mut self) {
712 self.local_time_base = None;
713 self.sync_time = None;
714 }
715
716 pub fn get_sync_time(&self) -> Option<u64> {
718 self.sync_time
719 .and_then(|st| self.local_time_base.map(|ltb| ltb.elapsed().as_secs() + st))
720 }
721
722 pub fn clear_partner_tokens(&mut self) {
724 self.partner_id = None;
725 self.partner_token = None;
726 self.clear_sync_time();
727 }
728
729 pub fn clear_user_tokens(&mut self) {
731 self.user_id = None;
732 self.user_token = None;
733 }
734}
735
736impl ToEncryptionTokens for SessionTokens {
737 fn to_encrypt_key(&self) -> String {
739 self.encrypt_key.clone()
740 }
741 fn to_decrypt_key(&self) -> String {
743 self.decrypt_key.clone()
744 }
745}
746
747impl<T: ToEncryptionTokens> From<&T> for SessionTokens {
748 fn from(tokens: &T) -> Self {
749 Self::new(tokens)
750 }
751}
752
753#[derive(Debug, Clone, Deserialize)]
755#[serde(rename_all = "camelCase")]
756pub struct Timestamp {
757 _timezone_offset: u32,
759 time: i64,
761 _year: u32,
763 _month: u8,
765 _day: u8,
767 _hours: u8,
769 _minutes: u8,
771 _seconds: u8,
773 _date: u8,
775}
776
777impl From<Timestamp> for chrono::DateTime<chrono::Utc> {
778 fn from(ts: Timestamp) -> chrono::DateTime<chrono::Utc> {
779 let dt = chrono::DateTime::from_timestamp(ts.time, 0).expect("Invalid date/time timestamp");
785 let naive_utc = dt.naive_utc();
786 let offset = *dt.offset();
787 chrono::DateTime::from_naive_utc_and_offset(naive_utc, offset)
788 }
789}
790
791#[cfg(test)]
792mod tests {
793 use super::*;
794
795 use crate::errors::Error;
796 use crate::json::auth::user_login;
797
798 pub async fn session_login(partner: &Partner) -> Result<PandoraSession, Error> {
804 let mut session = partner.init_session();
805 let _partner_login = partner.login(&mut session).await?;
806
807 let test_username_raw = include_str!("../../test_username.txt");
808 let test_username = test_username_raw.trim();
809 let test_password_raw = include_str!("../../test_password.txt");
810 let test_password = test_password_raw.trim();
811
812 let user_login = user_login(&mut session, &test_username, &test_password).await?;
813 session.update_user_tokens(&user_login);
814 Ok(session)
815 }
816
817 #[tokio::test]
818 async fn partner_test() {
819 let partner = Partner::default();
820 let mut session = partner.init_session();
821 let partner_login = partner
822 .login(&mut session)
823 .await
824 .expect("Failed while performing partner login");
825 session.update_partner_tokens(&partner_login);
826 }
827}