messages-dummy.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. *
  4. * OBEX Server
  5. *
  6. * Copyright (C) 2010-2011 Nokia Corporation
  7. *
  8. *
  9. */
  10. #ifdef HAVE_CONFIG_H
  11. #include <config.h>
  12. #endif
  13. #include <sys/types.h>
  14. #include <dirent.h>
  15. #include <stdio.h>
  16. #include <errno.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include "obexd/src/log.h"
  20. #include "messages.h"
  21. #define MSG_LIST_XML "mlisting.xml"
  22. static char *root_folder = NULL;
  23. struct session {
  24. char *cwd;
  25. char *cwd_absolute;
  26. void *request;
  27. };
  28. struct folder_listing_data {
  29. struct session *session;
  30. const char *name;
  31. uint16_t max;
  32. uint16_t offset;
  33. messages_folder_listing_cb callback;
  34. void *user_data;
  35. };
  36. struct message_listing_data {
  37. struct session *session;
  38. const char *name;
  39. uint16_t max;
  40. uint16_t offset;
  41. uint8_t subject_len;
  42. uint16_t size;
  43. char *path;
  44. FILE *fp;
  45. const struct messages_filter *filter;
  46. messages_get_messages_listing_cb callback;
  47. void *user_data;
  48. };
  49. /* NOTE: Neither IrOBEX nor MAP specs says that folder listing needs to
  50. * be sorted (in IrOBEX examples it is not). However existing implementations
  51. * seem to follow the fig. 3-2 from MAP specification v1.0, and I've seen a
  52. * test suite requiring folder listing to be in that order.
  53. */
  54. static int folder_names_cmp(gconstpointer a, gconstpointer b,
  55. gpointer user_data)
  56. {
  57. static const char *order[] = {
  58. "inbox", "outbox", "sent", "deleted", "draft", NULL
  59. };
  60. struct session *session = user_data;
  61. int ia, ib;
  62. if (g_strcmp0(session->cwd, "telecom/msg") == 0) {
  63. for (ia = 0; order[ia]; ia++) {
  64. if (g_strcmp0(a, order[ia]) == 0)
  65. break;
  66. }
  67. for (ib = 0; order[ib]; ib++) {
  68. if (g_strcmp0(b, order[ib]) == 0)
  69. break;
  70. }
  71. if (ia != ib)
  72. return ia - ib;
  73. }
  74. return g_strcmp0(a, b);
  75. }
  76. static char *get_next_subdir(DIR *dp, char *path)
  77. {
  78. struct dirent *ep;
  79. char *abs, *name;
  80. for (;;) {
  81. if ((ep = readdir(dp)) == NULL)
  82. return NULL;
  83. if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0)
  84. continue;
  85. abs = g_build_filename(path, ep->d_name, NULL);
  86. if (g_file_test(abs, G_FILE_TEST_IS_DIR)) {
  87. g_free(abs);
  88. break;
  89. }
  90. g_free(abs);
  91. }
  92. name = g_filename_to_utf8(ep->d_name, -1, NULL, NULL, NULL);
  93. if (name == NULL) {
  94. DBG("g_filename_to_utf8(): invalid filename");
  95. return NULL;
  96. }
  97. return name;
  98. }
  99. static ssize_t get_subdirs(struct folder_listing_data *fld, GSList **list)
  100. {
  101. DIR *dp;
  102. char *path, *name;
  103. size_t n;
  104. path = g_build_filename(fld->session->cwd_absolute, fld->name, NULL);
  105. dp = opendir(path);
  106. if (dp == NULL) {
  107. int err = -errno;
  108. DBG("opendir(): %d, %s", -err, strerror(-err));
  109. g_free(path);
  110. return err;
  111. }
  112. n = 0;
  113. while ((name = get_next_subdir(dp, path)) != NULL) {
  114. n++;
  115. if (fld->max > 0)
  116. *list = g_slist_prepend(*list, name);
  117. }
  118. closedir(dp);
  119. g_free(path);
  120. *list = g_slist_sort_with_data(*list, folder_names_cmp, fld->session);
  121. return n;
  122. }
  123. static void return_folder_listing(struct folder_listing_data *fld, GSList *list)
  124. {
  125. struct session *session = fld->session;
  126. GSList *cur;
  127. uint16_t num = 0;
  128. uint16_t offs = 0;
  129. for (cur = list; offs < fld->offset; offs++) {
  130. cur = cur->next;
  131. if (cur == NULL)
  132. break;
  133. }
  134. for (; cur != NULL && num < fld->max; cur = cur->next, num++)
  135. fld->callback(session, -EAGAIN, 0, cur->data, fld->user_data);
  136. fld->callback(session, 0, 0, NULL, fld->user_data);
  137. }
  138. static gboolean get_folder_listing(void *d)
  139. {
  140. struct folder_listing_data *fld = d;
  141. ssize_t n;
  142. GSList *list = NULL;
  143. n = get_subdirs(fld, &list);
  144. if (n < 0) {
  145. fld->callback(fld->session, n, 0, NULL, fld->user_data);
  146. return FALSE;
  147. }
  148. if (fld->max == 0) {
  149. fld->callback(fld->session, 0, n, NULL, fld->user_data);
  150. return FALSE;
  151. }
  152. return_folder_listing(fld, list);
  153. g_slist_free_full(list, g_free);
  154. return FALSE;
  155. }
  156. int messages_init(void)
  157. {
  158. char *tmp;
  159. if (root_folder)
  160. return 0;
  161. tmp = getenv("MAP_ROOT");
  162. if (tmp) {
  163. root_folder = g_strdup(tmp);
  164. return 0;
  165. }
  166. tmp = getenv("HOME");
  167. if (!tmp)
  168. return -ENOENT;
  169. root_folder = g_build_filename(tmp, "map-messages", NULL);
  170. return 0;
  171. }
  172. void messages_exit(void)
  173. {
  174. g_free(root_folder);
  175. root_folder = NULL;
  176. }
  177. int messages_connect(void **s)
  178. {
  179. struct session *session;
  180. session = g_new0(struct session, 1);
  181. session->cwd = g_strdup("");
  182. session->cwd_absolute = g_strdup(root_folder);
  183. *s = session;
  184. return 0;
  185. }
  186. void messages_disconnect(void *s)
  187. {
  188. struct session *session = s;
  189. g_free(session->cwd);
  190. g_free(session->cwd_absolute);
  191. g_free(session);
  192. }
  193. int messages_set_notification_registration(void *session,
  194. void (*send_event)(void *session,
  195. const struct messages_event *event, void *user_data),
  196. void *user_data)
  197. {
  198. return -ENOSYS;
  199. }
  200. int messages_set_folder(void *s, const char *name, gboolean cdup)
  201. {
  202. struct session *session = s;
  203. char *newrel = NULL;
  204. char *newabs;
  205. char *tmp;
  206. if (name && (strchr(name, '/') || strcmp(name, "..") == 0))
  207. return -EBADR;
  208. if (cdup) {
  209. if (session->cwd[0] == 0)
  210. return -ENOENT;
  211. newrel = g_path_get_dirname(session->cwd);
  212. /* We use empty string for indication of the root directory */
  213. if (newrel[0] == '.' && newrel[1] == 0)
  214. newrel[0] = 0;
  215. }
  216. tmp = newrel;
  217. if (!cdup && (!name || name[0] == 0))
  218. newrel = g_strdup("");
  219. else
  220. newrel = g_build_filename(newrel ? newrel : session->cwd, name,
  221. NULL);
  222. g_free(tmp);
  223. newabs = g_build_filename(root_folder, newrel, NULL);
  224. if (!g_file_test(newabs, G_FILE_TEST_IS_DIR)) {
  225. g_free(newrel);
  226. g_free(newabs);
  227. return -ENOENT;
  228. }
  229. g_free(session->cwd);
  230. session->cwd = newrel;
  231. g_free(session->cwd_absolute);
  232. session->cwd_absolute = newabs;
  233. return 0;
  234. }
  235. int messages_get_folder_listing(void *s, const char *name, uint16_t max,
  236. uint16_t offset,
  237. messages_folder_listing_cb callback,
  238. void *user_data)
  239. {
  240. struct session *session = s;
  241. struct folder_listing_data *fld;
  242. fld = g_new0(struct folder_listing_data, 1);
  243. fld->session = session;
  244. fld->name = name;
  245. fld->max = max;
  246. fld->offset = offset;
  247. fld->callback = callback;
  248. fld->user_data = user_data;
  249. session->request = fld;
  250. g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, get_folder_listing,
  251. fld, g_free);
  252. return 0;
  253. }
  254. static void max_msg_element(GMarkupParseContext *ctxt, const char *element,
  255. const char **names, const char **values,
  256. gpointer user_data, GError **gerr)
  257. {
  258. struct message_listing_data *mld = user_data;
  259. const char *key;
  260. int i;
  261. for (i = 0, key = names[i]; key; key = names[++i]) {
  262. if (g_strcmp0(names[i], "handle") == 0) {
  263. mld->size++;
  264. break;
  265. }
  266. }
  267. }
  268. static void msg_element(GMarkupParseContext *ctxt, const char *element,
  269. const char **names, const char **values,
  270. gpointer user_data, GError **gerr)
  271. {
  272. struct message_listing_data *mld = user_data;
  273. struct messages_message *entry = NULL;
  274. int i;
  275. entry = g_new0(struct messages_message, 1);
  276. if (mld->filter->parameter_mask == 0) {
  277. entry->mask = (entry->mask | PMASK_SUBJECT \
  278. | PMASK_DATETIME | PMASK_RECIPIENT_ADDRESSING \
  279. | PMASK_SENDER_ADDRESSING \
  280. | PMASK_ATTACHMENT_SIZE | PMASK_TYPE \
  281. | PMASK_RECEPTION_STATUS);
  282. } else
  283. entry->mask = mld->filter->parameter_mask;
  284. for (i = 0 ; names[i]; ++i) {
  285. if (g_strcmp0(names[i], "handle") == 0) {
  286. entry->handle = g_strdup(values[i]);
  287. mld->size++;
  288. continue;
  289. }
  290. if (g_strcmp0(names[i], "attachment_size") == 0) {
  291. entry->attachment_size = g_strdup(values[i]);
  292. continue;
  293. }
  294. if (g_strcmp0(names[i], "datetime") == 0) {
  295. entry->datetime = g_strdup(values[i]);
  296. continue;
  297. }
  298. if (g_strcmp0(names[i], "subject") == 0) {
  299. entry->subject = g_strdup(values[i]);
  300. continue;
  301. }
  302. if (g_strcmp0(names[i], "recipient_addressing") == 0) {
  303. entry->recipient_addressing = g_strdup(values[i]);
  304. continue;
  305. }
  306. if (g_strcmp0(names[i], "sender_addressing") == 0) {
  307. entry->sender_addressing = g_strdup(values[i]);
  308. continue;
  309. }
  310. if (g_strcmp0(names[i], "type") == 0) {
  311. entry->type = g_strdup(values[i]);
  312. continue;
  313. }
  314. if (g_strcmp0(names[i], "reception_status") == 0)
  315. entry->reception_status = g_strdup(values[i]);
  316. }
  317. if (mld->size > mld->offset)
  318. mld->callback(mld->session, -EAGAIN, mld->size, 0, entry, mld->user_data);
  319. g_free(entry->reception_status);
  320. g_free(entry->type);
  321. g_free(entry->sender_addressing);
  322. g_free(entry->subject);
  323. g_free(entry->datetime);
  324. g_free(entry->attachment_size);
  325. g_free(entry->handle);
  326. g_free(entry);
  327. }
  328. static const GMarkupParser msg_parser = {
  329. msg_element,
  330. NULL,
  331. NULL,
  332. NULL,
  333. NULL
  334. };
  335. static const GMarkupParser max_msg_parser = {
  336. max_msg_element,
  337. NULL,
  338. NULL,
  339. NULL,
  340. NULL
  341. };
  342. static gboolean get_messages_listing(void *d)
  343. {
  344. struct message_listing_data *mld = d;
  345. /* 1024 is the maximum size of the line which is calculated to be more
  346. * sufficient*/
  347. char buffer[1024];
  348. GMarkupParseContext *ctxt;
  349. size_t len;
  350. while (fgets(buffer, 1024, mld->fp)) {
  351. len = strlen(buffer);
  352. if (mld->max == 0) {
  353. ctxt = g_markup_parse_context_new(&max_msg_parser, 0, mld, NULL);
  354. g_markup_parse_context_parse(ctxt, buffer, len, NULL);
  355. g_markup_parse_context_free(ctxt);
  356. } else {
  357. ctxt = g_markup_parse_context_new(&msg_parser, 0, mld, NULL);
  358. g_markup_parse_context_parse(ctxt, buffer, len, NULL);
  359. g_markup_parse_context_free(ctxt);
  360. }
  361. }
  362. if (mld->max == 0) {
  363. mld->callback(mld->session, 0, mld->size, 0, NULL, mld->user_data);
  364. goto done;
  365. }
  366. mld->callback(mld->session, 0, mld->size, 0, NULL, mld->user_data);
  367. done:
  368. fclose(mld->fp);
  369. return FALSE;
  370. }
  371. int messages_get_messages_listing(void *session, const char *name,
  372. uint16_t max, uint16_t offset,
  373. uint8_t subject_len,
  374. const struct messages_filter *filter,
  375. messages_get_messages_listing_cb callback,
  376. void *user_data)
  377. {
  378. struct message_listing_data *mld;
  379. struct session *s = session;
  380. char *path;
  381. mld = g_new0(struct message_listing_data, 1);
  382. mld->session = s;
  383. mld->name = name;
  384. mld->max = max;
  385. mld->offset = offset;
  386. mld->subject_len = subject_len;
  387. mld->callback = callback;
  388. mld->filter = filter;
  389. mld->user_data = user_data;
  390. path = g_build_filename(s->cwd_absolute, MSG_LIST_XML, NULL);
  391. mld->fp = fopen(path, "r");
  392. if (mld->fp == NULL) {
  393. g_free(path);
  394. messages_set_folder(s, mld->name, 0);
  395. path = g_build_filename(s->cwd_absolute, MSG_LIST_XML, NULL);
  396. mld->fp = fopen(path, "r");
  397. if (mld->fp == NULL) {
  398. int err = -errno;
  399. DBG("fopen(): %d, %s", -err, strerror(-err));
  400. g_free(path);
  401. g_free(mld);
  402. return -EBADR;
  403. }
  404. }
  405. g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, get_messages_listing,
  406. mld, g_free);
  407. g_free(path);
  408. return 0;
  409. }
  410. int messages_get_message(void *session, const char *handle,
  411. unsigned long flags,
  412. messages_get_message_cb callback,
  413. void *user_data)
  414. {
  415. return -ENOSYS;
  416. }
  417. int messages_update_inbox(void *session, messages_status_cb callback,
  418. void *user_data)
  419. {
  420. return -ENOSYS;
  421. }
  422. int messages_set_read(void *session, const char *handle, uint8_t value,
  423. messages_status_cb callback, void *user_data)
  424. {
  425. return -ENOSYS;
  426. }
  427. int messages_set_delete(void *session, const char *handle, uint8_t value,
  428. messages_status_cb callback, void *user_data)
  429. {
  430. return -ENOSYS;
  431. }
  432. void messages_abort(void *s)
  433. {
  434. struct session *session = s;
  435. if (session->request) {
  436. g_idle_remove_by_data(session->request);
  437. session->request = NULL;
  438. }
  439. }