1use crate::{
2 config::{CertificateConfig, PrintConfig},
3 error::EricError,
4 error_code::ErrorCode,
5 response::{EricApiPayload, EricResponse, ResponseBuffer},
6 utils::ToCString,
7 ProcessingFlag,
8};
9use anyhow::{anyhow, Context};
10use eric_bindings::{
11 EricBearbeiteVorgang, EricBeende, EricCheckXML, EricDekodiereDaten, EricEntladePlugins,
12 EricHoleFehlerText, EricHoleZertifikatEigenschaften, EricInitialisiere,
13};
14use std::{path::Path, ptr};
15use tracing::{debug, error, info};
16
17pub struct Eric;
21
22impl Eric {
23 pub fn new(log_path: Option<&Path>, plugin_path: Option<&Path>) -> Result<Self, EricError> {
29 info!("Initializing eric");
30
31 if let Some(log_path) = log_path {
32 info!(log_path = %log_path.display(), "Setting log path");
33 info!(log_file = %log_path.join("eric.log").display(), "Logging to file");
34 } else {
35 info!("No log path provided, using ERiC default temporary directory");
36 }
37
38 if let Some(plugin_path) = plugin_path {
39 info!(plugin_path = %plugin_path.display(), "Setting plugin path");
40 } else {
41 info!("No plugin path provided, using ERiC default plugin directory");
42 }
43
44 let plugin_path_cstring = plugin_path
46 .map(|plugin_path| plugin_path.try_to_cstring())
47 .transpose()
48 .context("failed to convert plugin path to CString")?;
49 let plugin_ptr = plugin_path_cstring
50 .as_deref()
51 .map_or(ptr::null(), |cstr| cstr.as_ptr());
52
53 let log_path_cstring = log_path
55 .map(|path| path.try_to_cstring())
56 .transpose()
57 .context("failed to convert log path to CString")?;
58 let log_path_ptr = log_path_cstring
59 .as_deref()
60 .map_or(ptr::null(), |cstr| cstr.as_ptr());
61
62 let error_code = unsafe { EricInitialisiere(plugin_ptr, log_path_ptr) };
63
64 match error_code {
65 x if x == ErrorCode::ERIC_OK as i32 => Ok(Eric),
66 error_code => Err(EricError::Internal(anyhow!(
67 "Can't init eric: {}",
68 error_code
69 ))),
70 }
71 }
72
73 pub fn validate(
77 &self,
78 xml: String,
79 taxonomy_type: &str,
80 taxonomy_version: &str,
81 pdf_path: Option<&str>,
82 ) -> Result<EricResponse, EricError> {
83 let processing_flag: ProcessingFlag;
84 let type_version = format!("{}_{}", taxonomy_type, taxonomy_version);
85 let print_config = if let Some(pdf_path) = pdf_path {
86 processing_flag = ProcessingFlag::Print;
87 Some(PrintConfig::new(pdf_path, &processing_flag)?)
88 } else {
89 processing_flag = ProcessingFlag::Validate;
90 None
91 };
92 Self::process(xml, type_version, processing_flag, print_config, None)
93 }
94
95 pub fn send(
102 &self,
103 xml: String,
104 taxonomy_type: &str,
105 taxonomy_version: &str,
106 certificate_path: &Path,
107 certificate_password: &str,
108 pdf_path: Option<&str>,
109 ) -> Result<EricResponse, EricError> {
110 let certificate_path = certificate_path
111 .to_str()
112 .context("failed to convert path to string")?;
113 let processing_flag: ProcessingFlag;
114 let type_version = format!("{}_{}", taxonomy_type, taxonomy_version);
115 let print_config = if let Some(pdf_path) = pdf_path {
116 processing_flag = ProcessingFlag::SendAndPrint;
117 Some(PrintConfig::new(pdf_path, &processing_flag)?)
118 } else {
119 processing_flag = ProcessingFlag::Send;
120 None
121 };
122 let certificate_config = CertificateConfig::new(certificate_path, certificate_password)?;
123 Self::process(
124 xml,
125 type_version,
126 processing_flag,
127 print_config,
128 Some(certificate_config),
129 )
130 }
131
132 pub fn check_xml(
141 &self,
142 xml: String,
143 taxonomy_type: &str,
144 taxonomy_version: &str,
145 ) -> Result<EricResponse, EricError> {
146 let type_version = format!("{}_{}", taxonomy_type, taxonomy_version);
147 let xml = xml.try_to_cstring()?;
148 let type_version = type_version.try_to_cstring()?;
149
150 let validation_response_buffer = ResponseBuffer::new()?;
151
152 let error_code = unsafe {
153 EricCheckXML(
154 xml.as_ptr(),
155 type_version.as_ptr(),
156 validation_response_buffer.as_ptr(),
157 )
158 };
159
160 let validation_response = validation_response_buffer.read()?;
161 let payload = EricApiPayload::new(validation_response.to_string(), String::new());
162
163 if error_code == ErrorCode::ERIC_OK as i32 {
164 Ok(EricResponse::new(payload))
165 } else {
166 let response_buffer = ResponseBuffer::new()?;
167
168 unsafe {
169 EricHoleFehlerText(error_code, response_buffer.as_ptr());
170 }
171
172 let error_text = response_buffer.read()?;
173
174 Err(EricError::ApiError {
175 code: error_code,
176 message: error_text.to_string(),
177 payload,
178 })
179 }
180 }
181
182 pub fn get_error_text(&self, error_code: i32) -> Result<String, EricError> {
184 let response_buffer = ResponseBuffer::new()?;
185
186 unsafe {
187 EricHoleFehlerText(error_code, response_buffer.as_ptr());
188 }
189
190 Ok(response_buffer.read()?.to_string())
191 }
192
193 pub fn certificate_properties(
204 &self,
205 certificate_path: &Path,
206 pin: &str,
207 ) -> Result<String, EricError> {
208 let path = certificate_path
209 .to_str()
210 .context("failed to convert path to string")?
211 .try_to_cstring()?;
212 let pin = pin.try_to_cstring()?;
213 let certificate = crate::certificate::Certificate::new(&path)?;
214
215 let response_buffer = ResponseBuffer::new()?;
216
217 let error_code = unsafe {
218 EricHoleZertifikatEigenschaften(
219 certificate.handle,
220 pin.as_ptr(),
221 response_buffer.as_ptr(),
222 )
223 };
224
225 if error_code == ErrorCode::ERIC_OK as i32 {
226 Ok(response_buffer.read()?.to_string())
227 } else {
228 let error_text = self
229 .get_error_text(error_code)
230 .unwrap_or_else(|_| String::new());
231 Err(EricError::ApiError {
232 code: error_code,
233 message: error_text,
234 payload: EricApiPayload::new(String::new(), String::new()),
235 })
236 }
237 }
238
239 #[allow(dead_code)]
240 fn decrypt(
241 &self,
242 encrypted_file: &str,
243 certificate_config: CertificateConfig,
244 ) -> Result<i32, EricError> {
245 let encrypted_data = encrypted_file.try_to_cstring()?;
246 let response_buffer = ResponseBuffer::new()?;
247
248 let error_code = unsafe {
249 EricDekodiereDaten(
250 certificate_config.certificate.handle,
251 certificate_config.password.as_ptr(),
252 encrypted_data.as_ptr(),
253 response_buffer.as_ptr(),
254 )
255 };
256
257 Ok(error_code)
258 }
259
260 fn process(
261 xml: String,
262 type_version: String,
263 processing_flag: ProcessingFlag,
264 print_config: Option<PrintConfig>,
265 certificate_config: Option<CertificateConfig>,
266 ) -> Result<EricResponse, EricError> {
267 debug!("Processing xml file");
268
269 match processing_flag {
270 ProcessingFlag::Validate => debug!("Validating xml file"),
271 ProcessingFlag::Print => debug!("Validating xml file"),
272 ProcessingFlag::Send => debug!("Sending xml file"),
273 ProcessingFlag::SendAndPrint => debug!("Send and print"),
274 ProcessingFlag::CheckHints => debug!("Check hints"),
275 ProcessingFlag::ValidateWithoutDate => debug!("Validate without release date"),
276 }
277
278 let xml = xml.try_to_cstring()?;
279 let type_version = type_version.try_to_cstring()?;
280
281 if let Some(print_config) = &print_config {
282 info!(
283 pdf_path = %print_config
284 .pdf_path
285 .to_str()
286 .context("failed to convert path to string")?,
287 "Printing confirmation to file"
288 )
289 }
290
291 let validation_response_buffer = ResponseBuffer::new()?;
292 let server_response_buffer = ResponseBuffer::new()?;
293
294 let error_code = unsafe {
295 EricBearbeiteVorgang(
296 xml.as_ptr(),
297 type_version.as_ptr(),
298 processing_flag.into_u32(),
299 match &print_config {
303 Some(el) => el.print_parameter.as_ptr(),
304 None => ptr::null(),
305 },
306 match &certificate_config {
310 Some(config) => config.certificate_parameter.as_ptr(),
311 None => ptr::null(),
312 },
313 validation_response_buffer.as_ptr(),
314 server_response_buffer.as_ptr(),
315 )
316 };
317
318 let validation_response = validation_response_buffer.read()?;
319 let server_response = server_response_buffer.read()?;
321 let payload =
322 EricApiPayload::new(validation_response.to_string(), server_response.to_string());
323
324 if error_code == ErrorCode::ERIC_OK as i32 {
325 Ok(EricResponse::new(payload))
326 } else {
327 let response_buffer = ResponseBuffer::new()?;
328
329 unsafe {
330 EricHoleFehlerText(error_code, response_buffer.as_ptr());
331 }
332
333 let error_text = response_buffer.read()?;
334
335 Err(EricError::ApiError {
336 code: error_code,
337 message: error_text.to_string(),
338 payload,
339 })
340 }
341 }
342}
343
344impl Drop for Eric {
345 fn drop(&mut self) {
346 info!("Closing eric");
347
348 let error_code = unsafe { EricEntladePlugins() };
349
350 if error_code != ErrorCode::ERIC_OK as i32 {
351 error!(error_code = %error_code, "Error while unloading plugins");
352 }
353
354 let error_code = unsafe { EricBeende() };
355
356 if error_code != ErrorCode::ERIC_OK as i32 {
357 error!(error_code = %error_code, "Can't close eric");
358 }
359 }
360}