| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- *
- * OBEX Server
- *
- * Copyright (C) 2010-2011 Nokia Corporation
- *
- *
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #include <sys/types.h>
- #include <dirent.h>
- #include <stdio.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <string.h>
- #include "obexd/src/log.h"
- #include "messages.h"
- #define MSG_LIST_XML "mlisting.xml"
- static char *root_folder = NULL;
- struct session {
- char *cwd;
- char *cwd_absolute;
- void *request;
- };
- struct folder_listing_data {
- struct session *session;
- const char *name;
- uint16_t max;
- uint16_t offset;
- messages_folder_listing_cb callback;
- void *user_data;
- };
- struct message_listing_data {
- struct session *session;
- const char *name;
- uint16_t max;
- uint16_t offset;
- uint8_t subject_len;
- uint16_t size;
- char *path;
- FILE *fp;
- const struct messages_filter *filter;
- messages_get_messages_listing_cb callback;
- void *user_data;
- };
- /* NOTE: Neither IrOBEX nor MAP specs says that folder listing needs to
- * be sorted (in IrOBEX examples it is not). However existing implementations
- * seem to follow the fig. 3-2 from MAP specification v1.0, and I've seen a
- * test suite requiring folder listing to be in that order.
- */
- static int folder_names_cmp(gconstpointer a, gconstpointer b,
- gpointer user_data)
- {
- static const char *order[] = {
- "inbox", "outbox", "sent", "deleted", "draft", NULL
- };
- struct session *session = user_data;
- int ia, ib;
- if (g_strcmp0(session->cwd, "telecom/msg") == 0) {
- for (ia = 0; order[ia]; ia++) {
- if (g_strcmp0(a, order[ia]) == 0)
- break;
- }
- for (ib = 0; order[ib]; ib++) {
- if (g_strcmp0(b, order[ib]) == 0)
- break;
- }
- if (ia != ib)
- return ia - ib;
- }
- return g_strcmp0(a, b);
- }
- static char *get_next_subdir(DIR *dp, char *path)
- {
- struct dirent *ep;
- char *abs, *name;
- for (;;) {
- if ((ep = readdir(dp)) == NULL)
- return NULL;
- if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0)
- continue;
- abs = g_build_filename(path, ep->d_name, NULL);
- if (g_file_test(abs, G_FILE_TEST_IS_DIR)) {
- g_free(abs);
- break;
- }
- g_free(abs);
- }
- name = g_filename_to_utf8(ep->d_name, -1, NULL, NULL, NULL);
- if (name == NULL) {
- DBG("g_filename_to_utf8(): invalid filename");
- return NULL;
- }
- return name;
- }
- static ssize_t get_subdirs(struct folder_listing_data *fld, GSList **list)
- {
- DIR *dp;
- char *path, *name;
- size_t n;
- path = g_build_filename(fld->session->cwd_absolute, fld->name, NULL);
- dp = opendir(path);
- if (dp == NULL) {
- int err = -errno;
- DBG("opendir(): %d, %s", -err, strerror(-err));
- g_free(path);
- return err;
- }
- n = 0;
- while ((name = get_next_subdir(dp, path)) != NULL) {
- n++;
- if (fld->max > 0)
- *list = g_slist_prepend(*list, name);
- }
- closedir(dp);
- g_free(path);
- *list = g_slist_sort_with_data(*list, folder_names_cmp, fld->session);
- return n;
- }
- static void return_folder_listing(struct folder_listing_data *fld, GSList *list)
- {
- struct session *session = fld->session;
- GSList *cur;
- uint16_t num = 0;
- uint16_t offs = 0;
- for (cur = list; offs < fld->offset; offs++) {
- cur = cur->next;
- if (cur == NULL)
- break;
- }
- for (; cur != NULL && num < fld->max; cur = cur->next, num++)
- fld->callback(session, -EAGAIN, 0, cur->data, fld->user_data);
- fld->callback(session, 0, 0, NULL, fld->user_data);
- }
- static gboolean get_folder_listing(void *d)
- {
- struct folder_listing_data *fld = d;
- ssize_t n;
- GSList *list = NULL;
- n = get_subdirs(fld, &list);
- if (n < 0) {
- fld->callback(fld->session, n, 0, NULL, fld->user_data);
- return FALSE;
- }
- if (fld->max == 0) {
- fld->callback(fld->session, 0, n, NULL, fld->user_data);
- return FALSE;
- }
- return_folder_listing(fld, list);
- g_slist_free_full(list, g_free);
- return FALSE;
- }
- int messages_init(void)
- {
- char *tmp;
- if (root_folder)
- return 0;
- tmp = getenv("MAP_ROOT");
- if (tmp) {
- root_folder = g_strdup(tmp);
- return 0;
- }
- tmp = getenv("HOME");
- if (!tmp)
- return -ENOENT;
- root_folder = g_build_filename(tmp, "map-messages", NULL);
- return 0;
- }
- void messages_exit(void)
- {
- g_free(root_folder);
- root_folder = NULL;
- }
- int messages_connect(void **s)
- {
- struct session *session;
- session = g_new0(struct session, 1);
- session->cwd = g_strdup("");
- session->cwd_absolute = g_strdup(root_folder);
- *s = session;
- return 0;
- }
- void messages_disconnect(void *s)
- {
- struct session *session = s;
- g_free(session->cwd);
- g_free(session->cwd_absolute);
- g_free(session);
- }
- int messages_set_notification_registration(void *session,
- void (*send_event)(void *session,
- const struct messages_event *event, void *user_data),
- void *user_data)
- {
- return -ENOSYS;
- }
- int messages_set_folder(void *s, const char *name, gboolean cdup)
- {
- struct session *session = s;
- char *newrel = NULL;
- char *newabs;
- char *tmp;
- if (name && (strchr(name, '/') || strcmp(name, "..") == 0))
- return -EBADR;
- if (cdup) {
- if (session->cwd[0] == 0)
- return -ENOENT;
- newrel = g_path_get_dirname(session->cwd);
- /* We use empty string for indication of the root directory */
- if (newrel[0] == '.' && newrel[1] == 0)
- newrel[0] = 0;
- }
- tmp = newrel;
- if (!cdup && (!name || name[0] == 0))
- newrel = g_strdup("");
- else
- newrel = g_build_filename(newrel ? newrel : session->cwd, name,
- NULL);
- g_free(tmp);
- newabs = g_build_filename(root_folder, newrel, NULL);
- if (!g_file_test(newabs, G_FILE_TEST_IS_DIR)) {
- g_free(newrel);
- g_free(newabs);
- return -ENOENT;
- }
- g_free(session->cwd);
- session->cwd = newrel;
- g_free(session->cwd_absolute);
- session->cwd_absolute = newabs;
- return 0;
- }
- int messages_get_folder_listing(void *s, const char *name, uint16_t max,
- uint16_t offset,
- messages_folder_listing_cb callback,
- void *user_data)
- {
- struct session *session = s;
- struct folder_listing_data *fld;
- fld = g_new0(struct folder_listing_data, 1);
- fld->session = session;
- fld->name = name;
- fld->max = max;
- fld->offset = offset;
- fld->callback = callback;
- fld->user_data = user_data;
- session->request = fld;
- g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, get_folder_listing,
- fld, g_free);
- return 0;
- }
- static void max_msg_element(GMarkupParseContext *ctxt, const char *element,
- const char **names, const char **values,
- gpointer user_data, GError **gerr)
- {
- struct message_listing_data *mld = user_data;
- const char *key;
- int i;
- for (i = 0, key = names[i]; key; key = names[++i]) {
- if (g_strcmp0(names[i], "handle") == 0) {
- mld->size++;
- break;
- }
- }
- }
- static void msg_element(GMarkupParseContext *ctxt, const char *element,
- const char **names, const char **values,
- gpointer user_data, GError **gerr)
- {
- struct message_listing_data *mld = user_data;
- struct messages_message *entry = NULL;
- int i;
- entry = g_new0(struct messages_message, 1);
- if (mld->filter->parameter_mask == 0) {
- entry->mask = (entry->mask | PMASK_SUBJECT \
- | PMASK_DATETIME | PMASK_RECIPIENT_ADDRESSING \
- | PMASK_SENDER_ADDRESSING \
- | PMASK_ATTACHMENT_SIZE | PMASK_TYPE \
- | PMASK_RECEPTION_STATUS);
- } else
- entry->mask = mld->filter->parameter_mask;
- for (i = 0 ; names[i]; ++i) {
- if (g_strcmp0(names[i], "handle") == 0) {
- entry->handle = g_strdup(values[i]);
- mld->size++;
- continue;
- }
- if (g_strcmp0(names[i], "attachment_size") == 0) {
- entry->attachment_size = g_strdup(values[i]);
- continue;
- }
- if (g_strcmp0(names[i], "datetime") == 0) {
- entry->datetime = g_strdup(values[i]);
- continue;
- }
- if (g_strcmp0(names[i], "subject") == 0) {
- entry->subject = g_strdup(values[i]);
- continue;
- }
- if (g_strcmp0(names[i], "recipient_addressing") == 0) {
- entry->recipient_addressing = g_strdup(values[i]);
- continue;
- }
- if (g_strcmp0(names[i], "sender_addressing") == 0) {
- entry->sender_addressing = g_strdup(values[i]);
- continue;
- }
- if (g_strcmp0(names[i], "type") == 0) {
- entry->type = g_strdup(values[i]);
- continue;
- }
- if (g_strcmp0(names[i], "reception_status") == 0)
- entry->reception_status = g_strdup(values[i]);
- }
- if (mld->size > mld->offset)
- mld->callback(mld->session, -EAGAIN, mld->size, 0, entry, mld->user_data);
- g_free(entry->reception_status);
- g_free(entry->type);
- g_free(entry->sender_addressing);
- g_free(entry->subject);
- g_free(entry->datetime);
- g_free(entry->attachment_size);
- g_free(entry->handle);
- g_free(entry);
- }
- static const GMarkupParser msg_parser = {
- msg_element,
- NULL,
- NULL,
- NULL,
- NULL
- };
- static const GMarkupParser max_msg_parser = {
- max_msg_element,
- NULL,
- NULL,
- NULL,
- NULL
- };
- static gboolean get_messages_listing(void *d)
- {
- struct message_listing_data *mld = d;
- /* 1024 is the maximum size of the line which is calculated to be more
- * sufficient*/
- char buffer[1024];
- GMarkupParseContext *ctxt;
- size_t len;
- while (fgets(buffer, 1024, mld->fp)) {
- len = strlen(buffer);
- if (mld->max == 0) {
- ctxt = g_markup_parse_context_new(&max_msg_parser, 0, mld, NULL);
- g_markup_parse_context_parse(ctxt, buffer, len, NULL);
- g_markup_parse_context_free(ctxt);
- } else {
- ctxt = g_markup_parse_context_new(&msg_parser, 0, mld, NULL);
- g_markup_parse_context_parse(ctxt, buffer, len, NULL);
- g_markup_parse_context_free(ctxt);
- }
- }
- if (mld->max == 0) {
- mld->callback(mld->session, 0, mld->size, 0, NULL, mld->user_data);
- goto done;
- }
- mld->callback(mld->session, 0, mld->size, 0, NULL, mld->user_data);
- done:
- fclose(mld->fp);
- return FALSE;
- }
- int messages_get_messages_listing(void *session, const char *name,
- uint16_t max, uint16_t offset,
- uint8_t subject_len,
- const struct messages_filter *filter,
- messages_get_messages_listing_cb callback,
- void *user_data)
- {
- struct message_listing_data *mld;
- struct session *s = session;
- char *path;
- mld = g_new0(struct message_listing_data, 1);
- mld->session = s;
- mld->name = name;
- mld->max = max;
- mld->offset = offset;
- mld->subject_len = subject_len;
- mld->callback = callback;
- mld->filter = filter;
- mld->user_data = user_data;
- path = g_build_filename(s->cwd_absolute, MSG_LIST_XML, NULL);
- mld->fp = fopen(path, "r");
- if (mld->fp == NULL) {
- g_free(path);
- messages_set_folder(s, mld->name, 0);
- path = g_build_filename(s->cwd_absolute, MSG_LIST_XML, NULL);
- mld->fp = fopen(path, "r");
- if (mld->fp == NULL) {
- int err = -errno;
- DBG("fopen(): %d, %s", -err, strerror(-err));
- g_free(path);
- g_free(mld);
- return -EBADR;
- }
- }
- g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, get_messages_listing,
- mld, g_free);
- g_free(path);
- return 0;
- }
- int messages_get_message(void *session, const char *handle,
- unsigned long flags,
- messages_get_message_cb callback,
- void *user_data)
- {
- return -ENOSYS;
- }
- int messages_update_inbox(void *session, messages_status_cb callback,
- void *user_data)
- {
- return -ENOSYS;
- }
- int messages_set_read(void *session, const char *handle, uint8_t value,
- messages_status_cb callback, void *user_data)
- {
- return -ENOSYS;
- }
- int messages_set_delete(void *session, const char *handle, uint8_t value,
- messages_status_cb callback, void *user_data)
- {
- return -ENOSYS;
- }
- void messages_abort(void *s)
- {
- struct session *session = s;
- if (session->request) {
- g_idle_remove_by_data(session->request);
- session->request = NULL;
- }
- }
|