diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 2d654ba03..ca255ae2b 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -1043,7 +1043,7 @@ pub unsafe extern "C" fn dc_send_locations_to_chat( assert!(!context.is_null()); let context = &*context; - location::dc_send_locations_to_chat(context, chat_id, seconds as i64) + location::send_locations_to_chat(context, chat_id, seconds as i64) } #[no_mangle] @@ -1054,7 +1054,7 @@ pub unsafe extern "C" fn dc_is_sending_locations_to_chat( assert!(!context.is_null()); let context = &*context; - location::dc_is_sending_locations_to_chat(context, chat_id) as libc::c_int + location::is_sending_locations_to_chat(context, chat_id) as libc::c_int } #[no_mangle] @@ -1067,7 +1067,7 @@ pub unsafe extern "C" fn dc_set_location( assert!(!context.is_null()); let context = &*context; - location::dc_set_location(context, latitude, longitude, accuracy) + location::set(context, latitude, longitude, accuracy) } #[no_mangle] @@ -1081,7 +1081,7 @@ pub unsafe extern "C" fn dc_get_locations( assert!(!context.is_null()); let context = &*context; - let res = location::dc_get_locations( + let res = location::get_range( context, chat_id, contact_id, @@ -1096,7 +1096,7 @@ pub unsafe extern "C" fn dc_delete_all_locations(context: *mut dc_context_t) { assert!(!context.is_null()); let context = &*context; - location::dc_delete_all_locations(context); + location::delete_all(context); } // dc_array_t diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index 3cded24ff..170870881 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -13,7 +13,7 @@ use deltachat::dc_receive_imf::*; use deltachat::dc_tools::*; use deltachat::error::Error; use deltachat::job::*; -use deltachat::location::*; +use deltachat::location; use deltachat::lot::LotState; use deltachat::message::*; use deltachat::peerstate::*; @@ -652,7 +652,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E ); } } - if dc_is_sending_locations_to_chat(context, 0 as uint32_t) { + if location::is_sending_locations_to_chat(context, 0 as uint32_t) { info!(context, 0, "Location streaming enabled."); } println!("{} chats", cnt); @@ -778,14 +778,17 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E println!( "{} contacts\nLocation streaming: {}", contacts.len(), - dc_is_sending_locations_to_chat(context, sel_chat.as_ref().unwrap().get_id()), + location::is_sending_locations_to_chat( + context, + sel_chat.as_ref().unwrap().get_id() + ), ); } "getlocations" => { ensure!(sel_chat.is_some(), "No chat selected."); let contact_id = arg1.parse().unwrap_or_default(); - let locations = dc_get_locations( + let locations = location::get_range( context, sel_chat.as_ref().unwrap().get_id(), contact_id, @@ -819,7 +822,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E ensure!(!arg1.is_empty(), "No timeout given."); let seconds = arg1.parse()?; - dc_send_locations_to_chat(context, sel_chat.as_ref().unwrap().get_id(), seconds); + location::send_locations_to_chat(context, sel_chat.as_ref().unwrap().get_id(), seconds); println!( "Locations will be sent to Chat#{} for {} seconds. Use 'setlocation ' to play around.", sel_chat.as_ref().unwrap().get_id(), @@ -834,7 +837,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E let latitude = arg1.parse()?; let longitude = arg2.parse()?; - let continue_streaming = dc_set_location(context, latitude, longitude, 0.); + let continue_streaming = location::set(context, latitude, longitude, 0.); if 0 != continue_streaming { println!("Success, streaming should be continued."); } else { @@ -842,7 +845,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E } } "dellocations" => { - dc_delete_all_locations(context); + location::delete_all(context)?; } "send" => { ensure!(sel_chat.is_some(), "No chat selected."); diff --git a/src/dc_mimefactory.rs b/src/dc_mimefactory.rs index fc146214c..0b5622548 100644 --- a/src/dc_mimefactory.rs +++ b/src/dc_mimefactory.rs @@ -19,7 +19,7 @@ use crate::dc_e2ee::*; use crate::dc_strencode::*; use crate::dc_tools::*; use crate::error::Error; -use crate::location::*; +use crate::location; use crate::message::*; use crate::param::*; use crate::stock::StockMessage; @@ -883,8 +883,11 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i .param .get_float(Param::SetLongitude) .unwrap_or_default(); - let kml_file = - dc_get_message_kml(factory.msg.timestamp_sort, latitude, longitude); + let kml_file = location::get_message_kml( + factory.msg.timestamp_sort, + latitude, + longitude, + ); let content_type = mailmime_content_new_with_str( b"application/vnd.google-earth.kml+xml\x00" as *const u8 as *const libc::c_char, @@ -899,9 +902,12 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i mailmime_smart_add_part(message, kml_mime_part); } - if dc_is_sending_locations_to_chat(factory.msg.context, factory.msg.chat_id) { + if location::is_sending_locations_to_chat( + factory.msg.context, + factory.msg.chat_id, + ) { if let Ok((kml_file, last_added_location_id)) = - dc_get_location_kml(factory.msg.context, factory.msg.chat_id) + location::get_kml(factory.msg.context, factory.msg.chat_id) { let content_type = mailmime_content_new_with_str( b"application/vnd.google-earth.kml+xml\x00" as *const u8 diff --git a/src/dc_mimeparser.rs b/src/dc_mimeparser.rs index 8109f8673..8a208a5b3 100644 --- a/src/dc_mimeparser.rs +++ b/src/dc_mimeparser.rs @@ -17,7 +17,7 @@ use crate::dc_e2ee::*; use crate::dc_simplify::*; use crate::dc_strencode::*; use crate::dc_tools::*; -use crate::location::*; +use crate::location; use crate::param::*; use crate::stock::StockMessage; use crate::types::*; @@ -57,8 +57,8 @@ pub struct dc_mimeparser_t<'a> { pub context: &'a Context, pub reports: Vec<*mut mailmime>, pub is_system_message: libc::c_int, - pub location_kml: Option, - pub message_kml: Option, + pub location_kml: Option, + pub message_kml: Option, } // deprecated: flag to switch generation of compound messages on and off. @@ -1297,7 +1297,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known( let d = dc_null_terminate(decoded_data, decoded_data_bytes as i32); mimeparser.location_kml = - dc_kml_parse(mimeparser.context, as_str(d)).ok(); + location::Kml::parse(mimeparser.context, as_str(d)).ok(); free(d.cast()); } } else if strncmp( @@ -1317,7 +1317,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known( let d = dc_null_terminate(decoded_data, decoded_data_bytes as i32); mimeparser.message_kml = - dc_kml_parse(mimeparser.context, as_str(d)).ok(); + location::Kml::parse(mimeparser.context, as_str(d)).ok(); free(d.cast()); } } else { diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 43d3add6a..f3764d45b 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -21,7 +21,7 @@ use crate::dc_strencode::*; use crate::dc_tools::*; use crate::error::Result; use crate::job::*; -use crate::location::*; +use crate::location; use crate::message::*; use crate::param::*; use crate::peerstate::*; @@ -938,17 +938,16 @@ unsafe fn save_locations( let mut send_event = false; if !mime_parser.message_kml.is_none() && chat_id > DC_CHAT_ID_LAST_SPECIAL as libc::c_uint { - let newest_location_id: uint32_t = dc_save_locations( - context, - chat_id, - from_id, - &mime_parser.message_kml.as_ref().unwrap().locations, - 1, - ); - if 0 != newest_location_id && 0 == hidden { - dc_set_msg_location_id(context, insert_msg_id, newest_location_id); - location_id_written = true; - send_event = true; + if let Some(ref locations) = mime_parser.message_kml.as_ref().unwrap().locations { + let newest_location_id = + location::save(context, chat_id, from_id, locations, 1).unwrap_or_default(); + if 0 != newest_location_id && 0 == hidden { + if location::set_msg_location_id(context, insert_msg_id, newest_location_id).is_ok() + { + location_id_written = true; + send_event = true; + } + } } } @@ -958,17 +957,23 @@ unsafe fn save_locations( if !contact.get_addr().is_empty() && contact.get_addr().to_lowercase() == addr.to_lowercase() { - let newest_location_id = dc_save_locations( - context, - chat_id, - from_id, - &mime_parser.location_kml.as_ref().unwrap().locations, - 0, - ); - if newest_location_id != 0 && hidden == 0 && !location_id_written { - dc_set_msg_location_id(context, insert_msg_id, newest_location_id); + if let Some(ref locations) = + mime_parser.location_kml.as_ref().unwrap().locations + { + let newest_location_id = + location::save(context, chat_id, from_id, locations, 0) + .unwrap_or_default(); + if newest_location_id != 0 && hidden == 0 && !location_id_written { + if let Err(err) = location::set_msg_location_id( + context, + insert_msg_id, + newest_location_id, + ) { + error!(context, 0, "Failed to set msg_location_id: {:?}", err); + } + } + send_event = true; } - send_event = true; } } } diff --git a/src/job.rs b/src/job.rs index e7286c711..0b42c5945 100644 --- a/src/job.rs +++ b/src/job.rs @@ -14,7 +14,7 @@ use crate::dc_loginparam::*; use crate::dc_mimefactory::*; use crate::dc_tools::*; use crate::imap::*; -use crate::location::*; +use crate::location; use crate::message::*; use crate::param::*; use crate::sql; @@ -739,13 +739,19 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int { chat::set_gossiped_timestamp(context, mimefactory.msg.chat_id, time()); } if 0 != mimefactory.out_last_added_location_id { - dc_set_kml_sent_timestamp(context, mimefactory.msg.chat_id, time()); + if let Err(err) = + location::set_kml_sent_timestamp(context, mimefactory.msg.chat_id, time()) + { + error!(context, 0, "Failed to set kml sent_timestamp: {:?}", err); + } if !mimefactory.msg.hidden { - dc_set_msg_location_id( + if let Err(err) = location::set_msg_location_id( context, mimefactory.msg.id, mimefactory.out_last_added_location_id, - ); + ) { + error!(context, 0, "Failed to set msg_location_id: {:?}", err); + } } } if 0 != mimefactory.out_encrypted @@ -875,9 +881,11 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) { Action::SendMdn => job.do_DC_JOB_SEND(context), Action::ConfigureImap => unsafe { dc_job_do_DC_JOB_CONFIGURE_IMAP(context, &job) }, Action::ImexImap => unsafe { dc_job_do_DC_JOB_IMEX_IMAP(context, &job) }, - Action::MaybeSendLocations => dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context, &job), + Action::MaybeSendLocations => { + location::job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context, &job) + } Action::MaybeSendLocationsEnded => { - dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context, &mut job) + location::job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context, &mut job) } Action::Housekeeping => sql::housekeeping(context), Action::SendMdnOld => {} diff --git a/src/location.rs b/src/location.rs index b218d39f7..abfd4101f 100644 --- a/src/location.rs +++ b/src/location.rs @@ -47,15 +47,147 @@ impl Kml { pub fn new() -> Self { Default::default() } + + pub fn parse(context: &Context, content: impl AsRef) -> Result { + ensure!( + content.as_ref().len() <= (1 * 1024 * 1024), + "A kml-files with {} bytes is larger than reasonably expected.", + content.as_ref().len() + ); + + let mut reader = quick_xml::Reader::from_str(content.as_ref()); + reader.trim_text(true); + + let mut kml = Kml::new(); + kml.locations = Some(Vec::with_capacity(100)); + + let mut buf = Vec::new(); + + loop { + match reader.read_event(&mut buf) { + Ok(quick_xml::events::Event::Start(ref e)) => kml.starttag_cb(e, &reader), + Ok(quick_xml::events::Event::End(ref e)) => kml.endtag_cb(e), + Ok(quick_xml::events::Event::Text(ref e)) => kml.text_cb(e, &reader), + Err(e) => { + error!( + context, + 0, + "Location parsing: Error at position {}: {:?}", + reader.buffer_position(), + e + ); + } + Ok(quick_xml::events::Event::Eof) => break, + _ => (), + } + buf.clear(); + } + + Ok(kml) + } + + fn text_cb(&mut self, event: &BytesText, reader: &quick_xml::Reader) { + if 0 != self.tag & (0x4 | 0x10) { + let val = event.unescape_and_decode(reader).unwrap_or_default(); + + let val = val + .replace("\n", "") + .replace("\r", "") + .replace("\t", "") + .replace(" ", ""); + + if 0 != self.tag & 0x4 && val.len() >= 19 { + // YYYY-MM-DDTHH:MM:SSZ + // 0 4 7 10 13 16 19 + match chrono::NaiveDateTime::parse_from_str(&val, "%Y-%m-%dT%H:%M:%SZ") { + Ok(res) => { + self.curr.timestamp = res.timestamp(); + if self.curr.timestamp > time() { + self.curr.timestamp = time(); + } + } + Err(_err) => { + self.curr.timestamp = time(); + } + } + } else if 0 != self.tag & 0x10 { + let parts = val.splitn(2, ',').collect::>(); + if parts.len() == 2 { + self.curr.longitude = parts[0].parse().unwrap_or_default(); + self.curr.latitude = parts[1].parse().unwrap_or_default(); + } + } + } + } + + fn endtag_cb(&mut self, event: &BytesEnd) { + let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase(); + + if tag == "placemark" { + if 0 != self.tag & 0x1 + && 0 != self.curr.timestamp + && 0. != self.curr.latitude + && 0. != self.curr.longitude + { + if let Some(ref mut locations) = self.locations { + locations.push(std::mem::replace(&mut self.curr, Location::new())); + } + } + self.tag = 0 + }; + } + + fn starttag_cb( + &mut self, + event: &BytesStart, + reader: &quick_xml::Reader, + ) { + let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase(); + if tag == "document" { + if let Some(addr) = event.attributes().find(|attr| { + attr.as_ref() + .map(|a| String::from_utf8_lossy(a.key).trim().to_lowercase() == "addr") + .unwrap_or_default() + }) { + self.addr = addr.unwrap().unescape_and_decode_value(reader).ok(); + } + } else if tag == "placemark" { + self.tag = 0x1; + self.curr.timestamp = 0; + self.curr.latitude = 0.0; + self.curr.longitude = 0.0; + self.curr.accuracy = 0.0 + } else if tag == "timestamp" && 0 != self.tag & 0x1 { + self.tag = 0x1 | 0x2 + } else if tag == "when" && 0 != self.tag & 0x2 { + self.tag = 0x1 | 0x2 | 0x4 + } else if tag == "point" && 0 != self.tag & 0x1 { + self.tag = 0x1 | 0x8 + } else if tag == "coordinates" && 0 != self.tag & 0x8 { + self.tag = 0x1 | 0x8 | 0x10; + if let Some(acc) = event.attributes().find(|attr| { + attr.as_ref() + .map(|a| String::from_utf8_lossy(a.key).trim().to_lowercase() == "accuracy") + .unwrap_or_default() + }) { + let v = acc + .unwrap() + .unescape_and_decode_value(reader) + .unwrap_or_default(); + + self.curr.accuracy = v.trim().parse().unwrap_or_default(); + } + } + } } // location streaming -pub fn dc_send_locations_to_chat(context: &Context, chat_id: u32, seconds: i64) { +pub fn send_locations_to_chat(context: &Context, chat_id: u32, seconds: i64) { let now = time(); let mut msg: Message; let is_sending_locations_before: bool; if !(seconds < 0 || chat_id <= 9i32 as libc::c_uint) { - is_sending_locations_before = dc_is_sending_locations_to_chat(context, chat_id); + is_sending_locations_before = is_sending_locations_to_chat(context, chat_id); if sql::execute( context, &context.sql, @@ -101,9 +233,6 @@ pub fn dc_send_locations_to_chat(context: &Context, chat_id: u32, seconds: i64) } } -/******************************************************************************* - * job to send locations out to all chats that want them - ******************************************************************************/ #[allow(non_snake_case)] fn schedule_MAYBE_SEND_LOCATIONS(context: &Context, flags: i32) { if 0 != flags & 0x1 || !job_action_exists(context, Action::MaybeSendLocations) { @@ -111,7 +240,7 @@ fn schedule_MAYBE_SEND_LOCATIONS(context: &Context, flags: i32) { }; } -pub fn dc_is_sending_locations_to_chat(context: &Context, chat_id: u32) -> bool { +pub fn is_sending_locations_to_chat(context: &Context, chat_id: u32) -> bool { context .sql .exists( @@ -121,12 +250,7 @@ pub fn dc_is_sending_locations_to_chat(context: &Context, chat_id: u32) -> bool .unwrap_or_default() } -pub fn dc_set_location( - context: &Context, - latitude: f64, - longitude: f64, - accuracy: f64, -) -> libc::c_int { +pub fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64) -> libc::c_int { if latitude == 0.0 && longitude == 0.0 { return 1; } @@ -162,7 +286,7 @@ pub fn dc_set_location( ).unwrap_or_default() } -pub fn dc_get_locations( +pub fn get_range( context: &Context, chat_id: u32, contact_id: u32, @@ -229,15 +353,13 @@ fn is_marker(txt: &str) -> bool { txt.len() == 1 && txt.chars().next().unwrap() != ' ' } -pub fn dc_delete_all_locations(context: &Context) -> bool { - if sql::execute(context, &context.sql, "DELETE FROM locations;", params![]).is_err() { - return false; - } +pub fn delete_all(context: &Context) -> Result<(), Error> { + sql::execute(context, &context.sql, "DELETE FROM locations;", params![])?; context.call_cb(Event::LOCATION_CHANGED, 0, 0); - true + Ok(()) } -pub fn dc_get_location_kml(context: &Context, chat_id: u32) -> Result<(String, u32), Error> { +pub fn get_kml(context: &Context, chat_id: u32) -> Result<(String, u32), Error> { let now = time(); let mut location_count = 0; let mut ret = String::new(); @@ -313,7 +435,7 @@ fn get_kml_timestamp(utc: i64) -> String { .to_string() } -pub fn dc_get_message_kml(timestamp: i64, latitude: f64, longitude: f64) -> String { +pub fn get_message_kml(timestamp: i64, latitude: f64, longitude: f64) -> String { format!( "\n\ \n\ @@ -330,222 +452,84 @@ pub fn dc_get_message_kml(timestamp: i64, latitude: f64, longitude: f64) -> Stri ) } -pub fn dc_set_kml_sent_timestamp(context: &Context, chat_id: u32, timestamp: i64) -> bool { +pub fn set_kml_sent_timestamp( + context: &Context, + chat_id: u32, + timestamp: i64, +) -> Result<(), Error> { sql::execute( context, &context.sql, "UPDATE chats SET locations_last_sent=? WHERE id=?;", params![timestamp, chat_id as i32], - ) - .is_ok() + )?; + + Ok(()) } -pub fn dc_set_msg_location_id(context: &Context, msg_id: u32, location_id: u32) -> bool { +pub fn set_msg_location_id(context: &Context, msg_id: u32, location_id: u32) -> Result<(), Error> { sql::execute( context, &context.sql, "UPDATE msgs SET location_id=? WHERE id=?;", params![location_id, msg_id as i32], - ) - .is_ok() + )?; + + Ok(()) } -pub fn dc_save_locations( +pub fn save( context: &Context, chat_id: u32, contact_id: u32, - locations_opt: &Option>, + locations: &[Location], independent: i32, -) -> u32 { - if chat_id <= 9 || locations_opt.is_none() { - return 0; - } +) -> Result { + ensure!(chat_id > 9, "Invalid chat id"); + context.sql.prepare2( + "SELECT id FROM locations WHERE timestamp=? AND from_id=?", + "INSERT INTO locations\ + (timestamp, from_id, chat_id, latitude, longitude, accuracy, independent) \ + VALUES (?,?,?,?,?,?,?);", + |mut stmt_test, mut stmt_insert, conn| { + let mut newest_timestamp = 0; + let mut newest_location_id = 0; - let locations = locations_opt.as_ref().unwrap(); - context - .sql - .prepare2( - "SELECT id FROM locations WHERE timestamp=? AND from_id=?", - "INSERT INTO locations\ - (timestamp, from_id, chat_id, latitude, longitude, accuracy, independent) \ - VALUES (?,?,?,?,?,?,?);", - |mut stmt_test, mut stmt_insert, conn| { - let mut newest_timestamp = 0; - let mut newest_location_id = 0; + for location in locations { + let exists = stmt_test.exists(params![location.timestamp, contact_id as i32])?; - for location in locations { - let exists = - stmt_test.exists(params![location.timestamp, contact_id as i32])?; + if 0 != independent || !exists { + stmt_insert.execute(params![ + location.timestamp, + contact_id as i32, + chat_id as i32, + location.latitude, + location.longitude, + location.accuracy, + independent, + ])?; - if 0 != independent || !exists { - stmt_insert.execute(params![ + if location.timestamp > newest_timestamp { + newest_timestamp = location.timestamp; + newest_location_id = sql::get_rowid2_with_conn( + context, + conn, + "locations", + "timestamp", location.timestamp, + "from_id", contact_id as i32, - chat_id as i32, - location.latitude, - location.longitude, - location.accuracy, - independent, - ])?; - - if location.timestamp > newest_timestamp { - newest_timestamp = location.timestamp; - newest_location_id = sql::get_rowid2_with_conn( - context, - conn, - "locations", - "timestamp", - location.timestamp, - "from_id", - contact_id as i32, - ); - } + ); } } - Ok(newest_location_id) - }, - ) - .unwrap_or_default() -} - -pub fn dc_kml_parse(context: &Context, content: impl AsRef) -> Result { - ensure!( - content.as_ref().len() <= (1 * 1024 * 1024), - "A kml-files with {} bytes is larger than reasonably expected.", - content.as_ref().len() - ); - - let mut reader = quick_xml::Reader::from_str(content.as_ref()); - reader.trim_text(true); - - let mut kml = Kml::new(); - kml.locations = Some(Vec::with_capacity(100)); - - let mut buf = Vec::new(); - - loop { - match reader.read_event(&mut buf) { - Ok(quick_xml::events::Event::Start(ref e)) => kml_starttag_cb(e, &mut kml, &reader), - Ok(quick_xml::events::Event::End(ref e)) => kml_endtag_cb(e, &mut kml), - Ok(quick_xml::events::Event::Text(ref e)) => kml_text_cb(e, &mut kml, &reader), - Err(e) => { - error!( - context, - 0, - "Location parsing: Error at position {}: {:?}", - reader.buffer_position(), - e - ); } - Ok(quick_xml::events::Event::Eof) => break, - _ => (), - } - buf.clear(); - } - - Ok(kml) -} - -fn kml_text_cb( - event: &BytesText, - kml: &mut Kml, - reader: &quick_xml::Reader, -) { - if 0 != kml.tag & (0x4 | 0x10) { - let val = event.unescape_and_decode(reader).unwrap_or_default(); - - let val = val - .replace("\n", "") - .replace("\r", "") - .replace("\t", "") - .replace(" ", ""); - - if 0 != kml.tag & 0x4 && val.len() >= 19 { - // YYYY-MM-DDTHH:MM:SSZ - // 0 4 7 10 13 16 19 - match chrono::NaiveDateTime::parse_from_str(&val, "%Y-%m-%dT%H:%M:%SZ") { - Ok(res) => { - kml.curr.timestamp = res.timestamp(); - if kml.curr.timestamp > time() { - kml.curr.timestamp = time(); - } - } - Err(_err) => { - kml.curr.timestamp = time(); - } - } - } else if 0 != kml.tag & 0x10 { - let parts = val.splitn(2, ',').collect::>(); - if parts.len() == 2 { - kml.curr.longitude = parts[0].parse().unwrap_or_default(); - kml.curr.latitude = parts[1].parse().unwrap_or_default(); - } - } - } -} - -fn kml_endtag_cb(event: &BytesEnd, kml: &mut Kml) { - let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase(); - - if tag == "placemark" { - if 0 != kml.tag & 0x1 - && 0 != kml.curr.timestamp - && 0. != kml.curr.latitude - && 0. != kml.curr.longitude - { - if let Some(ref mut locations) = kml.locations { - locations.push(std::mem::replace(&mut kml.curr, Location::new())); - } - } - kml.tag = 0 - }; -} - -fn kml_starttag_cb( - event: &BytesStart, - kml: &mut Kml, - reader: &quick_xml::Reader, -) { - let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase(); - if tag == "document" { - if let Some(addr) = event.attributes().find(|attr| { - attr.as_ref() - .map(|a| String::from_utf8_lossy(a.key).trim().to_lowercase() == "addr") - .unwrap_or_default() - }) { - kml.addr = addr.unwrap().unescape_and_decode_value(reader).ok(); - } - } else if tag == "placemark" { - kml.tag = 0x1; - kml.curr.timestamp = 0; - kml.curr.latitude = 0.0; - kml.curr.longitude = 0.0; - kml.curr.accuracy = 0.0 - } else if tag == "timestamp" && 0 != kml.tag & 0x1 { - kml.tag = 0x1 | 0x2 - } else if tag == "when" && 0 != kml.tag & 0x2 { - kml.tag = 0x1 | 0x2 | 0x4 - } else if tag == "point" && 0 != kml.tag & 0x1 { - kml.tag = 0x1 | 0x8 - } else if tag == "coordinates" && 0 != kml.tag & 0x8 { - kml.tag = 0x1 | 0x8 | 0x10; - if let Some(acc) = event.attributes().find(|attr| { - attr.as_ref() - .map(|a| String::from_utf8_lossy(a.key).trim().to_lowercase() == "accuracy") - .unwrap_or_default() - }) { - let v = acc - .unwrap() - .unescape_and_decode_value(reader) - .unwrap_or_default(); - - kml.curr.accuracy = v.trim().parse().unwrap_or_default(); - } - } + Ok(newest_location_id) + }, + ) } #[allow(non_snake_case)] -pub fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: &Job) { +pub fn job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: &Job) { let now = time(); let mut continue_streaming: libc::c_int = 1; info!( @@ -626,7 +610,7 @@ pub fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: &Job) { } #[allow(non_snake_case)] -pub fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut Job) { +pub fn job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut Job) { // this function is called when location-streaming _might_ have ended for a chat. // the function checks, if location-streaming is really ended; // if so, a device-message is added if not yet done. @@ -667,13 +651,13 @@ mod tests { use crate::test_utils::dummy_context; #[test] - fn test_dc_kml_parse() { + fn test_kml_parse() { let context = dummy_context(); let xml = "\n\n\n2019-03-06T21:09:57Z9.423110,53.790302\n\n \n\t2018-12-13T22:11:12Z\t 19.423110 \t , \n 63.790302\n \n\n"; - let kml = dc_kml_parse(&context.ctx, &xml).expect("parsing failed"); + let kml = Kml::parse(&context.ctx, &xml).expect("parsing failed"); assert!(kml.addr.is_some()); assert_eq!(kml.addr.as_ref().unwrap(), "user@example.org",);