# HG changeset patch # User Olaf Wintermann # Date 1783698981 -7200 # Node ID 0bcf3d69b37af738df9e55483cef1ad27d1746d4 # Parent 8470d5aac30e70eef7e0e8f903808b66e32c6569 add jmap_mailbox_get_list diff -r 8470d5aac30e -r 0bcf3d69b37a jmap/jmmail.c --- a/jmap/jmmail.c Thu Jul 09 22:07:33 2026 +0200 +++ b/jmap/jmmail.c Fri Jul 10 17:56:21 2026 +0200 @@ -105,7 +105,14 @@ const char *password = argv[3]; JMAPSession *sn = jmap_session_new_with_user(session_url, user, password); + curl_easy_setopt(sn->handle, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(sn->handle, CURLOPT_STDERR, stderr); + CxList *mailboxes = jmap_mailbox_get_list(sn); + CxIterator i = cxListIterator(mailboxes); + cx_foreach(JMAPMailBox *, mbox, i) { + printf("{%s}: %s (%s)\n", mbox->id, mbox->name, mbox->role); + } return 0; } diff -r 8470d5aac30e -r 0bcf3d69b37a jmap/jmmail.h --- a/jmap/jmmail.h Thu Jul 09 22:07:33 2026 +0200 +++ b/jmap/jmmail.h Fri Jul 10 17:56:21 2026 +0200 @@ -30,6 +30,7 @@ #define JMMAIL_H #include +#include #ifdef __cplusplus extern "C" { diff -r 8470d5aac30e -r 0bcf3d69b37a libidav/Makefile --- a/libidav/Makefile Thu Jul 09 22:07:33 2026 +0200 +++ b/libidav/Makefile Fri Jul 10 17:56:21 2026 +0200 @@ -44,6 +44,7 @@ SRC += jmap.c SRC += jmapsession.c SRC += jmap_methods.c +SRC += jmap_mail.c OBJ = $(SRC:%.c=../build/libidav/%$(OBJ_EXT)) diff -r 8470d5aac30e -r 0bcf3d69b37a libidav/jmap.h --- a/libidav/jmap.h Thu Jul 09 22:07:33 2026 +0200 +++ b/libidav/jmap.h Fri Jul 10 17:56:21 2026 +0200 @@ -34,11 +34,22 @@ #endif #include +#include #include #include #include +#include + typedef struct JMAPSession JMAPSession; +typedef struct JMAPPrimaryAccounts JMAPPrimaryAccounts; + +struct JMAPPrimaryAccounts { + char *mail; + char *vacationresponse; + char *contacts; + char *calendars; +}; struct JMAPSession { CURL *handle; @@ -51,8 +62,12 @@ char *downloadUrl; char *uploadUrl; char *eventSourceUrl; + + JMAPPrimaryAccounts primary_accounts; }; + + JMAPSession* jmap_session_new_with_user(const char *session_url, const char *user, const char *password); #ifdef __cplusplus diff -r 8470d5aac30e -r 0bcf3d69b37a libidav/jmap_mail.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libidav/jmap_mail.c Fri Jul 10 17:56:21 2026 +0200 @@ -0,0 +1,121 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2026 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "jmap_mail.h" +#include "jmap_methods.h" + +void jmap_mailbox_free(JMAPMailBox *mailbox) { + const CxAllocator *a = mailbox->session->mp->allocator; + cxFree(a, mailbox->id); + cxFree(a, mailbox->parent_id); + cxFree(a, mailbox->name); + cxFree(a, mailbox->role); + cxFree(a, mailbox); +} + +CxList* jmap_mailbox_get_list(JMAPSession *sn) { + if(!sn->primary_accounts.mail) { + return NULL; + } + const CxAllocator *a = sn->mp->allocator; + + CxBuffer *request = jm_mailbox_get_request(cx_str(sn->primary_accounts.mail)); + //printf("%.*s\n", (int)request->size, request->space); + + CxJsonValue *response = jm_do_api_request(sn, request); + cxBufferFree(request); + if(!response) { + return NULL; + } + + CxJsonValue *mailbox_list = NULL; + // check response and get mailbox response value + if(cxJsonIsObject(response)) { + CxJsonValue *method_responses = cxJsonObjGet(response, "methodResponses"); + if(cxJsonIsArray(method_responses) && method_responses->array.size > 0) { + CxJsonValue *method_response0 = method_responses->array.data[0]; + if(cxJsonIsArray(method_response0) && method_response0->array.size > 1) { + CxJsonValue *method = method_response0->array.data[0]; + if(cxJsonIsString(method) && !cx_strcmp(method->string, "Mailbox/get")) { + CxJsonValue *mailbox_response_obj = method_response0->array.data[1]; + if(cxJsonIsObject(mailbox_response_obj)) { + mailbox_list = cxJsonObjGet(mailbox_response_obj, "list"); + } + } + } + } + } + + if(!mailbox_list) { + cxJsonValueFree(response); + return NULL; + } + + CxList *list = cxArrayListCreate(a, CX_STORE_POINTERS, 16); + list->collection.simple_destructor = (cx_destructor_func)jmap_mailbox_free; + int error = 0; + for(size_t i=0;iarray.size;i++) { + CxJsonValue *mailbox = mailbox_list->array.data[i]; + if(!cxJsonIsObject(mailbox)) { + error = 1; + break; + } + + CxJsonValue *id = cxJsonObjGet(mailbox, "id"); + CxJsonValue *name = cxJsonObjGet(mailbox, "name"); + CxJsonValue *parent_id = cxJsonObjGet(mailbox, "parentId"); + CxJsonValue *role = cxJsonObjGet(mailbox, "role"); + + if(!cxJsonIsString(id)) { + error = 1; + break; + } + + JMAPMailBox *mbox = cxZalloc(a, sizeof(JMAPMailBox)); + mbox->id = cx_strdup_a(a, id->string).ptr; + if(cxJsonIsString(name)) { + mbox->name = cx_strdup_a(a, name->string).ptr; + } + if(cxJsonIsString(parent_id)) { + mbox->parent_id = cx_strdup_a(a, parent_id->string).ptr; + } + if(cxJsonIsString(role)) { + mbox->role = cx_strdup_a(a, role->string).ptr; + } + + cxListAdd(list, mbox); + } + + cxJsonValueFree(response); + if(error) { + cxListFree(list); + return NULL; + } else { + return list; + } +} diff -r 8470d5aac30e -r 0bcf3d69b37a libidav/jmap_mail.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libidav/jmap_mail.h Fri Jul 10 17:56:21 2026 +0200 @@ -0,0 +1,63 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2026 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JMAP_MAIL_H +#define JMAP_MAIL_H + +#include "jmap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct JMAPMailBox JMAPMailBox; + +struct JMAPMailBox { + JMAPSession *session; + char *id; + char *parent_id; + char *name; + char *role; + + uint32_t total_emails; + uint32_t unread_emails; + uint32_t total_threads; + uint32_t unread_threads; +}; + +void jmap_mailbox_free(JMAPMailBox *mailbox); + +CxList* jmap_mailbox_get_list(JMAPSession *sn); + + +#ifdef __cplusplus +} +#endif + +#endif /* JMAP_MAIL_H */ + diff -r 8470d5aac30e -r 0bcf3d69b37a libidav/jmap_methods.c --- a/libidav/jmap_methods.c Thu Jul 09 22:07:33 2026 +0200 +++ b/libidav/jmap_methods.c Fri Jul 10 17:56:21 2026 +0200 @@ -52,17 +52,25 @@ curl_easy_setopt(sn->handle, CURLOPT_URL, sn->apiUrl); curl_easy_setopt(sn->handle, CURLOPT_POST, 1L); - curl_easy_setopt(sn->handle, CURLOPT_READFUNCTION, cxBufferRead); - curl_easy_setopt(sn->handle, CURLOPT_SEEKFUNCTION, cxBufferSeek); - curl_easy_setopt(sn->handle, CURLOPT_READDATA, request); - curl_easy_setopt(sn->handle, CURLOPT_SEEKDATA, request); - curl_easy_setopt(sn->handle, CURLOPT_INFILESIZE, request->size); + //curl_easy_setopt(sn->handle, CURLOPT_READFUNCTION, cxBufferRead); + //curl_easy_setopt(sn->handle, CURLOPT_SEEKFUNCTION, cxBufferSeek); + //curl_easy_setopt(sn->handle, CURLOPT_READDATA, request); + //curl_easy_setopt(sn->handle, CURLOPT_SEEKDATA, request); + //curl_easy_setopt(sn->handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)request->size); + curl_easy_setopt(sn->handle, CURLOPT_POSTFIELDSIZE, request->size); + curl_easy_setopt(sn->handle, CURLOPT_POSTFIELDS, request->space); CxBuffer *response = cxBufferCreate(NULL, NULL, 4096, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_FREE_CONTENTS); curl_easy_setopt(sn->handle, CURLOPT_WRITEFUNCTION, cxBufferWrite); curl_easy_setopt(sn->handle, CURLOPT_WRITEDATA, response); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Content-Type: application/json"); + headers = curl_slist_append(headers, "Accept: */*"); + curl_easy_setopt(sn->handle, CURLOPT_HTTPHEADER, headers); + CURLcode ret = curl_easy_perform(sn->handle); + curl_slist_free_all(headers); long http_status; curl_easy_getinfo(sn->handle, CURLINFO_RESPONSE_CODE, &http_status); CxJsonValue *json = NULL; diff -r 8470d5aac30e -r 0bcf3d69b37a libidav/jmapsession.c --- a/libidav/jmapsession.c Thu Jul 09 22:07:33 2026 +0200 +++ b/libidav/jmapsession.c Fri Jul 10 17:56:21 2026 +0200 @@ -78,16 +78,33 @@ CxJsonValue *uploadUrl = cxJsonObjGet(sessionValue, "uploadUrl"); if(cxJsonIsString(username)) { - sn->username = cx_strdup_a(mp->allocator, username->string.ptr).ptr; + sn->username = cx_strdup_a(mp->allocator, username->string).ptr; } if(cxJsonIsString(apiUrl)) { - sn->apiUrl = cx_strdup_a(mp->allocator, apiUrl->string.ptr).ptr; + sn->apiUrl = cx_strdup_a(mp->allocator, apiUrl->string).ptr; } if(cxJsonIsString(downloadUrl)) { - sn->downloadUrl = cx_strdup_a(mp->allocator, downloadUrl->string.ptr).ptr; + sn->downloadUrl = cx_strdup_a(mp->allocator, downloadUrl->string).ptr; } if(cxJsonIsString(uploadUrl)) { - sn->uploadUrl = cx_strdup_a(mp->allocator, uploadUrl->string.ptr).ptr; + sn->uploadUrl = cx_strdup_a(mp->allocator, uploadUrl->string).ptr; + } + + // primary accounts + CxJsonValue *primary_accounts = cxJsonObjGet(sessionValue, "primaryAccounts"); + if(cxJsonIsObject(primary_accounts)) { + CxJsonValue *pm_mail = cxJsonObjGet(primary_accounts, "urn:ietf:params:jmap:mail"); + CxJsonValue *pm_contacts = cxJsonObjGet(primary_accounts, "urn:ietf:params:jmap:contacts"); + CxJsonValue *pm_calendars = cxJsonObjGet(primary_accounts, "urn:ietf:params:jmap:calendars"); + if(cxJsonIsString(pm_mail)) { + sn->primary_accounts.mail = cx_strdup_a(mp->allocator, pm_mail->string).ptr; + } + if(cxJsonIsString(pm_contacts)) { + sn->primary_accounts.contacts = cx_strdup_a(mp->allocator, pm_contacts->string).ptr; + } + if(cxJsonIsString(pm_calendars)) { + sn->primary_accounts.calendars = cx_strdup_a(mp->allocator, pm_calendars->string).ptr; + } } return sn;