summaryrefslogtreecommitdiff
path: root/src/rexmpp_xml_parser.rs
blob: 037c2f215afb594e5a28dcfe134cc8d2f70b1eac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
extern crate libc;
extern crate rxml;
use libc::{free, strndup};
use std::ptr;
use std::os::raw::{c_char, c_void};
use std::ffi::{CStr, CString};
use std::slice;
use rxml::{FeedParser, Error, ResolvedEvent, XmlVersion, EventRead, CData};
use std::io;
use super::{rexmpp_xml};

type RexmppXMLParserElementStart = unsafe extern "C"
fn (data: *mut c_void,
    name: *const c_char,
    namespace: *const c_char,
    attributes: *mut rexmpp_xml::RexmppXMLAttribute) -> ();

type RexmppXMLParserElementEnd = unsafe extern "C"
fn (data: *mut c_void) -> ();

type RexmppXMLParserCharacters = unsafe extern "C"
fn (data: *mut c_void,
    ch: *const c_char,
    len: usize) -> ();

#[repr(C)]
struct RexmppXMLParserHandlers {
    elem_start: RexmppXMLParserElementStart,
    elem_end: RexmppXMLParserElementEnd,
    text: RexmppXMLParserCharacters
}

#[repr(C)]
struct RexmppXMLParserCtx {
    xml_parser: *mut FeedParser,
    handlers: *mut RexmppXMLParserHandlers,
    user_data: *mut c_void
}

#[no_mangle]
extern "C"
fn rexmpp_xml_parser_new (handlers: *mut RexmppXMLParserHandlers,
                          data: *mut c_void)
                          -> *mut RexmppXMLParserCtx
{
    let mut fp = FeedParser::default();
    let ctx = RexmppXMLParserCtx {
        xml_parser: Box::into_raw(Box::new(fp)),
        handlers: handlers,
        user_data: data
    };
    Box::into_raw(Box::new(ctx))
}

#[no_mangle]
extern "C"
fn rexmpp_xml_parser_free (ctx: *mut RexmppXMLParserCtx) {
    unsafe { free(ctx as *mut c_void) };
}

#[no_mangle]
extern "C"
fn rexmpp_xml_parser_feed (ctx: *mut RexmppXMLParserCtx,
                           chunk: *const c_char,
                           len: usize,
                           is_final: bool)
{
    unsafe {
        // todo: maybe duplicate the string, since apparently a
        // mutable one is expected by the parser.
        let mut buf : &[u8] = slice::from_raw_parts(chunk as *mut u8, len);
        let user_data_ptr = (*ctx).user_data;
        let handlers = (*ctx).handlers;
        (*((*ctx).xml_parser)).parse_all(&mut buf, is_final, |ev| {
            match ev {
                ResolvedEvent::StartElement(_, (namespace, name), attrs) =>
                {
                    let name_str = name.to_string();
                    let ns_opt_cstr : Option<CString> = match namespace {
                        None => None,
                        Some(ns_arc_name) => {
                            match CString::new(ns_arc_name.to_string()) {
                                Ok(cstr) => Some(cstr),
                                Err(_) => None
                            }
                        }
                    };
                    match CString::new(name_str) {
                        Ok(name_cstr) => {
                            let name_cstr_ptr = name_cstr.as_ptr();
                            let namespace_cstr_ptr =
                                match ns_opt_cstr {
                                    None => ptr::null_mut(),
                                    // "ref" is important to use here,
                                    // otherwise the pointer will be
                                    // wrong.
                                    Some(ref ns_cstr) => ns_cstr.as_ptr()
                                };
                            let mut attributes = ptr::null_mut();
                            for ((_, attr_name), attr_val) in attrs.iter() {
                                match (CString::new(attr_name.to_string()),
                                       CString::new(attr_val.to_string())) {
                                    (Ok(attr_name_cstr), Ok(attr_val_cstr)) => {
                                        let attr =
                                            rexmpp_xml::rexmpp_xml_attr_new
                                            (attr_name_cstr.as_ptr(),
                                             ptr::null_mut(),
                                             attr_val_cstr.as_ptr());
                                        (*attr).next = attributes;
                                        attributes = attr;
                                    },
                                    _ => ()
                                }
                            }
                            ((*handlers).elem_start)
                                (user_data_ptr,
                                 name_cstr_ptr,
                                 namespace_cstr_ptr,
                                 attributes);
                        },
                        Err(_) => ()
                    }
                },
                ResolvedEvent::EndElement(_) =>
                    ((*handlers).elem_end)(user_data_ptr),
                ResolvedEvent::Text(_, cd) =>
                    ((*handlers).text)(
                        user_data_ptr,
                        cd.as_ptr() as *const i8,
                        cd.len()
                    ),
                _ => ()
            }
        });
    }
}

#[no_mangle]
extern "C"
fn rexmpp_xml_parser_reset (ctx_raw: *mut RexmppXMLParserCtx)
                            -> *mut RexmppXMLParserCtx
{
    let ctx = unsafe { Box::from_raw(ctx_raw) };
    rexmpp_xml_parser_new((*ctx).handlers, (*ctx).user_data)
}