├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── active.rs ├── basic.rs ├── bot.rs ├── component.rs ├── roster.rs └── uuid.rs └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | target 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | language: rust 3 | rust: nightly 4 | addons: 5 | apt: 6 | packages: 7 | - libstrophe-dev 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libstrophe-sys" 3 | license = "MIT" 4 | version = "0.0.2" 5 | authors = ["Cristian Kubis "] 6 | description = "FFI bindings to libstrophe a simple, lightweight library for writing XMPP clients." 7 | documentation = "https://docs.rs/libstrophe-sys" 8 | homepage = "https://github.com/tsurai/libstrophe-sys" 9 | repository = "https://github.com/tsurai/libstrophe-sys" 10 | readme = "README.md" 11 | keywords = ["strophe","libstrophe", "xmpp", "jabber"] 12 | 13 | [badges] 14 | travis-ci = { repository = "tsurai/libstrophe-sys" } 15 | 16 | [dependencies] 17 | libc = "^0.2.23" 18 | 19 | [lib] 20 | name = "strophe" 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Cristian Kubis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | libstrophe-sys [![Build Status](https://travis-ci.org/tsurai/libstrophe-sys.svg?branch=master)](https://travis-ci.org/tsurai/libstrophe-sys) 2 | ==== 3 | 4 | Rust bindings for [libstrophe](https://github.com/strophe/libstrophe) ([http://strophe.im/](http://strophe.im/libstrophe)) a simple, lightweight library for writing XMPP clients. 5 | 6 | *Note:* Please compile the libstrophe C library from sources if you get unresolved symbol errors because the version in your packagemanager might be too old. 7 | -------------------------------------------------------------------------------- /examples/active.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_variables)] 2 | 3 | extern crate libc; 4 | extern crate strophe; 5 | 6 | use std::{env, mem, ptr, str}; 7 | use std::ffi::{CStr, CString}; 8 | 9 | use libc::*; 10 | use strophe::*; 11 | 12 | /* 13 | * Basic handler function demonstrations 14 | * 15 | * Rust version of the original active.c example file 16 | * https://github.com/strophe/libstrophe/blob/master/examples/active.c 17 | */ 18 | 19 | extern "C" fn reply_handler(conn: *const xmpp_conn_t, 20 | stanza: *const xmpp_stanza_t, 21 | userdata: *const c_void) -> i32 { 22 | unsafe { 23 | let stanza_type = ptr_to_str(xmpp_stanza_get_type(stanza)); 24 | if stanza_type != "error" { 25 | let query = xmpp_stanza_get_child_by_name(stanza, str_to_ptr("query")); 26 | 27 | println!("Active Session:"); 28 | 29 | let mut item = xmpp_stanza_get_children(query); 30 | while !item.is_null() { 31 | let jid = get_stanza_attr(item, "jid").unwrap(); 32 | println!("\t {}", jid); 33 | 34 | item = xmpp_stanza_get_next(item); 35 | } 36 | } else { 37 | println!("error: query failed"); 38 | } 39 | 40 | xmpp_disconnect(conn); 41 | 42 | return 0; 43 | } 44 | } 45 | 46 | extern "C" fn conn_handler(conn: *const xmpp_conn_t, 47 | status: xmpp_conn_event_t, 48 | error: i32, 49 | stream_error: *const xmpp_stream_error_t, 50 | userdata: *const c_void) { 51 | unsafe { 52 | let ctx: *mut xmpp_ctx_t = mem::transmute(userdata); 53 | 54 | if status != XMPP_CONN_CONNECT { 55 | println!("disconnected"); 56 | xmpp_stop(ctx); 57 | return; 58 | } 59 | 60 | // create iq stanza for the request 61 | let iq = xmpp_stanza_new(ctx); 62 | xmpp_stanza_set_name(iq, str_to_ptr("iq")); 63 | xmpp_stanza_set_type(iq, str_to_ptr("get")); 64 | xmpp_stanza_set_id(iq, str_to_ptr("active1")); 65 | xmpp_stanza_set_to(iq, str_to_ptr("jabber.ccc.de")); 66 | 67 | // create query to request active resources on the server 68 | let query = xmpp_stanza_new(ctx); 69 | xmpp_stanza_set_name(query, str_to_ptr("query")); 70 | xmpp_stanza_set_ns(query, str_to_ptr(XMPP_NS_DISCO_ITEMS)); 71 | xmpp_stanza_set_attribute(query, 72 | str_to_ptr("node"), 73 | str_to_ptr("sessions")); 74 | 75 | xmpp_stanza_add_child(iq, query); 76 | 77 | // release query stanza because it belongs to iq now 78 | xmpp_stanza_release(query); 79 | 80 | // register reply handler 81 | xmpp_id_handler_add(conn, 82 | Some(reply_handler), 83 | str_to_ptr("active1"), 84 | mem::transmute(ctx)); 85 | 86 | // send the stanza 87 | xmpp_send(conn, iq); 88 | 89 | // release the iq stanza 90 | xmpp_stanza_release(iq); 91 | } 92 | } 93 | 94 | fn main() { 95 | let args: Vec = env::args().collect(); 96 | 97 | if args.len() < 3 { 98 | println!("usage: ./active "); 99 | return; 100 | } 101 | 102 | let jid = args[1].clone(); 103 | let pwd = args[2].clone(); 104 | 105 | unsafe { 106 | // initialize library 107 | xmpp_initialize(); 108 | 109 | // create a context 110 | let ctx = xmpp_ctx_new(ptr::null(), ptr::null()); 111 | 112 | // create a connection 113 | let conn = xmpp_conn_new(ctx); 114 | 115 | // setup authentication information 116 | xmpp_conn_set_jid(conn, str_to_ptr(jid)); 117 | xmpp_conn_set_pass(conn, str_to_ptr(pwd)); 118 | 119 | // initialize the connection 120 | xmpp_connect_client(conn, 121 | ptr::null(), 122 | 0, 123 | Some(conn_handler), 124 | ctx as *const c_void); 125 | 126 | // start the event loop 127 | xmpp_run(ctx); 128 | 129 | // release the connection and context 130 | xmpp_conn_release(conn); 131 | xmpp_ctx_free(ctx); 132 | 133 | // shutdown library 134 | xmpp_shutdown(); 135 | } 136 | } 137 | 138 | fn get_stanza_attr<'a>(item: *const xmpp_stanza_t, 139 | attr: &str) -> Option<&'a str> { 140 | let res = unsafe { xmpp_stanza_get_attribute(item, str_to_ptr(attr)) }; 141 | 142 | if !res.is_null() { 143 | Some(ptr_to_str(res)) 144 | } else { 145 | None 146 | } 147 | } 148 | 149 | fn str_to_ptr>>(input: T) -> *const i8 { 150 | CString::new(input).unwrap().as_bytes_with_nul().as_ptr() as *const i8 151 | } 152 | 153 | fn ptr_to_str<'a>(ptr: *const c_char) -> &'a str { 154 | unsafe { 155 | str::from_utf8(CStr::from_ptr(ptr).to_bytes()).unwrap() 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /examples/basic.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_variables)] 2 | 3 | extern crate libc; 4 | extern crate strophe; 5 | 6 | use std::{env, mem, ptr}; 7 | use std::ffi::CString; 8 | 9 | use libc::*; 10 | use strophe::*; 11 | 12 | /* 13 | * Connection settings example 14 | * 15 | * Rust version of the original basic.c example file 16 | * https://github.com/strophe/libstrophe/blob/master/examples/basic.c 17 | */ 18 | 19 | extern "C" fn conn_handler(conn: *const xmpp_conn_t, 20 | status: xmpp_conn_event_t, 21 | error: i32, 22 | stream_error: *const xmpp_stream_error_t, 23 | userdata: *const c_void) { 24 | let ctx: *mut xmpp_ctx_t = unsafe { mem::transmute(userdata) }; 25 | 26 | if status == XMPP_CONN_CONNECT { 27 | println!("connected"); 28 | let secured = unsafe { xmpp_conn_is_secured(conn) }; 29 | println!("connection is {}", 30 | if secured == 1 { "secured" } else { "NOT secured" }); 31 | unsafe { xmpp_disconnect(conn) }; 32 | } else { 33 | println!("disconnected"); 34 | unsafe { xmpp_stop(ctx) }; 35 | } 36 | } 37 | 38 | fn main() { 39 | let args: Vec = env::args().collect(); 40 | 41 | let mut flags: i64 = 0; 42 | let mut num_flags = 0; 43 | 44 | for arg in args.iter().skip(1).by_ref() { 45 | match &arg[..] { 46 | "--disable-tls" => { 47 | flags |= XMPP_CONN_FLAG_DISABLE_TLS; 48 | }, 49 | "--mandatory-tls" => { 50 | flags |= XMPP_CONN_FLAG_MANDATORY_TLS; 51 | }, 52 | "--legacy-ssl" => { 53 | flags |= XMPP_CONN_FLAG_LEGACY_SSL; 54 | }, 55 | _ => { 56 | break; 57 | } 58 | } 59 | num_flags += 1; 60 | } 61 | 62 | if (args.len() - num_flags) < 3 { 63 | println!("usage: ./basic [options] []\n 64 | Options:\n 65 | --disable-tls Disable TLS\n 66 | --mandatory-tls Deny plaintext connection\n 67 | --legacy-ssl Use old style SSL\n\n 68 | Note: --disable-tls conflicts with --mandatory-tls or --legacy-ssl"); 69 | return; 70 | } 71 | 72 | let jid = args[num_flags + 1].clone(); 73 | let pwd = args[num_flags + 2].clone(); 74 | 75 | let host = if num_flags + 3 < args.len() { 76 | Some(args[num_flags + 3].clone()) 77 | } else { 78 | None 79 | }; 80 | 81 | unsafe { 82 | // initialize library 83 | xmpp_initialize(); 84 | 85 | // create a context 86 | let log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG); 87 | let ctx = xmpp_ctx_new(ptr::null(), log); 88 | 89 | // create a connection 90 | let conn = xmpp_conn_new(ctx); 91 | 92 | // configure connection properties (optional) 93 | xmpp_conn_set_flags(conn, flags); 94 | 95 | // setup authentication information 96 | xmpp_conn_set_jid(conn, str_to_ptr(jid)); 97 | xmpp_conn_set_pass(conn, str_to_ptr(pwd)); 98 | 99 | // initialize the connection 100 | xmpp_connect_client(conn, 101 | host.map_or(ptr::null(), |x| str_to_ptr(x)), 102 | 0, 103 | Some(conn_handler), 104 | ctx as *const c_void); 105 | 106 | // start the event loop 107 | xmpp_run(ctx); 108 | 109 | // release the connection and context 110 | xmpp_conn_release(conn); 111 | xmpp_ctx_free(ctx); 112 | 113 | // shutdown library 114 | xmpp_shutdown(); 115 | } 116 | } 117 | 118 | fn str_to_ptr>>(input: T) -> *const i8 { 119 | CString::new(input).unwrap().as_bytes_with_nul().as_ptr() as *const i8 120 | } 121 | -------------------------------------------------------------------------------- /examples/bot.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_variables)] 2 | 3 | extern crate libc; 4 | extern crate strophe; 5 | 6 | use std::{env, mem, ptr, str}; 7 | use std::ffi::{CStr, CString}; 8 | 9 | use libc::*; 10 | use strophe::*; 11 | 12 | /* 13 | * Simple echoing bot example 14 | * 15 | * Rust version of the original bot.c example file 16 | * https://github.com/strophe/libstrophe/blob/master/examples/bot.c 17 | */ 18 | 19 | extern "C" fn version_handler(conn: *const xmpp_conn_t, 20 | stanza: *const xmpp_stanza_t, 21 | userdata: *const c_void) -> i32 { 22 | unsafe { 23 | let ctx: *mut xmpp_ctx_t = mem::transmute(userdata); 24 | 25 | println!("Received version request from {}", 26 | ptr_to_str(xmpp_stanza_get_from(stanza))); 27 | 28 | let reply = xmpp_stanza_reply(stanza); 29 | xmpp_stanza_set_type(reply, str_to_ptr("result")); 30 | 31 | let query = xmpp_stanza_new(ctx); 32 | xmpp_stanza_set_name(query, str_to_ptr("query")); 33 | 34 | let ns = xmpp_stanza_get_ns(xmpp_stanza_get_children(stanza)); 35 | if !ns.is_null() { 36 | xmpp_stanza_set_ns(query, ns); 37 | } 38 | 39 | let name = xmpp_stanza_new(ctx); 40 | xmpp_stanza_set_name(name, str_to_ptr("name")); 41 | xmpp_stanza_add_child(query, name); 42 | 43 | let mut text = xmpp_stanza_new(ctx); 44 | xmpp_stanza_set_text(text, str_to_ptr("libstrophe example bot")); 45 | xmpp_stanza_add_child(name, text); 46 | 47 | let version = xmpp_stanza_new(ctx); 48 | xmpp_stanza_set_name(version, str_to_ptr("version")); 49 | xmpp_stanza_add_child(query, version); 50 | 51 | text = xmpp_stanza_new(ctx); 52 | xmpp_stanza_set_text(text, str_to_ptr("1.0")); 53 | xmpp_stanza_add_child(version, text); 54 | 55 | xmpp_stanza_add_child(reply, query); 56 | 57 | xmpp_send(conn, reply); 58 | xmpp_stanza_release(reply); 59 | 60 | return 1; 61 | } 62 | } 63 | 64 | // handler for connection events 65 | extern "C" fn message_handler(conn: *const xmpp_conn_t, 66 | stanza: *const xmpp_stanza_t, 67 | userdata: *const c_void) -> i32 { 68 | unsafe { 69 | let ctx: *mut xmpp_ctx_t = mem::transmute(userdata); 70 | 71 | if xmpp_stanza_get_child_by_name(stanza, str_to_ptr("body")).is_null() { 72 | return 1; 73 | } 74 | 75 | if !xmpp_stanza_get_type(stanza).is_null() 76 | && ptr_to_str(xmpp_stanza_get_type(stanza)) == "error" { 77 | return 1; 78 | } 79 | 80 | let intext = xmpp_stanza_get_text( 81 | xmpp_stanza_get_child_by_name(stanza, str_to_ptr("body"))); 82 | 83 | println!("Incoming message from {}: {}", ptr_to_str(xmpp_stanza_get_from(stanza)), ptr_to_str(intext)); 84 | 85 | let reply = xmpp_stanza_reply(stanza); 86 | if xmpp_stanza_get_type(reply).is_null() { 87 | xmpp_stanza_set_type(reply, str_to_ptr("char")); 88 | } 89 | 90 | let body = xmpp_stanza_new(ctx); 91 | xmpp_stanza_set_name(body, str_to_ptr("body")); 92 | 93 | let reply_text = format!("{} to you too!", ptr_to_str(intext)); 94 | xmpp_free(ctx, intext as *const c_void); 95 | 96 | let text = xmpp_stanza_new(ctx); 97 | xmpp_stanza_set_text(text, str_to_ptr(reply_text)); 98 | xmpp_stanza_add_child(body, text); 99 | xmpp_stanza_add_child(reply, body); 100 | xmpp_stanza_release(body); 101 | xmpp_stanza_release(text); 102 | 103 | xmpp_send(conn, reply); 104 | xmpp_stanza_release(reply); 105 | 106 | return 1; 107 | } 108 | } 109 | 110 | extern "C" fn conn_handler(conn: *const xmpp_conn_t, 111 | status: xmpp_conn_event_t, 112 | error: i32, 113 | stream_error: *const xmpp_stream_error_t, 114 | userdata: *const c_void) { 115 | unsafe { 116 | let ctx: *mut xmpp_ctx_t = mem::transmute(userdata); 117 | 118 | if status == XMPP_CONN_CONNECT { 119 | println!("connected"); 120 | 121 | xmpp_handler_add(conn, 122 | Some(version_handler), 123 | str_to_ptr("jabber:iq:version"), 124 | str_to_ptr("iq"), 125 | ptr::null(), 126 | ctx as *const c_void); 127 | xmpp_handler_add(conn, 128 | Some(message_handler), 129 | ptr::null(), 130 | str_to_ptr("message"), 131 | ptr::null(), 132 | ctx as *const c_void); 133 | 134 | let pres = xmpp_stanza_new(ctx); 135 | xmpp_stanza_set_name(pres, str_to_ptr("presence")); 136 | xmpp_send(conn, pres); 137 | xmpp_stanza_release(pres); 138 | } else { 139 | println!("disconnected"); 140 | xmpp_stop(ctx); 141 | } 142 | } 143 | } 144 | 145 | fn main() { 146 | let args: Vec = env::args().collect(); 147 | 148 | if args.len() < 3 { 149 | println!("usage: ./bot "); 150 | return; 151 | } 152 | 153 | let jid = args[1].clone(); 154 | let pwd = args[2].clone(); 155 | 156 | unsafe { 157 | // initialize library 158 | xmpp_initialize(); 159 | 160 | // create a context 161 | let ctx = xmpp_ctx_new(ptr::null(), ptr::null()); 162 | 163 | // create a connection 164 | let conn = xmpp_conn_new(ctx); 165 | 166 | // setup authentication information 167 | xmpp_conn_set_jid(conn, str_to_ptr(jid)); 168 | xmpp_conn_set_pass(conn, str_to_ptr(pwd)); 169 | 170 | // initialize the connection 171 | xmpp_connect_client(conn, 172 | ptr::null(), 173 | 0, 174 | Some(conn_handler), 175 | ctx as *const c_void); 176 | 177 | // start the event loop 178 | xmpp_run(ctx); 179 | 180 | // release the connection and context 181 | xmpp_conn_release(conn); 182 | xmpp_ctx_free(ctx); 183 | 184 | // shutdown library 185 | xmpp_shutdown(); 186 | } 187 | } 188 | 189 | fn str_to_ptr>>(input: T) -> *const i8 { 190 | CString::new(input).unwrap().as_bytes_with_nul().as_ptr() as *const i8 191 | } 192 | 193 | fn ptr_to_str<'a>(ptr: *const c_char) -> &'a str { 194 | unsafe { 195 | str::from_utf8(CStr::from_ptr(ptr).to_bytes()).unwrap() 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /examples/component.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_variables)] 2 | 3 | extern crate libc; 4 | extern crate strophe; 5 | 6 | use std::{env, mem, ptr}; 7 | use std::ffi::CString; 8 | use std::str::FromStr; 9 | 10 | use libc::*; 11 | use strophe::*; 12 | 13 | /* 14 | * Demonstration of a simple connection to a server 15 | * as an external component (See XEP-0114) 16 | * 17 | * Rust version of the original component.c example file 18 | * https://github.com/strophe/libstrophe/blob/master/examples/component.c 19 | */ 20 | 21 | extern "C" fn conn_handler(conn: *const xmpp_conn_t, 22 | status: xmpp_conn_event_t, 23 | error: i32, 24 | stream_error: *const xmpp_stream_error_t, 25 | userdata: *const c_void) { 26 | let ctx: *mut xmpp_ctx_t = unsafe { mem::transmute(userdata) }; 27 | 28 | if status == XMPP_CONN_CONNECT { 29 | println!("connected"); 30 | unsafe {xmpp_disconnect(conn) }; 31 | } else { 32 | println!("disconnected"); 33 | unsafe { xmpp_stop(ctx) }; 34 | } 35 | } 36 | 37 | fn main() { 38 | let args: Vec = env::args().collect(); 39 | 40 | if args.len() < 4 || args.len() > 5 { 41 | println!("usage: ./components [port]\n"); 42 | return; 43 | } 44 | 45 | let jid = args[1].clone(); 46 | let pwd = args[2].clone(); 47 | let host = args[3].clone(); 48 | 49 | let port: u16 = if args.len() == 5 { 50 | match u16::from_str(&args[4][..]) { 51 | Ok(p) => p, 52 | Err(err) => { 53 | println!("Can't parse port: {}", err); 54 | return; 55 | } 56 | } 57 | } else { 58 | 0 59 | }; 60 | 61 | unsafe { 62 | // initialize library 63 | xmpp_initialize(); 64 | 65 | // create a context with logger set to debug level 66 | let log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG); 67 | let ctx = xmpp_ctx_new(ptr::null(), log); 68 | 69 | // create a connection 70 | let conn = xmpp_conn_new(ctx); 71 | 72 | // setup authentication information 73 | xmpp_conn_set_jid(conn, str_to_ptr(jid)); 74 | xmpp_conn_set_pass(conn, str_to_ptr(pwd)); 75 | 76 | // initialize the connection 77 | xmpp_connect_client(conn, 78 | str_to_ptr(host), 79 | port, 80 | Some(conn_handler), 81 | ctx as *const c_void); 82 | 83 | // start the event loop 84 | xmpp_run(ctx); 85 | 86 | // release the connection and context 87 | xmpp_conn_release(conn); 88 | xmpp_ctx_free(ctx); 89 | 90 | // shutdown library 91 | xmpp_shutdown(); 92 | } 93 | } 94 | 95 | fn str_to_ptr>>(input: T) -> *const i8 { 96 | CString::new(input).unwrap().as_bytes_with_nul().as_ptr() as *const i8 97 | } 98 | -------------------------------------------------------------------------------- /examples/roster.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_variables)] 2 | 3 | extern crate libc; 4 | extern crate strophe; 5 | 6 | use std::{env, mem, ptr, str}; 7 | use std::ffi::{CStr, CString}; 8 | 9 | use libc::*; 10 | use strophe::*; 11 | 12 | /* 13 | * Example printing out the user's roster 14 | * 15 | * Rust version of the original roster.c example file 16 | * https://github.com/strophe/libstrophe/blob/master/examples/roster.c 17 | */ 18 | 19 | extern "C" fn reply_handler(conn: *const xmpp_conn_t, 20 | stanza: *const xmpp_stanza_t, 21 | userdata: *const c_void) -> i32 { 22 | unsafe { 23 | let stanza_type = ptr_to_str(xmpp_stanza_get_type(stanza)); 24 | 25 | if stanza_type != "error" { 26 | let query = xmpp_stanza_get_child_by_name(stanza, str_to_ptr("query")); 27 | 28 | println!("Roster:"); 29 | 30 | let mut item = xmpp_stanza_get_children(query); 31 | while !item.is_null() { 32 | let jid = get_stanza_attr(item, "jid").unwrap(); 33 | let sub = get_stanza_attr(item, "subscription").unwrap(); 34 | 35 | if let Some(name) = get_stanza_attr(item, "name") { 36 | println!("\t {} ({}) sub={}", name, jid, sub); 37 | } else { 38 | println!("\t {} sub={}", jid, sub); 39 | } 40 | 41 | item = xmpp_stanza_get_next(item); 42 | } 43 | } else { 44 | println!("error: query failed"); 45 | } 46 | 47 | xmpp_disconnect(conn); 48 | 49 | return 0; 50 | } 51 | } 52 | 53 | extern "C" fn conn_handler(conn: *const xmpp_conn_t, 54 | status: xmpp_conn_event_t, 55 | error: i32, 56 | stream_error: *const xmpp_stream_error_t, 57 | userdata: *const c_void) { 58 | unsafe { 59 | let ctx: *mut xmpp_ctx_t = mem::transmute(userdata); 60 | 61 | if status != XMPP_CONN_CONNECT { 62 | println!("disconnected"); 63 | xmpp_stop(ctx); 64 | return; 65 | } 66 | 67 | // create iq stanza for the request 68 | let iq = xmpp_stanza_new(ctx); 69 | xmpp_stanza_set_name(iq, str_to_ptr("iq")); 70 | xmpp_stanza_set_type(iq, str_to_ptr("get")); 71 | xmpp_stanza_set_id(iq, str_to_ptr("roster1")); 72 | 73 | // create query to request the roster 74 | let query = xmpp_stanza_new(ctx); 75 | xmpp_stanza_set_name(query, str_to_ptr("query")); 76 | xmpp_stanza_set_ns(query, str_to_ptr(XMPP_NS_ROSTER)); 77 | 78 | xmpp_stanza_add_child(iq, query); 79 | 80 | // release query stanza because it belongs to iq now 81 | xmpp_stanza_release(query); 82 | 83 | // register reply handler 84 | xmpp_id_handler_add(conn, 85 | Some(reply_handler), 86 | str_to_ptr("roster1"), 87 | mem::transmute(ctx)); 88 | 89 | // send the stanza 90 | xmpp_send(conn, iq); 91 | 92 | // release the iq stanza 93 | xmpp_stanza_release(iq); 94 | } 95 | } 96 | 97 | fn main() { 98 | let args: Vec = env::args().collect(); 99 | 100 | if args.len() < 3 { 101 | println!("usage: ./roster "); 102 | return; 103 | } 104 | 105 | let jid = args[1].clone(); 106 | let pwd = args[2].clone(); 107 | 108 | unsafe { 109 | // initialize library 110 | xmpp_initialize(); 111 | 112 | // create a context 113 | let ctx = xmpp_ctx_new(ptr::null(), ptr::null()); 114 | 115 | // create a connection 116 | let conn = xmpp_conn_new(ctx); 117 | 118 | // setup authentication information 119 | xmpp_conn_set_jid(conn, str_to_ptr(jid)); 120 | xmpp_conn_set_pass(conn, str_to_ptr(pwd)); 121 | 122 | // initialize the connection 123 | xmpp_connect_client(conn, 124 | ptr::null(), 125 | 0, 126 | Some(conn_handler), 127 | ctx as *const c_void); 128 | 129 | // start the event loop 130 | xmpp_run(ctx); 131 | 132 | // release the connection and context 133 | xmpp_conn_release(conn); 134 | xmpp_ctx_free(ctx); 135 | 136 | // shutdown library 137 | xmpp_shutdown(); 138 | } 139 | } 140 | 141 | fn get_stanza_attr<'a>(item: *const xmpp_stanza_t, 142 | attr: &str) -> Option<&'a str> { 143 | let res = unsafe { xmpp_stanza_get_attribute(item, str_to_ptr(attr)) }; 144 | 145 | if !res.is_null() { 146 | Some(ptr_to_str(res)) 147 | } else { 148 | None 149 | } 150 | } 151 | 152 | fn str_to_ptr>>(input: T) -> *const i8 { 153 | CString::new(input).unwrap().as_bytes_with_nul().as_ptr() as *const i8 154 | } 155 | 156 | fn ptr_to_str<'a>(ptr: *const c_char) -> &'a str { 157 | unsafe { str::from_utf8(CStr::from_ptr(ptr).to_bytes()).unwrap() } 158 | } 159 | -------------------------------------------------------------------------------- /examples/uuid.rs: -------------------------------------------------------------------------------- 1 | extern crate libc; 2 | extern crate strophe; 3 | 4 | use std::{ptr, str}; 5 | use std::ffi::CStr; 6 | 7 | use libc::*; 8 | use strophe::*; 9 | 10 | /* 11 | * Example generating a new uuid 12 | * 13 | * Rust version of the original uuid.c example file 14 | * https://github.com/strophe/libstrophe/blob/master/examples/uuid.c 15 | */ 16 | 17 | fn main() { 18 | unsafe { 19 | let ctx = xmpp_ctx_new(ptr::null(), ptr::null()); 20 | let uuid = xmpp_uuid_gen(ctx); 21 | 22 | if !uuid.is_null() { 23 | println!("{}", ptr_to_str(uuid)); 24 | xmpp_free(ctx, uuid as *const c_void); 25 | } else { 26 | println!("couldn't allocate memory"); 27 | } 28 | 29 | xmpp_ctx_free(ctx); 30 | } 31 | } 32 | 33 | fn ptr_to_str<'a>(ptr: *const c_char) -> &'a str { 34 | unsafe { 35 | str::from_utf8(CStr::from_ptr(ptr).to_bytes()).unwrap() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | 3 | #![crate_name = "strophe"] 4 | #![crate_type = "lib"] 5 | #![crate_type = "dylib"] 6 | #![crate_type = "rlib"] 7 | 8 | extern crate libc; 9 | 10 | use libc::*; 11 | 12 | pub use xmpp_log_level_t::*; 13 | pub use xmpp_conn_type_t::*; 14 | pub use xmpp_conn_event_t::*; 15 | pub use xmpp_error_type_t::*; 16 | 17 | pub type xmpp_handler = Option c_int>; 20 | pub type xmpp_conn_handler = Option; 25 | pub type xmpp_timed_handler = Option c_int>; 28 | pub type xmpp_log_handler = Option; 33 | pub type xmpp_open_handler = Option; 34 | 35 | pub type hash_free_func = Option; 36 | 37 | #[repr(C)] 38 | pub struct xmpp_conn_t { 39 | _ref: c_uint, 40 | ctx: *mut xmpp_ctx_t, 41 | _type: c_int, 42 | 43 | state: c_int, 44 | timeout_stamp: uint64_t, 45 | error: c_int, 46 | stream_error: *mut xmpp_stream_error_t, 47 | sock: c_int, 48 | tls: *mut c_void, 49 | 50 | tls_support: c_int, 51 | tls_disabled: c_int, 52 | tls_failed: c_int, 53 | sasl_support: c_int, 54 | secured: c_int, 55 | 56 | bind_required: c_int, 57 | session_required: c_int, 58 | 59 | lang: *mut c_char, 60 | domain: *mut c_char, 61 | connectdomain: *mut c_char, 62 | connectport: *mut c_char, 63 | jid: *mut c_char, 64 | pass: *mut c_char, 65 | bound_jid: *mut c_char, 66 | stream_id: *mut c_char, 67 | 68 | blocking_seed: c_int, 69 | send_queue_max: c_int, 70 | send_queue_len: c_int, 71 | send_queue_head: *mut c_void, 72 | send_queue_tail: *mut c_void, 73 | 74 | reset_parser: c_int, 75 | parser: *mut c_void, 76 | connect_timeout: c_uint, 77 | open_handler: xmpp_open_handler, 78 | authenticated: c_int, 79 | 80 | conn_handler: xmpp_conn_handler, 81 | userdata: *mut c_void, 82 | 83 | timed_handlers: *mut c_void, 84 | id_handlers: *mut hash_t, 85 | handlers: *mut c_void, 86 | } 87 | 88 | #[repr(C)] 89 | pub struct xmpp_stanza_t { 90 | _ref: c_int, 91 | ctx: *mut xmpp_ctx_t, 92 | _type: c_int, 93 | prev: *mut xmpp_stanza_t, 94 | next: *mut xmpp_stanza_t, 95 | children: *mut xmpp_stanza_t, 96 | parent: *mut xmpp_stanza_t, 97 | data: *mut c_char, 98 | attributes: *mut hash_t, 99 | } 100 | 101 | #[repr(C)] 102 | pub struct xmpp_ctx_t { 103 | mem: *const xmpp_mem_t, 104 | log: *const xmpp_log_t, 105 | loop_status: c_int, 106 | connlist: *mut xmpp_connlist_t, 107 | } 108 | 109 | #[repr(C)] 110 | pub struct xmpp_connlist_t { 111 | conn: *mut xmpp_conn_t, 112 | next: *mut xmpp_connlist_t, 113 | } 114 | 115 | #[repr(C)] 116 | pub struct xmpp_mem_t { 117 | alloc: Option *mut u8>, 118 | free: Option, 119 | realloc: Option *mut u8>, 120 | userdata: *mut c_void, 121 | } 122 | 123 | #[repr(C)] 124 | pub struct xmpp_log_t { 125 | handler: xmpp_log_handler, 126 | userdata: *mut c_void, 127 | } 128 | 129 | #[repr(C)] 130 | pub struct xmpp_stream_error_t { 131 | _type: c_int, // xmpp_error_type_t 132 | text: *mut c_char, 133 | stanza: *mut xmpp_stanza_t, 134 | } 135 | 136 | #[repr(C)] 137 | pub struct hashentry_t { 138 | next: *mut hashentry_t, 139 | key: *mut c_char, 140 | values: *mut c_void, 141 | } 142 | 143 | #[repr(C)] 144 | pub struct hash_t { 145 | _ref: c_uint, 146 | ctx: *mut xmpp_ctx_t, 147 | free: hash_free_func, 148 | length: c_int, 149 | num_keys: c_int, 150 | entries: *mut *mut hashentry_t, 151 | } 152 | 153 | #[repr(C)] 154 | #[derive(PartialEq, Eq)] 155 | pub enum xmpp_log_level_t { 156 | XMPP_LEVEL_DEBUG, 157 | XMPP_LEVEL_INFO, 158 | XMPP_LEVEL_WARN, 159 | XMPP_LEVEL_ERROR, 160 | } 161 | 162 | #[repr(C)] 163 | #[derive(PartialEq, Eq)] 164 | pub enum xmpp_conn_type_t { 165 | XMPP_UNKNOWN, 166 | XMPP_CLIENT, 167 | XMPP_COMPONENT, 168 | } 169 | 170 | #[repr(C)] 171 | #[derive(PartialEq, Eq)] 172 | pub enum xmpp_conn_event_t { 173 | XMPP_CONN_CONNECT, 174 | XMPP_CONN_DISCONNECT, 175 | XMPP_CONN_FAIL, 176 | } 177 | 178 | #[repr(C)] 179 | #[derive(PartialEq, Eq)] 180 | pub enum xmpp_error_type_t { 181 | XMPP_SE_BAD_FORMAT, 182 | XMPP_SE_BAD_NS_PREFIX, 183 | XMPP_SE_CONFLICT, 184 | XMPP_SE_CONN_TIMEOUT, 185 | XMPP_SE_HOST_GONE, 186 | XMPP_SE_HOST_UNKNOWN, 187 | XMPP_SE_IMPROPER_ADDR, 188 | XMPP_SE_INTERNAL_SERVER_ERROR, 189 | XMPP_SE_INVALID_FROM, 190 | XMPP_SE_INVALID_ID, 191 | XMPP_SE_INVALID_NS, 192 | XMPP_SE_INVALID_XML, 193 | XMPP_SE_NOT_AUTHORIZED, 194 | XMPP_SE_POLICY_VIOLATION, 195 | XMPP_SE_REMOTE_CONN_FAILED, 196 | XMPP_SE_RESOURCE_CONSTRAINT, 197 | XMPP_SE_RESTRICTED_XML, 198 | XMPP_SE_SEE_OTHER_HOST, 199 | XMPP_SE_SYSTEM_SHUTDOWN, 200 | XMPP_SE_UNDEFINED_CONDITION, 201 | XMPP_SE_UNSUPPORTED_ENCODING, 202 | XMPP_SE_UNSUPPORTED_STANZA_TYPE, 203 | XMPP_SE_UNSUPPORTED_VERSION, 204 | XMPP_SE_XML_NOT_WELL_FORMED, 205 | } 206 | 207 | pub const XMPP_NS_CLIENT: &'static str = "jabber:client"; 208 | pub const XMPP_NS_COMPONENT: &'static str = "jabber:component:accept"; 209 | pub const XMPP_NS_STREAMS: &'static str = "http://etherx.jabber.org/streams"; 210 | pub const XMPP_NS_STREAMS_IETF: &'static str = "urn:ietf:params:xml:ns:xmpp-streams"; 211 | pub const XMPP_NS_TLS: &'static str = "urn:ietf:params:xml:ns:xmpp-tls"; 212 | pub const XMPP_NS_SASL: &'static str = "urn:ietf:params:xml:ns:xmpp-sasl"; 213 | pub const XMPP_NS_BIND: &'static str = "urn:ietf:params:xml:ns:xmpp-bind"; 214 | pub const XMPP_NS_SESSION: &'static str = "urn:ietf:params:xml:ns:xmpp-session"; 215 | pub const XMPP_NS_AUTH: &'static str = "jabber:iq:auth"; 216 | pub const XMPP_NS_DISCO_INFO: &'static str = "http://jabber.org/protocol/disco#info"; 217 | pub const XMPP_NS_DISCO_ITEMS: &'static str = "http://jabber.org/protocol/disco#items"; 218 | pub const XMPP_NS_ROSTER: &'static str = "jabber:iq:roster"; 219 | 220 | pub const XMPP_EOK: c_int = 0; 221 | pub const XMPP_EMEM: c_int = -1; 222 | pub const XMPP_EINVOP: c_int = -2; 223 | pub const XMPP_EINT: c_int = -3; 224 | 225 | pub const XMPP_CONN_FLAG_DISABLE_TLS: c_long = 1; 226 | pub const XMPP_CONN_FLAG_MANDATORY_TLS: c_long = 2; 227 | pub const XMPP_CONN_FLAG_LEGACY_SSL: c_long = 4; 228 | 229 | #[link(name="strophe")] 230 | extern "C" { 231 | // connection management 232 | pub fn xmpp_conn_new(ctx: *const xmpp_ctx_t) -> *mut xmpp_conn_t; 233 | pub fn xmpp_conn_close(ctx: *const xmpp_ctx_t) -> *mut xmpp_conn_t; 234 | pub fn xmpp_conn_release(conn: *const xmpp_conn_t) -> c_int; 235 | pub fn xmpp_conn_get_jid(conn: *const xmpp_conn_t) -> *const c_char; 236 | pub fn xmpp_conn_get_bound_jid(conn: *const xmpp_conn_t) -> *const c_char; 237 | pub fn xmpp_conn_set_jid(conn: *const xmpp_conn_t, jid: *const c_char); 238 | pub fn xmpp_conn_get_pass(conn: *const xmpp_conn_t) -> *const c_char; 239 | pub fn xmpp_conn_set_pass(conn: *const xmpp_conn_t, pass: *const c_char); 240 | pub fn xmpp_conn_get_context(conn: *const xmpp_conn_t) -> *mut xmpp_ctx_t; 241 | pub fn xmpp_conn_disable_tls(conn: *const xmpp_conn_t); 242 | pub fn xmpp_conn_is_secured(conn: *const xmpp_conn_t) -> c_int; 243 | pub fn xmpp_connect_client(conn: *const xmpp_conn_t, 244 | altdomain: *const c_char, 245 | altport: c_ushort, 246 | callback: xmpp_conn_handler, 247 | userdata: *const c_void) -> c_int; 248 | pub fn xmpp_connect_component(conn: *const xmpp_conn_t, 249 | server: *const c_char, 250 | port: c_ushort, 251 | callback: xmpp_conn_handler, 252 | userdata: *const c_void) -> c_int; 253 | pub fn xmpp_disconnect(conn: *const xmpp_conn_t); 254 | pub fn xmpp_send(conn: *const xmpp_conn_t, stanza: *const xmpp_stanza_t); 255 | pub fn xmpp_send_raw(conn: *const xmpp_conn_t, 256 | data: *const c_char, 257 | len: size_t); 258 | 259 | // context objects 260 | pub fn xmpp_get_default_logger(level: xmpp_log_level_t) -> *mut xmpp_log_t; 261 | pub fn xmpp_ctx_new(mem: *const xmpp_mem_t, log: *const xmpp_log_t) -> *mut xmpp_ctx_t; 262 | pub fn xmpp_ctx_free(ctx: *const xmpp_ctx_t); 263 | 264 | // initialization, shutdown and versioning 265 | pub fn xmpp_initialize(); 266 | pub fn xmpp_shutdown(); 267 | pub fn xmpp_version_check(major: c_int, minor: c_int) -> c_int; 268 | 269 | // event loop 270 | pub fn xmpp_run_once(ctx: *mut xmpp_ctx_t, timeout: c_ulong); 271 | pub fn xmpp_run(ctx: *mut xmpp_ctx_t); 272 | pub fn xmpp_stop(ctx: *mut xmpp_ctx_t); 273 | 274 | // stanza and timed event handlers 275 | pub fn xmpp_timed_handler_delete(conn: *const xmpp_conn_t, handler: xmpp_timed_handler); 276 | pub fn xmpp_id_handler_delete(conn: *const xmpp_conn_t, 277 | handler: xmpp_handler, 278 | id: *const c_char); 279 | pub fn xmpp_handler_delete(conn: *const xmpp_conn_t, handler: xmpp_handler); 280 | pub fn xmpp_timed_handler_add(conn: *const xmpp_conn_t, 281 | handler: xmpp_timed_handler, 282 | period: c_ulong, 283 | userdata: *const c_void); 284 | pub fn xmpp_id_handler_add(conn: *const xmpp_conn_t, 285 | handler: xmpp_handler, 286 | id: *const c_char, 287 | userdata: *const c_void); 288 | pub fn xmpp_handler_add(conn: *const xmpp_conn_t, 289 | handler: xmpp_handler, 290 | ns: *const c_char, 291 | name: *const c_char, 292 | _type: *const c_char, 293 | userdata: *const c_void); 294 | 295 | // stanza creation and manipulation 296 | pub fn xmpp_stanza_new(ctx: *mut xmpp_ctx_t) -> *mut xmpp_stanza_t; 297 | pub fn xmpp_stanza_clone(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t; 298 | pub fn xmpp_stanza_copy(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t; 299 | pub fn xmpp_stanza_release(stanza: *const xmpp_stanza_t) -> c_int; 300 | pub fn xmpp_stanza_is_text(stanza: *const xmpp_stanza_t) -> c_int; 301 | pub fn xmpp_stanza_is_tag(stanza: *const xmpp_stanza_t) -> c_int; 302 | pub fn xmpp_stanza_to_text(stanza: *mut xmpp_stanza_t, 303 | buf: *const *const c_char, 304 | sizelen: *const size_t) -> c_int; 305 | pub fn xmpp_stanza_set_name(stanza: *mut xmpp_stanza_t, name: *const c_char) -> c_int; 306 | pub fn xmpp_stanza_get_name(stanza: *const xmpp_stanza_t) -> *mut c_char; 307 | pub fn xmpp_stanza_get_attribute_count(stanza: *const xmpp_stanza_t) -> c_int; 308 | pub fn xmpp_stanza_get_attributes(stanza: *const xmpp_stanza_t, 309 | attr: *const *const c_char, 310 | attrlen: c_int) -> c_int; 311 | pub fn xmpp_stanza_set_attribute(stanza: *const xmpp_stanza_t, 312 | key: *const c_char, 313 | value: *const c_char) -> c_int; 314 | pub fn xmpp_stanza_set_ns(stanza: *const xmpp_stanza_t, ns: *const c_char) -> c_int; 315 | pub fn xmpp_stanza_add_child(stanza: *mut xmpp_stanza_t, child: *mut xmpp_stanza_t) -> c_int; 316 | pub fn xmpp_stanza_set_text(stanza: *mut xmpp_stanza_t, text: *const c_char) -> c_int; 317 | pub fn xmpp_stanza_set_text_with_size(stanza: *mut xmpp_stanza_t, 318 | text: *const c_char, 319 | size: size_t) -> c_int; 320 | pub fn xmpp_stanza_get_id(stanza: *const xmpp_stanza_t) -> *mut c_char; 321 | pub fn xmpp_stanza_get_to(stanza: *const xmpp_stanza_t) -> *mut c_char; 322 | pub fn xmpp_stanza_get_from(stanza: *const xmpp_stanza_t) -> *mut c_char; 323 | pub fn xmpp_stanza_get_ns(stanza: *const xmpp_stanza_t) -> *mut c_char; 324 | pub fn xmpp_stanza_get_type(stanza: *const xmpp_stanza_t) -> *mut c_char; 325 | pub fn xmpp_stanza_get_child_by_name(stanza: *const xmpp_stanza_t, 326 | name: *const c_char) -> *mut xmpp_stanza_t; 327 | pub fn xmpp_stanza_get_child_by_ns(stanza: *const xmpp_stanza_t, 328 | ns: *const c_char) -> *mut xmpp_stanza_t; 329 | pub fn xmpp_stanza_get_children(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t; 330 | pub fn xmpp_stanza_get_next(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t; 331 | pub fn xmpp_stanza_get_text(stanza: *const xmpp_stanza_t) -> *mut c_char; 332 | pub fn xmpp_stanza_get_text_ptr(stanza: *const xmpp_stanza_t) -> *mut c_char; 333 | pub fn xmpp_stanza_set_id(stanza: *const xmpp_stanza_t, id: *const c_char) -> c_int; 334 | pub fn xmpp_stanza_set_type(stanza: *const xmpp_stanza_t, _type: *const c_char) -> c_int; 335 | pub fn xmpp_stanza_get_attribute(stanza: *const xmpp_stanza_t, 336 | name: *const c_char) -> *mut c_char; 337 | pub fn xmpp_stanza_set_to(stanza: *const xmpp_stanza_t, 338 | to: *const c_char) -> c_int; 339 | pub fn xmpp_stanza_set_from(stanza: *const xmpp_stanza_t, 340 | from: *const c_char) -> c_int; 341 | pub fn xmpp_conn_set_flags(conn: *const xmpp_conn_t, flags: c_long) -> c_int; 342 | pub fn xmpp_conn_get_flags(conn: *const xmpp_conn_t) -> c_long; 343 | 344 | // jid functions 345 | pub fn xmpp_jid_new(ctx: *const xmpp_ctx_t, 346 | node: *const c_char, 347 | domain: *const c_char, 348 | resource: *const c_char) -> *const c_char; 349 | pub fn xmpp_jid_bare(ctx: *const xmpp_ctx_t, 350 | jid: *const c_char) -> *const c_char; 351 | pub fn xmpp_jid_node(ctx: *const xmpp_ctx_t, 352 | jid: *const c_char) -> *const c_char; 353 | pub fn xmpp_jid_domain(ctx: *const xmpp_ctx_t, 354 | jid: *const c_char) -> *const c_char; 355 | pub fn xmpp_jid_resource(ctx: *const xmpp_ctx_t, 356 | jid: *const c_char) -> *const c_char; 357 | 358 | pub fn xmpp_uuid_gen(ctx: *const xmpp_ctx_t) -> *mut c_char; 359 | pub fn xmpp_alloc(ctx: *const xmpp_ctx_t, size: size_t); 360 | pub fn xmpp_free(ctx: *const xmpp_ctx_t, ptr: *const c_void); 361 | pub fn xmpp_realloc(ctx: *const xmpp_ctx_t, ptr: c_void, size: size_t); 362 | pub fn xmpp_stanza_reply(ctx: *const xmpp_stanza_t) -> *mut xmpp_stanza_t; 363 | } 364 | --------------------------------------------------------------------------------