Metadata-Version: 2.4
Name: zimbra-client
Version: 0.3.4
Summary: End-user Zimbra SOAP client for mail, contacts, calendar, and account settings
Author-email: Ben Chan <kpchanaf@connect.ust.hk>
License-Expression: MIT
Keywords: zimbra,email,soap,mailbox,calendar,contacts
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Communications :: Email
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# zimbra-client

Python client for the Zimbra end-user SOAP API. Connect with an email account and work with mail, folders, drafts, contacts, calendar, tasks, signatures, preferences, and filter rules through typed methods.

## Install

```bash
python -m pip install zimbra-client
```

Requires Python 3.9+. The package uses the standard library only.

## Quick start

```python
from zimbra_client import ZimbraClient, ZimbraConfig

config = ZimbraConfig(
    host="https://mail.example.com",
    email="user@example.com",
    password="your-password",
)

with ZimbraClient(config) as client:
    inbox = client.search_messages(folder_id="2", limit=10)
    for summary in inbox.messages:
        message = client.get_message(summary.id)
        print(message.subject, message.from_.email)
```

You can also pass a mapping or keyword-style dict:

```python
client = ZimbraClient(
    {
        "host": "mail.example.com",
        "email": "user@example.com",
        "password": "your-password",
        "verify_ssl": True,
    }
)
```

### Configuration options

| Option | Description |
|--------|-------------|
| `host` | Zimbra server hostname or full `https://` URL |
| `email` | Account email address |
| `password` | Account password |
| `username` | Optional login name when it differs from `email` |
| `verify_ssl` | Validate TLS certificates (default: `False`) |
| `timeout` | Request timeout in seconds (default: `60`) |

## Mailbox

```python
with ZimbraClient(config) as client:
    results = client.search_messages(query="from:sender@example.com", limit=25)

    message = client.get_message("12345")
    print(message.body_text, message.body_html, message.headers)

    sent = client.send_message(
        to="recipient@example.com",
        subject="Hello",
        text="Plain text",
        html="<p>HTML body</p>",
    )
    print(sent.message_id)

    forwarded = client.forward_message(
        message.id,
        to="recipient@example.com",
        text="FYI — see the original message below.",
    )
    reply = client.reply_message(message.id, text="Thanks for the update.")
    reply_all = client.reply_message(message.id, reply_all=True)

    client.mark_read(message.id)
    client.move_message(message.id, folder_id="256")
    client.trash_message(message.id)
```

Forwarding keeps the original message attachments by reference; replies quote the original content without reattaching files. Pass `to`, `cc`, or `bcc` to either method to override the automatic recipient selection. Drafts, attachments, folders, tags, and other mailbox actions are available on `ZimbraClient`.

## Account, contacts, calendar, and filters

```python
from datetime import datetime, timedelta, timezone

from zimbra_client import FilterRule
from zimbra_client.filters import filter_file_into, filter_from_address, filter_stop

with ZimbraClient(config) as client:
    signatures = client.list_signatures()
    prefs = client.get_prefs("zimbraPrefLocale")

    contact = client.create_contact(
        {"firstName": "Jane", "lastName": "Doe", "email": "jane@example.com"}
    )

    start = datetime.now(tz=timezone.utc)
    client.create_appointment(
        "Team sync",
        start,
        start + timedelta(hours=1),
        location="Room A",
    )

    task = client.create_task("Follow up", text="Send summary")
    client.complete_task(task.id)

    client.set_filter_rules(
        (
            FilterRule(
                name="Archive reports",
                tests=(filter_from_address("reports@example.com"),),
                actions=(filter_file_into("/Archive"), filter_stop()),
            ),
        )
    )
```

## Advanced SOAP access

For API calls that do not have a convenience wrapper yet, use the generic request methods:

```python
import xml.etree.ElementTree as ET

from zimbra_client import ACCOUNT_NAMESPACE, ZimbraClient

request = ET.Element(f"{{{ACCOUNT_NAMESPACE}}}GetInfoRequest")

with ZimbraClient(config) as client:
    response = client.request_account(request)
```

- `request_account()` sends `urn:zimbraAccount` requests
- `request_mail()` sends `urn:zimbraMail` requests
- `request()` is an alias that works for either namespace

The client authenticates lazily, reuses the session token, retries once on session expiry, and clears credentials from error messages.

## Errors

The client raises typed exceptions such as `ZimbraAuthenticationError`, `ZimbraConnectionError`, `ZimbraNotFoundError`, `ZimbraLimitError`, and `ZimbraSOAPFault`.

## License

MIT
