Monal XMPP Client

repository·develop·Indexed 20 days ago

https://github.com/monal-im/monal

A modern, cross-platform XMPP client for iOS and macOS supporting various XMPP Extension Protocols (XEPs). The project includes a suite of Rust-based utilities such as monal-html-parser for CSS-based HTML extraction, monal-xml-parser for incremental XML streaming, and sdp-to-jingle for converting between Session Description Protocol (SDP) and Jingle XML strings. It also provides a Python-based UDP log server for decrypting and decompressing AES-GCM encrypted JSON log packets.

Tokens
4.8K
Snippets
22
Records
24
Agent score
71%

What's inside Monal

  1. Install Monal on iOS

    develop

    Monal is available for iOS through the following channels:

    • Stable: Search for 'Monal' in the Apple App Store.
    • Beta: Access via Apple Testflight.
    • Alpha: Available upon request to info@monal-im.org. Once granted, download from the Monal alpha download site.
  2. Install Monal on macOS via Homebrew

    develop

    You can install Monal on macOS using Homebrew. There are three available tracks: Stable, Beta, and Alpha.

    • Stable: Use the standard cask installation.
    • Beta: Use the @beta cask.
    • Alpha: Requires tapping the specific alpha repository before installation.
    # Install Stable version
    brew install --cask monal
    
    # Install Beta version
    brew install --cask monal@beta
    
    # Install Alpha version
    brew tap monal-im/homebrew-monal-alpha
    brew install --cask monal-alpha
  3. Access the global XML context with `global_context()`

    develop

    The global_context() function returns an Arc<Context> that is shared globally across different parsers and threads. This context is used for namespace string sharing to optimize parsing performance. It is initialized lazily on the first call.

    use monal_xml_parser::global_context;
    
    let context = global_context();
  4. Use `MonalXmlStreamParser` for incremental XML parsing

    develop

    The MonalXmlStreamParser is designed for streaming XML data. You can feed it chunks of bytes and then poll it to receive parsed events. This is useful when XML data arrives in fragments (e.g., over a network socket).

    Workflow

    1. Initialize: Create a new parser using MonalXmlStreamParser::new(capacity, max_token_length).
    2. Feed: Provide new data chunks using the .feed(chunk) method.
    3. Poll: Call .poll() to retrieve the next parsed event. If the parser needs more data to complete an event, it returns MonalXmlStreamParserResult::NeedMoreData.
    use monal_xml_parser::{MonalXmlStreamParser, MonalXmlStreamParserResult};
    
    let mut parser = MonalXmlStreamParser::new(1024, 4096);
    
    // Feed a chunk of data
    parser.feed(b"<message xmlns='http://xmpp.org'>Hello</message>");
    
    // Poll for results
    match parser.poll() {
        Ok(MonalXmlStreamParserResult::Start(local, ns, attrs)) => {
            println!("Start element: {} in namespace {}", local, ns);
        }
        Ok(MonalXmlStreamParserResult::Text(text)) => {
            println!("Text content: {}", text);
        }
        Ok(MonalXmlStreamParserResult::End) => {
            println!("End element");
        }
        Ok(MonalXmlStreamParserResult::NeedMoreData) => {
            // Wait for more data via .feed()
        }
        Err(e) => println!("Parsing error: {}", e),
        _ => {}
    }
  5. Format log entries using formatLogline()

    develop

    The formatLogline(entry) function converts a decoded JSON log entry into a human-readable string.

    Expected JSON structure for entry:

    • timestamp: String timestamp.
    • flag: Integer representing the log level (e.g., 1 for ERROR, 4 for INFO).
    • file: Path to the source file.
    • line: Line number.
    • message: The log message.
    • function: The function name.
    • threadID: The thread identifier.
    • tag: A dictionary containing:
      • processName: Name of the process.
      • queueThreadLabel: Label for the queue thread.
      • qosName: Quality of Service name.

    Output Format: [timestamp] [LEVEL] [processName] [threadID:queueThreadLabel (QOS:qosName)] [parentDir/filename] [line]: [message]

    def formatLogline(entry):
        # ... implementation ...
        return formatted_string
  6. Use MonalHtmlParser for HTML selection

    develop

    The MonalHtmlParser allows you to parse HTML strings and select elements using CSS-like selectors.

    1. Initialize the parser with an HTML string using new(html: String).
    2. Use select(selector: String, atrribute: Option<String>) to retrieve a list of strings representing the selected elements or their attributes.
    // Example usage in Swift
    let parser = MonalHtmlParser(html: "<div><p class='text'>Hello</p></div>")
    let classes = parser.select("p", atrribute: Some("class"))
    // classes would contain ["text"]
    
    let tags = parser.select("p", atrribute: nil)
    // tags would contain ["<p class='text'>Hello</p>"]
  7. Use MonalHtmlParser to parse and query HTML

    develop

    The MonalHtmlParser struct provides a wrapper around an HTML document, allowing you to extract text content or specific attribute values using CSS selectors.

    To use it, initialize a new parser with a String containing the HTML content. You can then use the select method to find elements matching a CSS selector.

    • If you provide None as the atrribute argument, select returns a vector of the text content found within the matched elements.
    • If you provide Some(attribute_name) as the atrribute argument, select returns a vector of the values of that specific attribute for each matched element.

    Note: If the provided CSS selector is invalid, the error is printed to stderr and an empty vector is returned.

    use monal_html_parser::MonalHtmlParser;
    
    let html = r#"<div class="item" id="test">Hello <span>World</span></div>"#.to_string();
    let parser = MonalHtmlParser::new(html);
    
    // Extract text content from elements matching the selector
    let text_results = parser.select(".item".to_string(), None);
    // Result: ["Hello World"]
    
    // Extract a specific attribute value
    let id_results = parser.select(".item".to_string(), Some("id".to_string()));
    // Result: ["test"]
  8. Decrypt AES-GCM ciphertext

    develop

    The decrypt(ciphertext, key) function performs AES-GCM decryption.

    Expected ciphertext format:

    1. IV (12 bytes): The initialization vector.
    2. Tag (16 bytes): The authentication tag.
    3. Encrypted Data: The actual ciphertext.

    The key provided to this function should be the 32-byte (256-bit) derived key.

    Raises:

    • Exception: If the IV length is not 12 bytes, the tag length is not 16 bytes, or if decryption/verification fails.
    def decrypt(ciphertext, key):
        # ... implementation ...
        return plaintext
  9. Convert SDP string to Jingle XML string

    develop

    Use sdp_str_to_jingle_str to transform a Session Description Protocol (SDP) string into a Jingle-formatted XML string. This is useful for bridging WebRTC-style SDP descriptions with XMPP Jingle signaling.

    Arguments:

    • sdp_str: The input SDP string to be parsed.
    • initiator: A boolean indicating whether the caller is the session initiator. This affects how the Jingle session is constructed.

    Returns Some(String) containing the XML representation of the Jingle session if successful, or None if parsing or conversion fails.

    let sdp = "v=0\r\no=- 4728394728394728394728394728394728394728\r\ns=-\r\n...";
    if let Some(jingle_xml) = sdp_str_to_jingle_str(sdp, true) {
        println!("Converted Jingle XML: {}", jingle_xml);
    } else {
        eprintln!("Failed to convert SDP to Jingle");
    }