diff --git a/src/dc_chat.rs b/src/dc_chat.rs index d6d7936b3..39bacf4db 100644 --- a/src/dc_chat.rs +++ b/src/dc_chat.rs @@ -119,68 +119,66 @@ pub fn dc_chat_load_from_db(chat: *mut Chat, chat_id: u32) -> bool { let context = unsafe { (*chat).context }; - if let Some(mut stmt) = dc_sqlite3_prepare( - context, - &context.sql, - "SELECT c.id,c.type,c.name, c.grpid,c.param,c.archived, \ - c.blocked, c.gossiped_timestamp, c.locations_send_until \ - FROM chats c WHERE c.id=?;", - ) { - stmt.query_row(params![chat_id as i32], |row| { - let c = unsafe { &mut *chat }; + context + .sql + .query_row( + "SELECT c.id,c.type,c.name, c.grpid,c.param,c.archived, \ + c.blocked, c.gossiped_timestamp, c.locations_send_until \ + FROM chats c WHERE c.id=?;", + params![chat_id as i32], + |row| { + let c = unsafe { &mut *chat }; - c.id = row.get(0)?; - c.type_0 = row.get(1)?; - c.name = { - let raw: String = row.get(2)?; - unsafe { strdup(to_cstring(raw).as_ptr()) } - }; - c.grpid = { - let raw: String = row.get(3)?; - unsafe { strdup(to_cstring(raw).as_ptr()) } - }; + c.id = row.get(0)?; + c.type_0 = row.get(1)?; + c.name = { + let raw: String = row.get(2)?; + unsafe { strdup(to_cstring(raw).as_ptr()) } + }; + c.grpid = { + let raw: String = row.get(3)?; + unsafe { strdup(to_cstring(raw).as_ptr()) } + }; - let packed: String = row.get(4)?; - unsafe { dc_param_set_packed((*chat).param, to_cstring(&packed).as_ptr()) }; - c.archived = row.get(5)?; - c.blocked = row.get(6)?; - c.gossiped_timestamp = row.get(7)?; - c.is_sending_locations = row.get(8)?; + let packed: String = row.get(4)?; + unsafe { dc_param_set_packed((*chat).param, to_cstring(&packed).as_ptr()) }; + c.archived = row.get(5)?; + c.blocked = row.get(6)?; + c.gossiped_timestamp = row.get(7)?; + c.is_sending_locations = row.get(8)?; - match c.id { - 1 => unsafe { - free((*chat).name as *mut libc::c_void); - (*chat).name = dc_stock_str((*chat).context, 8); - }, - 6 => unsafe { - free((*chat).name as *mut libc::c_void); - let tempname: *mut libc::c_char = dc_stock_str((*chat).context, 40); - (*chat).name = dc_mprintf( - b"%s (%i)\x00" as *const u8 as *const libc::c_char, - tempname, - dc_get_archived_cnt((*chat).context), - ); - free(tempname as *mut libc::c_void); - }, - 5 => unsafe { - free((*chat).name as *mut libc::c_void); - (*chat).name = dc_stock_str((*chat).context, 41); - }, - _ => { - if 0 != unsafe { dc_param_exists((*chat).param, 'K' as i32) } { - unsafe { - free((*chat).name as *mut libc::c_void); - (*chat).name = dc_stock_str((*chat).context, 2); + match c.id { + 1 => unsafe { + free((*chat).name as *mut libc::c_void); + (*chat).name = dc_stock_str((*chat).context, 8); + }, + 6 => unsafe { + free((*chat).name as *mut libc::c_void); + let tempname: *mut libc::c_char = dc_stock_str((*chat).context, 40); + (*chat).name = dc_mprintf( + b"%s (%i)\x00" as *const u8 as *const libc::c_char, + tempname, + dc_get_archived_cnt((*chat).context), + ); + free(tempname as *mut libc::c_void); + }, + 5 => unsafe { + free((*chat).name as *mut libc::c_void); + (*chat).name = dc_stock_str((*chat).context, 41); + }, + _ => { + if 0 != unsafe { dc_param_exists((*chat).param, 'K' as i32) } { + unsafe { + free((*chat).name as *mut libc::c_void); + (*chat).name = dc_stock_str((*chat).context, 2); + } } } } - } - Ok(()) - }) + Ok(()) + }, + ) .is_ok() - } else { - false - } } pub unsafe fn dc_create_chat_by_contact_id(context: &Context, contact_id: uint32_t) -> uint32_t { @@ -318,20 +316,15 @@ pub fn dc_lookup_real_nchat_by_contact_id( return; } - if let Some(mut stmt) = dc_sqlite3_prepare( - context, - &context.sql, + context.sql.query_row( "SELECT c.id, c.blocked FROM chats c INNER JOIN chats_contacts j ON c.id=j.chat_id WHERE c.type=100 AND c.id>9 AND j.contact_id=?;", - ) { - stmt.query_row( - params![contact_id as i32], - |row| { - unsafe { *ret_chat_id = row.get(0)? }; - unsafe { *ret_chat_blocked = row.get(1)? }; - Ok(()) - } - ).unwrap(); - } + params![contact_id as i32], + |row| { + unsafe { *ret_chat_id = row.get(0)? }; + unsafe { *ret_chat_blocked = row.get(1)? }; + Ok(()) + } + ).unwrap(); } pub unsafe fn dc_get_chat_id_by_contact_id(context: &Context, contact_id: uint32_t) -> uint32_t { @@ -560,16 +553,13 @@ unsafe fn prepare_msg_raw( let mut can_encrypt: libc::c_int = 1; let mut all_mutual: libc::c_int = 1; - if let Some(mut stmt) = dc_sqlite3_prepare( - context, - &context.sql, + context.sql.query_row( "SELECT ps.prefer_encrypted, c.addr \ FROM chats_contacts cc \ LEFT JOIN contacts c ON cc.contact_id=c.id \ LEFT JOIN acpeerstates ps ON c.addr=ps.addr \ WHERE cc.chat_id=? AND cc.contact_id>9;", - ) { - stmt.query_row(params![(*chat).id], |row| { + params![(*chat).id], |row| { let state: String = row.get(1)?; if let Some(prefer_encrypted) = row.get::<_, Option>(0)? { @@ -593,9 +583,8 @@ unsafe fn prepare_msg_raw( all_mutual = 0; } Ok(()) - }) - .unwrap(); - } + } + ).unwrap(); if 0 != can_encrypt { if 0 != all_mutual { @@ -765,33 +754,33 @@ unsafe fn get_parent_mime_headers( || parent_in_reply_to.is_null() || parent_references.is_null()) { - if let Some(mut stmt) = dc_sqlite3_prepare( - (*chat).context, - &(*chat).context.sql, - "SELECT rfc724_mid, mime_in_reply_to, mime_references \ - FROM msgs WHERE timestamp=(SELECT max(timestamp) \ - FROM msgs WHERE chat_id=? AND from_id!=?);", - ) { - success = stmt - .query_row(params![(*chat).id as i32, 1], |row| { + success = (*chat) + .context + .sql + .query_row( + "SELECT rfc724_mid, mime_in_reply_to, mime_references \ + FROM msgs WHERE timestamp=(SELECT max(timestamp) \ + FROM msgs WHERE chat_id=? AND from_id!=?);", + params![(*chat).id as i32, 1], + |row| { *parent_rfc724_mid = dc_strdup(to_cstring(row.get::<_, String>(0)?).as_ptr()); *parent_in_reply_to = dc_strdup(to_cstring(row.get::<_, String>(1)?).as_ptr()); *parent_references = dc_strdup(to_cstring(row.get::<_, String>(2)?).as_ptr()); Ok(()) - }) - .is_ok() as libc::c_int; - } + }, + ) + .is_ok() as libc::c_int; if 0 == success { - if let Some(mut stmt) = dc_sqlite3_prepare( - (*chat).context, - &(*chat).context.sql, - "SELECT rfc724_mid, mime_in_reply_to, mime_references \ - FROM msgs WHERE timestamp=(SELECT min(timestamp) \ - FROM msgs WHERE chat_id=? AND from_id==?);", - ) { - success = stmt - .query_row(params![(*chat).id as i32, 1], |row| { + success = (*chat) + .context + .sql + .query_row( + "SELECT rfc724_mid, mime_in_reply_to, mime_references \ + FROM msgs WHERE timestamp=(SELECT min(timestamp) \ + FROM msgs WHERE chat_id=? AND from_id==?);", + params![(*chat).id as i32, 1], + |row| { *parent_rfc724_mid = dc_strdup(to_cstring(row.get::<_, String>(0)?).as_ptr()); *parent_in_reply_to = @@ -799,9 +788,9 @@ unsafe fn get_parent_mime_headers( *parent_references = dc_strdup(to_cstring(row.get::<_, String>(2)?).as_ptr()); Ok(()) - }) - .is_ok() as libc::c_int; - } + }, + ) + .is_ok() as libc::c_int; } } success @@ -1074,75 +1063,16 @@ pub unsafe fn dc_get_chat_msgs( flags: uint32_t, marker1before: uint32_t, ) -> *mut dc_array_t { - //clock_t start = clock(); - let mut success: libc::c_int = 0i32; - let ret = dc_array_new(512i32 as size_t); + let ret = dc_array_new(512); + assert!(!ret.is_null()); let mut last_day = 0; let cnv_to_local = dc_gm2local_offset(); - if ret.is_null() { - return ret; - } - let process_row = |row: &rusqlite::Row| Ok((row.get::<_, i32>(0)?, row.get::<_, i64>(1)?)); - - let rows = if chat_id == 1 { - let show_emails = dc_sqlite3_get_config_int(context, &context.sql, "show_emails", 0); - if let Some(mut stmt) = dc_sqlite3_prepare( - context, - &context.sql, - "SELECT m.id, m.timestamp FROM msgs m \ - LEFT JOIN chats ON m.chat_id=chats.id \ - LEFT JOIN contacts ON m.from_id=contacts.id WHERE m.from_id!=1 \ - AND m.from_id!=2 \ - AND m.hidden=0 \ - AND chats.blocked=2 \ - AND contacts.blocked=0 \ - AND m.msgrmsg>=? \ - ORDER BY m.timestamp,m.id;", - ) { - stmt.query_map(params![if show_emails == 2 { 0 } else { 1 }], process_row) - .and_then(|res| res.collect::>>()) - } else { - dc_array_unref(ret); - return std::ptr::null_mut(); - } - } else if chat_id == 5 { - if let Some(mut stmt) = dc_sqlite3_prepare( - context, - &context.sql, - "SELECT m.id, m.timestamp FROM msgs m \ - LEFT JOIN contacts ct ON m.from_id=ct.id WHERE m.starred=1 \ - AND m.hidden=0 \ - AND ct.blocked=0 \ - ORDER BY m.timestamp,m.id;", - ) { - stmt.query_map(params![], process_row) - .and_then(|res| res.collect::>>()) - } else { - dc_array_unref(ret); - return std::ptr::null_mut(); - } - } else { - if let Some(mut stmt) = dc_sqlite3_prepare( - context, - &context.sql, - "SELECT m.id, m.timestamp FROM msgs m \ - WHERE m.chat_id=? \ - AND m.hidden=0 \ - ORDER BY m.timestamp,m.id;", - ) { - stmt.query_map(params![chat_id as i32], process_row) - .and_then(|res| res.collect::>>()) - } else { - dc_array_unref(ret); - return std::ptr::null_mut(); - } - }; - - if let Ok(rows) = rows { - for (curr_id, ts) in rows { + let process_rows = |rows: rusqlite::MappedRows<_>| { + for row in rows { + let (curr_id, ts) = row?; if curr_id as u32 == marker1before { dc_array_add_id(ret, 1); } @@ -1156,10 +1086,49 @@ pub unsafe fn dc_get_chat_msgs( } dc_array_add_id(ret, curr_id as u32); } - success = 1; - } + Ok(()) + }; - if 0 != success { + let success = if chat_id == 1 { + let show_emails = dc_sqlite3_get_config_int(context, &context.sql, "show_emails", 0); + context.sql.query_map( + "SELECT m.id, m.timestamp FROM msgs m \ + LEFT JOIN chats ON m.chat_id=chats.id \ + LEFT JOIN contacts ON m.from_id=contacts.id WHERE m.from_id!=1 \ + AND m.from_id!=2 \ + AND m.hidden=0 \ + AND chats.blocked=2 \ + AND contacts.blocked=0 \ + AND m.msgrmsg>=? \ + ORDER BY m.timestamp,m.id;", + params![if show_emails == 2 { 0 } else { 1 }], + process_row, + process_rows, + ) + } else if chat_id == 5 { + context.sql.query_map( + "SELECT m.id, m.timestamp FROM msgs m \ + LEFT JOIN contacts ct ON m.from_id=ct.id WHERE m.starred=1 \ + AND m.hidden=0 \ + AND ct.blocked=0 \ + ORDER BY m.timestamp,m.id;", + params![], + process_row, + process_rows, + ) + } else { + context.sql.query_map( + "SELECT m.id, m.timestamp FROM msgs m \ + WHERE m.chat_id=? \ + AND m.hidden=0 \ + ORDER BY m.timestamp,m.id;", + params![chat_id as i32], + process_row, + process_rows, + ) + }; + + if success.is_ok() { ret } else { if !ret.is_null() { @@ -1249,42 +1218,30 @@ pub fn dc_get_chat_media( msg_type2: libc::c_int, msg_type3: libc::c_int, ) -> *mut dc_array_t { - let ret = unsafe { dc_array_new(100) }; - - if let Some(mut stmt) = dc_sqlite3_prepare( - context, - &context.sql, - "SELECT id FROM msgs WHERE chat_id=? AND (type=? OR type=? OR type=?) ORDER BY timestamp, id;" - ) { - let rows = stmt.query_map( - params![ - chat_id as i32, - msg_type, - if msg_type2 > 0 { - msg_type2 - } else { - msg_type - }, if msg_type3 > 0 { - msg_type3 - } else { - msg_type - }, - ], - |row| row.get::<_, i32>(0) - ); - - if let Ok(ids) = rows { + context.sql.query_map( + "SELECT id FROM msgs WHERE chat_id=? AND (type=? OR type=? OR type=?) ORDER BY timestamp, id;", + params![ + chat_id as i32, + msg_type, + if msg_type2 > 0 { + msg_type2 + } else { + msg_type + }, if msg_type3 > 0 { + msg_type3 + } else { + msg_type + }, + ], + |row| row.get::<_, i32>(0), + |ids| { + let ret = unsafe { dc_array_new(100) }; for id in ids { - if let Ok(id) = id { - unsafe { dc_array_add_id(ret, id as u32) }; - } + unsafe { dc_array_add_id(ret, id? as u32) }; } - return ret; + Ok(ret) } - } - - unsafe { dc_array_unref(ret) }; - std::ptr::null_mut() + ).unwrap_or_else(|_| std::ptr::null_mut()) } pub unsafe fn dc_get_next_media( @@ -1425,31 +1382,28 @@ pub fn dc_get_chat_contacts(context: &Context, chat_id: u32) -> *mut dc_array_t return std::ptr::null_mut(); } - let ret = unsafe { dc_array_new(100) }; // we could also create a list for all contacts in the deaddrop by searching contacts belonging to chats with // chats.blocked=2, however, currently this is not needed - if let Some(mut stmt) = dc_sqlite3_prepare( - context, - &context.sql, - "SELECT cc.contact_id FROM chats_contacts cc \ - LEFT JOIN contacts c ON c.id=cc.contact_id WHERE cc.chat_id=? \ - ORDER BY c.id=1, LOWER(c.name||c.addr), c.id;", - ) { - let rows = stmt.query_map(params![chat_id as i32], |row| row.get::<_, i32>(0)); - if let Ok(ids) = rows { - for id in ids { - if let Ok(id) = id { - unsafe { dc_array_add_id(ret, id as u32) }; + context + .sql + .query_map( + "SELECT cc.contact_id FROM chats_contacts cc \ + LEFT JOIN contacts c ON c.id=cc.contact_id WHERE cc.chat_id=? \ + ORDER BY c.id=1, LOWER(c.name||c.addr), c.id;", + params![chat_id as i32], + |row| row.get::<_, i32>(0), + |ids| { + let ret = unsafe { dc_array_new(100) }; + + for id in ids { + unsafe { dc_array_add_id(ret, id? as u32) }; } - } - return ret; - } - } - - unsafe { dc_array_unref(ret) } - std::ptr::null_mut() + Ok(ret) + }, + ) + .unwrap_or_else(|_| std::ptr::null_mut()) } pub unsafe fn dc_get_chat(context: &Context, chat_id: uint32_t) -> *mut Chat { @@ -1962,9 +1916,9 @@ pub unsafe fn dc_forward_msgs( let msg = dc_msg_new_untyped(context); let chat = dc_chat_new(context); - let contact: *mut dc_contact_t = dc_contact_new(context); - let created_db_entries: *mut carray = carray_new(16i32 as libc::c_uint); - let mut idsstr: *mut libc::c_char = 0 as *mut libc::c_char; + let contact = dc_contact_new(context); + let created_db_entries = carray_new(16); + let mut idsstr = 0 as *mut libc::c_char; let mut curr_timestamp: i64; let original_param: *mut dc_param_t = dc_param_new(); @@ -1973,16 +1927,15 @@ pub unsafe fn dc_forward_msgs( curr_timestamp = dc_create_smeared_timestamps(context, msg_cnt); idsstr = dc_arr_to_string(msg_ids, msg_cnt); - if let Some(mut stmt) = dc_sqlite3_prepare( - context, - &context.sql, - "SELECT id FROM msgs WHERE id IN(?) ORDER BY timestamp,id", - ) { - let rows = stmt.query_map(params![as_str(idsstr)], |row| row.get::<_, i32>(0)); - - if let Ok(ids) = rows { - for id in ids { - if let Ok(src_msg_id) = id { + context + .sql + .query_map( + "SELECT id FROM msgs WHERE id IN(?) ORDER BY timestamp,id", + params![as_str(idsstr)], + |row| row.get::<_, i32>(0), + |ids| { + for id in ids { + let src_msg_id = id?; if !dc_msg_load_from_db(msg, context, src_msg_id as u32) { break; } @@ -2034,9 +1987,10 @@ pub unsafe fn dc_forward_msgs( 0 as *mut libc::c_uint, ); } - } - } - } + Ok(()) + }, + ) + .unwrap(); // TODO: better error handling } if !created_db_entries.is_null() { @@ -2241,26 +2195,24 @@ pub unsafe fn dc_get_chat_id_by_grpid( *ret_verified = 0; } - dc_sqlite3_prepare( - context, - &context.sql, - "SELECT id, blocked, type FROM chats WHERE grpid=?;", - ) - .and_then(|mut stmt| { - stmt.query_row(params![as_str(grpid)], |row| { - let chat_id = row.get(0)?; - if !ret_blocked.is_null() { - *ret_blocked = row.get(1)?; - } - if !ret_verified.is_null() { - let v: i32 = row.get(2)?; - *ret_verified = (v == 130) as libc::c_int; - } - Ok(chat_id) - }) - .ok() - }) - .unwrap_or_default() + context + .sql + .query_row( + "SELECT id, blocked, type FROM chats WHERE grpid=?;", + params![as_str(grpid)], + |row| { + let chat_id = row.get(0)?; + if !ret_blocked.is_null() { + *ret_blocked = row.get(1)?; + } + if !ret_verified.is_null() { + let v: i32 = row.get(2)?; + *ret_verified = (v == 130) as libc::c_int; + } + Ok(chat_id) + }, + ) + .unwrap_or_default() } pub fn dc_add_device_msg(context: &Context, chat_id: uint32_t, text: *const libc::c_char) { @@ -2274,9 +2226,7 @@ pub fn dc_add_device_msg(context: &Context, chat_id: uint32_t, text: *const libc ) }; - if !dc_sqlite3_execute( - context, - &context.sql, + if context.sql.execute( "INSERT INTO msgs (chat_id,from_id,to_id, timestamp,type,state, txt,rfc724_mid) VALUES (?,?,?, ?,?,?, ?,?);", params![ chat_id as i32, @@ -2288,7 +2238,8 @@ pub fn dc_add_device_msg(context: &Context, chat_id: uint32_t, text: *const libc as_str(text), as_str(rfc724_mid), ] - ) { + ).is_err() { + unsafe { free(rfc724_mid as *mut libc::c_void) }; return; } diff --git a/src/dc_contact.rs b/src/dc_contact.rs index 13d9f517a..e3d84e9e0 100644 --- a/src/dc_contact.rs +++ b/src/dc_contact.rs @@ -604,27 +604,23 @@ pub fn dc_get_blocked_cnt(context: &Context) -> libc::c_int { } pub fn dc_get_blocked_contacts(context: &Context) -> *mut dc_array_t { - let ret = unsafe { dc_array_new(100) }; + context + .sql + .query_map( + "SELECT id FROM contacts WHERE id>? AND blocked!=0 ORDER BY LOWER(name||addr),id;", + params![9], + |row| row.get::<_, i32>(0), + |ids| { + let ret = unsafe { dc_array_new(100) }; - if let Some(mut stmt) = dc_sqlite3_prepare( - context, - &context.sql, - "SELECT id FROM contacts WHERE id>? AND blocked!=0 ORDER BY LOWER(name||addr),id;", - ) { - let rows = stmt.query_map(params![9], |row| row.get::<_, i32>(0)); - if let Ok(ids) = rows { - for id in ids { - if let Ok(id) = id { - unsafe { dc_array_add_id(ret, id as u32) }; + for id in ids { + unsafe { dc_array_add_id(ret, id? as u32) }; } - } - return ret; - } - } - - unsafe { dc_array_unref(ret) }; - std::ptr::null_mut() + Ok(ret) + }, + ) + .unwrap_or_else(|_| std::ptr::null_mut()) } pub unsafe fn dc_get_contact_encrinfo( @@ -1057,18 +1053,23 @@ pub fn dc_real_contact_exists(context: &Context, contact_id: u32) -> bool { return false; } - dc_sqlite3_prepare(context, &context.sql, "SELECT id FROM contacts WHERE id=?;") - .map(|mut stmt| stmt.exists(params![contact_id as i32]).unwrap_or_default()) + context + .sql + .exists( + "SELECT id FROM contacts WHERE id=?;", + params![contact_id as i32], + ) .unwrap_or_default() } pub fn dc_scaleup_contact_origin(context: &Context, contact_id: u32, origin: libc::c_int) -> bool { - dc_sqlite3_execute( - context, - &context.sql, - "UPDATE contacts SET origin=? WHERE id=? AND origin bool { - dc_sqlite3_prepare( - context, - &context.sql, - "SELECT id FROM chats WHERE (? OR id=?) AND locations_send_until>?;", - ) - .and_then(|mut stmt| { - stmt.exists(params![ - if chat_id == 0 { 1 } else { 0 }, - chat_id as i32, - time() - ]) - .ok() - }) - .unwrap_or_default() + context + .sql + .exists( + "SELECT id FROM chats WHERE (? OR id=?) AND locations_send_until>?;", + params![if chat_id == 0 { 1 } else { 0 }, chat_id as i32, time()], + ) + .unwrap_or_default() } pub fn dc_set_location( @@ -144,43 +137,35 @@ pub fn dc_set_location( return 1; } - let mut continue_streaming = false; - let rows = if let Some(mut stmt) = dc_sqlite3_prepare( - context, - &context.sql, + context.sql.query_map( "SELECT id FROM chats WHERE locations_send_until>?;", - ) { - stmt.query_map(params![time()], |row| row.get::<_, i32>(0)) - .and_then(|res| res.collect::>>()) - .ok() - } else { - None - }; + params![time()], |row| row.get::<_, i32>(0), + |chats| { + let mut continue_streaming = false; - if let Some(chats) = rows { - for chat_id in chats { - dc_sqlite3_execute( - context, - &context.sql, - "INSERT INTO locations (latitude, longitude, accuracy, timestamp, chat_id, from_id) VALUES (?,?,?,?,?,?);", - params![ - latitude, - longitude, - accuracy, - time(), - chat_id, - 1, - ] - ); - continue_streaming = true; + for chat in chats { + let chat_id = chat?; + context.sql.execute( + "INSERT INTO locations \ + (latitude, longitude, accuracy, timestamp, chat_id, from_id) VALUES (?,?,?,?,?,?);", + params![ + latitude, + longitude, + accuracy, + time(), + chat_id, + 1, + ] + )?; + continue_streaming = true; + } + if continue_streaming { + context.call_cb(Event::LOCATION_CHANGED, 1, 0); + }; + unsafe { schedule_MAYBE_SEND_LOCATIONS(context, 0) }; + Ok(continue_streaming as libc::c_int) } - if continue_streaming { - context.call_cb(Event::LOCATION_CHANGED, 1, 0); - }; - unsafe { schedule_MAYBE_SEND_LOCATIONS(context, 0) }; - } - - continue_streaming as libc::c_int + ).unwrap_or_default() } pub fn dc_get_locations( @@ -194,19 +179,15 @@ pub fn dc_get_locations( timestamp_to = time() + 10; } - let ret = unsafe { dc_array_new_typed(1, 500) }; - - let locations = if let Some(mut stmt) = dc_sqlite3_prepare( - context, - &context.sql, - "SELECT l.id, l.latitude, l.longitude, l.accuracy, l.timestamp, l.independent, \ - m.id, l.from_id, l.chat_id, m.txt \ - FROM locations l LEFT JOIN msgs m ON l.id=m.location_id WHERE (? OR l.chat_id=?) \ - AND (? OR l.from_id=?) \ - AND (l.independent=1 OR (l.timestamp>=? AND l.timestamp<=?)) \ - ORDER BY l.timestamp DESC, l.id DESC, m.id DESC;", - ) { - stmt.query_map( + context + .sql + .query_map( + "SELECT l.id, l.latitude, l.longitude, l.accuracy, l.timestamp, l.independent, \ + m.id, l.from_id, l.chat_id, m.txt \ + FROM locations l LEFT JOIN msgs m ON l.id=m.location_id WHERE (? OR l.chat_id=?) \ + AND (? OR l.from_id=?) \ + AND (l.independent=1 OR (l.timestamp>=? AND l.timestamp<=?)) \ + ORDER BY l.timestamp DESC, l.id DESC, m.id DESC;", params![ if chat_id == 0 { 1 } else { 0 }, chat_id as i32, @@ -239,22 +220,16 @@ pub fn dc_get_locations( } Ok(loc) }, - ) - .and_then(|res| res.collect::>>()) - .ok() - } else { - None - }; + |locations| { + let ret = unsafe { dc_array_new_typed(1, 500) }; - if let Some(locations) = locations { - for location in locations { - unsafe { dc_array_add_ptr(ret, location as *mut libc::c_void) }; - } - ret - } else { - unsafe { dc_array_unref(ret) } - std::ptr::null_mut() - } + for location in locations { + unsafe { dc_array_add_ptr(ret, location? as *mut libc::c_void) }; + } + Ok(ret) + }, + ) + .unwrap_or_else(|_| std::ptr::null_mut()) } // TODO should be bool /rtn @@ -295,29 +270,23 @@ pub fn dc_get_location_kml( let self_addr = self_addr.unwrap(); - if let Some((locations_send_begin, locations_send_until, locations_last_sent)) = dc_sqlite3_prepare( - context, - &context.sql, + if let Ok((locations_send_begin, locations_send_until, locations_last_sent)) = context.sql.query_row( "SELECT locations_send_begin, locations_send_until, locations_last_sent FROM chats WHERE id=?;", - - ).and_then(|mut stmt| { - stmt.query_row(params![chat_id as i32], |row| { + params![chat_id as i32], |row| { let send_begin: i64 = row.get(0)?; let send_until: i64 = row.get(1)?; let last_sent: i64 = row.get(2)?; Ok((send_begin, send_until, last_sent)) - }).ok() - }) { + } + ) { if !(locations_send_begin == 0 || now > locations_send_until) { ret += &format!( "\n\n\n", self_addr, ); - let rows = if let Some(mut stmt) = dc_sqlite3_prepare( - context, - &context.sql, + context.sql.query_map( "SELECT id, latitude, longitude, accuracy, timestamp\ FROM locations WHERE from_id=? \ AND timestamp>=? \ @@ -325,38 +294,35 @@ pub fn dc_get_location_kml( AND independent=0 \ GROUP BY timestamp \ ORDER BY timestamp;", - ){ - stmt.query_map( - params![1, locations_send_begin, locations_last_sent, 1], - |row| { - let location_id: i32 = row.get(0)?; - let latitude: f64 = row.get(1)?; - let longitude: f64 = row.get(2)?; - let accuracy: f64 = row.get(3)?; - let timestamp = unsafe { get_kml_timestamp(row.get(4)?) }; + params![1, locations_send_begin, locations_last_sent, 1], + |row| { + let location_id: i32 = row.get(0)?; + let latitude: f64 = row.get(1)?; + let longitude: f64 = row.get(2)?; + let accuracy: f64 = row.get(3)?; + let timestamp = unsafe { get_kml_timestamp(row.get(4)?) }; - Ok((location_id, latitude, longitude, accuracy, timestamp)) - }).and_then(|res| res.collect::>>()).ok() - } else { - None - }; - - if let Some(rows) = rows { - for (location_id, latitude, longitude, accuracy, timestamp) in rows { - ret += &format!( - "{}{},{}\n\x00", - as_str(timestamp), - accuracy, - longitude, - latitude - ); - location_count += 1; - if !last_added_location_id.is_null() { - unsafe { *last_added_location_id = location_id as u32 }; + Ok((location_id, latitude, longitude, accuracy, timestamp)) + }, + |rows| { + for row in rows { + let (location_id, latitude, longitude, accuracy, timestamp) = row?; + ret += &format!( + "{}{},{}\n\x00", + as_str(timestamp), + accuracy, + longitude, + latitude + ); + location_count += 1; + if !last_added_location_id.is_null() { + unsafe { *last_added_location_id = location_id as u32 }; + } + unsafe { free(timestamp as *mut libc::c_void) }; } - unsafe { free(timestamp as *mut libc::c_void) }; + Ok(()) } - } + ).unwrap(); // TODO: better error handling } } @@ -690,74 +656,78 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: *mu as *const libc::c_char, ); - if let Some(mut stmt) = dc_sqlite3_prepare( - context, - &context.sql, - "SELECT id, locations_send_begin, locations_last_sent \ - FROM chats \ - WHERE locations_send_until>?;", - ) { - if let Ok(rows) = stmt.query_map(params![now], |row| { - let chat_id: i32 = row.get(0)?; - let locations_send_begin: i64 = row.get(1)?; - let locations_last_sent: i64 = row.get(2)?; - continue_streaming = 1; + context + .sql + .query_map( + "SELECT id, locations_send_begin, locations_last_sent \ + FROM chats \ + WHERE locations_send_until>?;", + params![now], + |row| { + let chat_id: i32 = row.get(0)?; + let locations_send_begin: i64 = row.get(1)?; + let locations_last_sent: i64 = row.get(2)?; + continue_streaming = 1; - // be a bit tolerant as the timer may not align exactly with time(NULL) - if now - locations_last_sent < (60 - 3) { - Ok(None) - } else { - Ok(Some((chat_id, locations_send_begin, locations_last_sent))) - } - }) { - let stmt_locations = dc_sqlite3_prepare( - context, - &context.sql, - "SELECT id \ - FROM locations \ - WHERE from_id=? \ - AND timestamp>=? \ - AND timestamp>? \ - AND independent=0 \ - ORDER BY timestamp;", - ); - if stmt_locations.is_none() { - // TODO: handle error - return; - } - let mut stmt_locations = stmt_locations.unwrap(); - - for (chat_id, locations_send_begin, locations_last_sent) in rows.filter_map(|r| match r - { - Ok(Some(v)) => Some(v), - _ => None, - }) { - // TODO: do I need to reset? - if !stmt_locations - .exists(params![1, locations_send_begin, locations_last_sent,]) - .unwrap_or_default() - { - // if there is no new location, there's nothing to send. - // however, maybe we want to bypass this test eg. 15 minutes - continue; + // be a bit tolerant as the timer may not align exactly with time(NULL) + if now - locations_last_sent < (60 - 3) { + Ok(None) + } else { + Ok(Some((chat_id, locations_send_begin, locations_last_sent))) } - // pending locations are attached automatically to every message, - // so also to this empty text message. - // DC_CMD_LOCATION is only needed to create a nicer subject. - // - // for optimisation and to avoid flooding the sending queue, - // we could sending these messages only if we're really online. - // the easiest way to determine this, is to check for an empty message queue. - // (might not be 100%, however, as positions are sent combined later - // and dc_set_location() is typically called periodically, this is ok) - let mut msg = dc_msg_new(context, 10); - (*msg).hidden = 1; - dc_param_set_int((*msg).param, 'S' as i32, 9); - dc_send_msg(context, chat_id as u32, msg); - dc_msg_unref(msg); - } - } - } + }, + |rows| { + let stmt_locations = dc_sqlite3_prepare( + context, + &context.sql, + "SELECT id \ + FROM locations \ + WHERE from_id=? \ + AND timestamp>=? \ + AND timestamp>? \ + AND independent=0 \ + ORDER BY timestamp;", + ); + if stmt_locations.is_none() { + // TODO: handle error + return Ok(()); + } + let mut stmt_locations = stmt_locations.unwrap(); + + for (chat_id, locations_send_begin, locations_last_sent) in + rows.filter_map(|r| match r { + Ok(Some(v)) => Some(v), + _ => None, + }) + { + // TODO: do I need to reset? + if !stmt_locations + .exists(params![1, locations_send_begin, locations_last_sent,]) + .unwrap_or_default() + { + // if there is no new location, there's nothing to send. + // however, maybe we want to bypass this test eg. 15 minutes + continue; + } + // pending locations are attached automatically to every message, + // so also to this empty text message. + // DC_CMD_LOCATION is only needed to create a nicer subject. + // + // for optimisation and to avoid flooding the sending queue, + // we could sending these messages only if we're really online. + // the easiest way to determine this, is to check for an empty message queue. + // (might not be 100%, however, as positions are sent combined later + // and dc_set_location() is typically called periodically, this is ok) + let mut msg = dc_msg_new(context, 10); + (*msg).hidden = 1; + dc_param_set_int((*msg).param, 'S' as i32, 9); + dc_send_msg(context, chat_id as u32, msg); + dc_msg_unref(msg); + } + Ok(()) + }, + ) + .unwrap(); // TODO: Better error handling if 0 != continue_streaming { schedule_MAYBE_SEND_LOCATIONS(context, 0x1); @@ -772,29 +742,21 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut let chat_id = (*job).foreign_id; let mut stock_str = 0 as *mut libc::c_char; - if let Some((send_begin, send_until)) = dc_sqlite3_prepare( - context, - &context.sql, + if let Ok((send_begin, send_until)) = context.sql.query_row( "SELECT locations_send_begin, locations_send_until FROM chats WHERE id=?", - ) - .and_then(|mut stmt| { - stmt.query_row(params![chat_id as i32], |row| { - Ok((row.get::<_, i64>(0)?, row.get::<_, i64>(1)?)) - }) - .ok() - }) { + params![chat_id as i32], + |row| Ok((row.get::<_, i64>(0)?, row.get::<_, i64>(1)?)), + ) { if !(send_begin != 0 && time() <= send_until) { // still streaming - // may happen as several calls to dc_send_locations_to_chat() // do not un-schedule pending DC_MAYBE_SEND_LOC_ENDED jobs if !(send_begin == 0 && send_until == 0) { // not streaming, device-message already sent - if dc_sqlite3_execute( - context, - &context.sql, + if context.sql.execute( "UPDATE chats SET locations_send_begin=0, locations_send_until=0 WHERE id=?", params![chat_id as i32], - ) { + ).is_ok() { stock_str = dc_stock_system_msg( context, 65, diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 3ab000688..a2bd7db83 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -1492,16 +1492,16 @@ unsafe fn create_or_lookup_adhoc_group( chat_ids = search_chat_ids_by_contact_ids(context, member_ids); if dc_array_get_cnt(chat_ids) > 0 { chat_ids_str = dc_array_get_string(chat_ids, b",\x00" as *const u8 as *const _); - let res = dc_sqlite3_prepare( - context, - &context.sql, + let res = context.sql.query_row( "SELECT c.id, c.blocked FROM chats c \ LEFT JOIN msgs m ON m.chat_id=c.id WHERE c.id IN(?) ORDER BY m.timestamp DESC, m.id DESC LIMIT 1;", - ).and_then(|mut stmt| stmt.query_row(params![as_str(chat_ids_str)], |row| { - Ok((row.get::<_, i32>(0)?, row.get::<_, i32>(1)?)) - }).ok()); + params![as_str(chat_ids_str)], + |row| { + Ok((row.get::<_, i32>(0)?, row.get::<_, i32>(1)?)) + } + ); - if let Some((id, id_blocked)) = res { + if let Ok((id, id_blocked)) = res { chat_id = id as u32; chat_id_blocked = id_blocked; /* success, chat found */ @@ -1607,32 +1607,26 @@ unsafe fn create_adhoc_grp_id(context: &Context, member_ids: *mut dc_array_t) -> .unwrap() .to_lowercase(); - let rows = if let Some(mut stmt) = dc_sqlite3_prepare( - context, - &context.sql, - "SELECT addr FROM contacts WHERE id IN(?) AND id!=1", - ) { - stmt.query_map(params![as_str(member_ids_str)], |row| { - row.get::<_, String>(0) - }) - .and_then(|res| res.collect::>>()) - .ok() - } else { - None - }; + let members = context + .sql + .query_map( + "SELECT addr FROM contacts WHERE id IN(?) AND id!=1", + params![as_str(member_ids_str)], + |row| row.get::<_, String>(0), + |rows| { + let mut addrs = rows.collect::, _>>()?; + addrs.sort(); + let mut acc = member_cs.clone(); + for addr in &addrs { + acc += ","; + acc += &addr.to_lowercase(); + } + Ok(acc) + }, + ) + .unwrap_or_else(|_| member_cs); free(member_ids_str as *mut libc::c_void); - let members = rows - .map(|mut addrs| { - addrs.sort(); - addrs.iter().fold(member_cs.clone(), |mut acc, addr| { - acc += ","; - acc += &addr.to_lowercase(); - acc - }) - }) - .unwrap_or_else(|| member_cs); - hex_hash(&members) as *mut _ } @@ -1673,47 +1667,40 @@ unsafe fn search_chat_ids_by_contact_ids( contact_ids_str = dc_array_get_string(contact_ids, b",\x00" as *const u8 as *const libc::c_char); - let rows = if let Some(mut stmt) = dc_sqlite3_prepare( - context, - &context.sql, + context.sql.query_map( "SELECT DISTINCT cc.chat_id, cc.contact_id FROM chats_contacts cc LEFT JOIN chats c ON c.id=cc.chat_id WHERE cc.chat_id IN(SELECT chat_id FROM chats_contacts WHERE contact_id IN(?)) AND c.type=120 AND cc.contact_id!=1 ORDER BY cc.chat_id, cc.contact_id;", - ) { - stmt.query_map( - params![as_str(contact_ids_str)], - |row| Ok((row.get::<_, i32>(0)?, row.get::<_, i32>(1)?)) - ).and_then(|res| res.collect::>>()).ok() - } else { - None - }; + params![as_str(contact_ids_str)], + |row| Ok((row.get::<_, i32>(0)?, row.get::<_, i32>(1)?)), + |rows| { + let mut last_chat_id = 0; + let mut matches = 0; + let mut mismatches = 0; - if let Some(rows) = rows { - let mut last_chat_id = 0; - let mut matches = 0; - let mut mismatches = 0; - - for (chat_id, contact_id) in rows { - if chat_id as u32 != last_chat_id { - if matches == dc_array_get_cnt(contact_ids) && mismatches == 0 { - dc_array_add_id(chat_ids, last_chat_id); + for row in rows { + let (chat_id, contact_id) = row?; + if chat_id as u32 != last_chat_id { + if matches == dc_array_get_cnt(contact_ids) && mismatches == 0 { + dc_array_add_id(chat_ids, last_chat_id); + } + last_chat_id = chat_id as u32; + matches = 0; + mismatches = 0; + } + if contact_id as u32 == dc_array_get_id(contact_ids, matches as size_t) { + matches += 1; + } else { + mismatches += 1; } - last_chat_id = chat_id as u32; - matches = 0; - mismatches = 0; } - if contact_id as u32 == dc_array_get_id(contact_ids, matches as size_t) { - matches += 1; - } else { - mismatches += 1; - } - } - if matches == dc_array_get_cnt(contact_ids) && mismatches == 0 { - dc_array_add_id(chat_ids, last_chat_id); + if matches == dc_array_get_cnt(contact_ids) && mismatches == 0 { + dc_array_add_id(chat_ids, last_chat_id); + } + Ok(()) } - } + ).unwrap(); // TODO: better error handling } } - free(contact_ids_str as *mut libc::c_void); dc_array_unref(contact_ids); @@ -1727,10 +1714,9 @@ unsafe fn check_verified_properties( to_ids: *const dc_array_t, failure_reason: *mut *mut libc::c_char, ) -> libc::c_int { - let mut everythings_okay: libc::c_int = 0; - let contact: *mut dc_contact_t = dc_contact_new(context); + let contact = dc_contact_new(context); - let verify_fail = |reason| { + let verify_fail = |reason: String| { *failure_reason = strdup(to_cstring(format!("{}. See \"Info\" for details.", reason)).as_ptr()); warn!(context, 0, "{}", reason); @@ -1741,13 +1727,13 @@ unsafe fn check_verified_properties( }; if !dc_contact_load_from_db(contact, &context.sql, from_id) { - verify_fail("Internal Error; cannot load contact"); + verify_fail("Internal Error; cannot load contact".into()); cleanup(); return 0; } if 0 == mimeparser.e2ee_helper.encrypted { - verify_fail("This message is not encrypted"); + verify_fail("This message is not encrypted".into()); cleanup(); return 0; } @@ -1760,14 +1746,14 @@ unsafe fn check_verified_properties( let peerstate = Peerstate::from_addr(context, &context.sql, as_str((*contact).addr)); if peerstate.is_none() || dc_contact_is_verified_ex(contact, peerstate.as_ref()) != 2 { - verify_fail("The sender of this message is not verified."); + verify_fail("The sender of this message is not verified.".into()); cleanup(); return 0; } if let Some(peerstate) = peerstate { if !peerstate.has_verified_key(&mimeparser.e2ee_helper.signatures) { - verify_fail("The message was sent with non-verified encryption."); + verify_fail("The message was sent with non-verified encryption.".into()); cleanup(); return 0; } @@ -1778,65 +1764,65 @@ unsafe fn check_verified_properties( let to_ids_str = to_string(to_ids_str_c); free(to_ids_str_c as *mut libc::c_void); - let rows = if let Some(mut stmt) = dc_sqlite3_prepare( - context, - &context.sql, - "SELECT c.addr, LENGTH(ps.verified_key_fingerprint) FROM contacts c \ - LEFT JOIN acpeerstates ps ON c.addr=ps.addr WHERE c.id IN(?) ", - ) { - stmt.query_map(params![&to_ids_str], |row| { - Ok((row.get::<_, String>(0)?, row.get::<_, i32>(1)?)) - }) - .and_then(|res| res.collect::>>()) - .ok() - } else { - None - }; + let ok = context + .sql + .query_map( + "SELECT c.addr, LENGTH(ps.verified_key_fingerprint) FROM contacts c \ + LEFT JOIN acpeerstates ps ON c.addr=ps.addr WHERE c.id IN(?) ", + params![&to_ids_str], + |row| Ok((row.get::<_, String>(0)?, row.get::<_, i32>(1)?)), + |rows| { + for row in rows { + let (to_addr, mut is_verified) = row?; + let mut peerstate = Peerstate::from_addr(context, &context.sql, &to_addr); + if mimeparser.e2ee_helper.gossipped_addr.contains(&to_addr) + && peerstate.is_some() + { + let peerstate = peerstate.as_mut().unwrap(); - if let Some(rows) = rows { - for (to_addr, mut is_verified) in rows { - let mut peerstate = Peerstate::from_addr(context, &context.sql, &to_addr); - if mimeparser.e2ee_helper.gossipped_addr.contains(&to_addr) && peerstate.is_some() { - let peerstate = peerstate.as_mut().unwrap(); - - // if we're here, we know the gossip key is verified: - // - use the gossip-key as verified-key if there is no verified-key - // - OR if the verified-key does not match public-key or gossip-key - // (otherwise a verified key can _only_ be updated through QR scan which might be annoying, - // see https://github.com/nextleap-project/countermitm/issues/46 for a discussion about this point) - if 0 == is_verified - || peerstate.verified_key_fingerprint != peerstate.public_key_fingerprint - && peerstate.verified_key_fingerprint != peerstate.gossip_key_fingerprint - { - info!( - context, - 0, - "{} has verfied {}.", - as_str((*contact).addr), - to_addr, - ); - let fp = peerstate.gossip_key_fingerprint.clone(); - if let Some(fp) = fp { - peerstate.set_verified(0, &fp, 2); - peerstate.save_to_db(&context.sql, false); - is_verified = 1; + // if we're here, we know the gossip key is verified: + // - use the gossip-key as verified-key if there is no verified-key + // - OR if the verified-key does not match public-key or gossip-key + // (otherwise a verified key can _only_ be updated through QR scan which might be annoying, + // see https://github.com/nextleap-project/countermitm/issues/46 for a discussion about this point) + if 0 == is_verified + || peerstate.verified_key_fingerprint + != peerstate.public_key_fingerprint + && peerstate.verified_key_fingerprint + != peerstate.gossip_key_fingerprint + { + info!( + context, + 0, + "{} has verfied {}.", + as_str((*contact).addr), + to_addr, + ); + let fp = peerstate.gossip_key_fingerprint.clone(); + if let Some(fp) = fp { + peerstate.set_verified(0, &fp, 2); + peerstate.save_to_db(&context.sql, false); + is_verified = 1; + } + } + } + if 0 == is_verified { + verify_fail(format!( + "{} is not a member of this verified group", + to_addr + )); + cleanup(); + return Err(failure::format_err!("not a valid memember").into()); } } - } - if 0 == is_verified { - verify_fail(&format!( - "{} is not a member of this verified group", - to_addr - )); - cleanup(); - return 0; - } - } - everythings_okay = 1; - } + Ok(()) + }, + ) + .is_ok(); // TODO: Better default cleanup(); - everythings_okay + + ok as libc::c_int } unsafe fn set_better_msg(mime_parser: &dc_mimeparser_t, better_msg: *mut *mut libc::c_char) { @@ -1932,10 +1918,16 @@ fn is_known_rfc724_mid(context: &Context, rfc724_mid: *const libc::c_char) -> li if rfc724_mid.is_null() { return 0; } - dc_sqlite3_prepare( - context, &context.sql, - "SELECT m.id FROM msgs m LEFT JOIN chats c ON m.chat_id=c.id WHERE m.rfc724_mid=? AND m.chat_id>9 AND c.blocked=0;" - ).and_then(|mut stmt| stmt.exists(params![as_str(rfc724_mid)]).ok()).unwrap_or_default() as libc::c_int + context + .sql + .exists( + "SELECT m.id FROM msgs m \ + LEFT JOIN chats c ON m.chat_id=c.id \ + WHERE m.rfc724_mid=? \ + AND m.chat_id>9 AND c.blocked=0;", + params![as_str(rfc724_mid)], + ) + .unwrap_or_default() as libc::c_int } unsafe fn dc_is_reply_to_messenger_message( @@ -2010,13 +2002,13 @@ fn is_msgrmsg_rfc724_mid(context: &Context, rfc724_mid: *const libc::c_char) -> if rfc724_mid.is_null() { return 0; } - dc_sqlite3_prepare( - context, - &context.sql, - "SELECT id FROM msgs WHERE rfc724_mid=? AND msgrmsg!=0 AND chat_id>9;", - ) - .and_then(|mut stmt| stmt.exists(params![as_str(rfc724_mid)]).ok()) - .unwrap_or_default() as libc::c_int + context + .sql + .exists( + "SELECT id FROM msgs WHERE rfc724_mid=? AND msgrmsg!=0 AND chat_id>9;", + params![as_str(rfc724_mid)], + ) + .unwrap_or_default() as libc::c_int } unsafe fn dc_add_or_lookup_contacts_by_address_list(