1use jwt::{SignWithKey, VerifyWithKey};
2
3use super::http_parser::Request;
4pub use expedite::datetime::period::Period;
5pub use expedite::datetime::time::Time;
6use hmac::Hmac;
7use sha2::Sha256;
8use std::collections::{BTreeMap, HashMap};
9use std::str::FromStr;
10use std::sync::Arc;
11
12use chrono::TimeZone;
13
14use chrono_tz::GMT;
15use chrono_tz::US::Pacific;
16
17pub struct Cookie {
18 name: String,
19 path: String,
20 domain: String,
21 max_age: Option<Time>,
22 http_only: bool,
23 data: BTreeMap<String, String>,
24 secret_key: Arc<Hmac<Sha256>>,
25}
26
27fn cookie_str_to_map(s: &str) -> HashMap<&str, &str> {
28 let mut map = HashMap::new();
29 for e in s.split(";") {
30 match e.split_once("=") {
31 Some((k, v)) => {
32 map.insert(k.trim(), v.trim());
33 }
34 None => {
35 continue;
36 }
37 }
38 }
39 map
40}
41
42impl Cookie {
43 pub fn new(name: String, req: &Request) -> Self {
47 let key = &*req.get_secret_key();
48 let path = req.url_to_path();
49 match req.get_header("Cookie") {
50 Some(s) => match cookie_str_to_map(s).get(name.as_str()) {
51 Some(&token) => {
52 match token.verify_with_key(key) {
53 Ok(x) => {
54 return Cookie {
56 name,
57 path: String::from(path),
58 domain: String::new(),
59 max_age: None,
60 http_only: false,
61 data: x,
62 secret_key: req.get_secret_key(),
63 };
64 }
65 Err(_) => {}
66 }
67 }
68 None => {}
69 },
70 None => {}
71 }
72 Cookie {
73 name,
74 path: String::from(path),
75 domain: String::new(),
76 max_age: None,
77 http_only: false,
78 data: BTreeMap::new(),
79 secret_key: req.get_secret_key(),
80 }
81 }
82
83 pub fn set_name(&mut self, name: String) {
85 self.name = name;
86 }
87 pub fn get_name(&self) -> &String {
89 &self.name
90 }
91 pub fn set_path(&mut self, path: String) {
93 self.path = path
94 }
95 pub fn get_path(&self) -> &String {
97 &self.path
98 }
99 pub fn set_domain(&mut self, domain: String) {
101 self.domain = domain;
102 }
103 pub fn get_domain(&self) -> &String {
105 &self.domain
106 }
107
108 pub fn set_max_age(&mut self, time: Time) {
110 self.max_age = Some(time);
111 }
112
113 pub fn get_max_age(&self) -> &Option<Time> {
115 &self.max_age
116 }
117
118 pub fn set_http_only(&mut self, v: bool) {
120 self.http_only = v;
121 }
122 pub fn get_http_only(&self) -> bool {
124 self.http_only
125 }
126
127 pub fn get_data<T: FromStr>(&self, k: String) -> Option<T> {
129 match self.data.get(&k) {
130 Some(v) => match v.parse() {
131 Ok(v) => {
132 return Some(v);
133 }
134 Err(_) => {
135 return None;
136 }
137 },
138 None => {
139 return None;
140 }
141 }
142 }
143
144 pub fn insert<T: ToString>(&mut self, k: String, v: T) {
146 self.data.insert(k, v.to_string());
147 }
148
149 pub fn gen_token(&self) -> Option<String> {
151 match self.data.clone().sign_with_key(&*self.secret_key) {
152 Ok(s) => Some(s),
153 Err(_) => None,
154 }
155 }
156
157 pub(crate) fn to_string(&self) -> Option<String> {
158 let time = if let Some(ref t) = self.max_age {
159 #[allow(deprecated)]
160 let time = Pacific
161 .ymd(t.date.year as i32, t.date.month, t.date.day)
162 .and_hms(t.hours, t.minutes, t.seconds);
163 let gmt = time.with_timezone(&GMT);
164 gmt.to_rfc2822()
165 } else {
166 String::from("Session")
167 };
168 let domain = {
169 if self.domain == "" {
170 "".to_string()
171 } else {
172 format!("domain={};", self.domain)
173 }
174 };
175 let http_only = {
176 if self.http_only {
177 "HttpOnly;"
178 } else {
179 ""
180 }
181 };
182 if let Some(token) = self.gen_token() {
183 let r = format!(
184 "{name}={token}; path={path}; {domain} Expires={time}; {http_only}",
185 name = self.name,
186 path = self.path,
187 time = time.to_string()
188 );
189 Some(r)
190 } else {
191 None
192 }
193 }
194}