PageRenderTime 54ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/response.c

https://github.com/crshd/imapfilter
C | 844 lines | 579 code | 184 blank | 81 comment | 141 complexity | 7c0c25905e626a224496f18da429b105 MD5 | raw file
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <strings.h>
  5. #include <ctype.h>
  6. #include <regex.h>
  7. #include "imapfilter.h"
  8. #include "session.h"
  9. #include "buffer.h"
  10. #include "regexp.h"
  11. extern options opts;
  12. buffer ibuf; /* Input buffer. */
  13. enum { /* Server data responses to be parsed;
  14. * regular expressions index. */
  15. DATA_RESPONSE_TAGGED,
  16. DATA_RESPONSE_CAPABILITY,
  17. DATA_RESPONSE_AUTHENTICATE,
  18. DATA_RESPONSE_NAMESPACE,
  19. DATA_RESPONSE_STATUS,
  20. DATA_RESPONSE_STATUS_MESSAGES,
  21. DATA_RESPONSE_STATUS_RECENT,
  22. DATA_RESPONSE_STATUS_UNSEEN,
  23. DATA_RESPONSE_STATUS_UIDNEXT,
  24. DATA_RESPONSE_EXAMINE_EXISTS,
  25. DATA_RESPONSE_EXAMINE_RECENT,
  26. DATA_RESPONSE_LIST,
  27. DATA_RESPONSE_SEARCH,
  28. DATA_RESPONSE_FETCH,
  29. DATA_RESPONSE_FETCH_FLAGS,
  30. DATA_RESPONSE_FETCH_DATE,
  31. DATA_RESPONSE_FETCH_SIZE,
  32. DATA_RESPONSE_FETCH_STRUCTURE,
  33. DATA_RESPONSE_FETCH_BODY,
  34. DATA_RESPONSE_IDLE,
  35. };
  36. regexp responses[] = { /* Server data responses to be parsed;
  37. * regular expressions patterns. */
  38. { "([0-9A-F]{4,4}) (OK|NO|BAD) .*\r\n", NULL, 0, NULL },
  39. { "\\* CAPABILITY (.*)\r\n", NULL, 0, NULL },
  40. { "\\+ ([^ ]*)\r\n", NULL, 0, NULL },
  41. { "\\* NAMESPACE (NIL|\\(\\(\"([^ ]*)\" \"(.)\"\\).*\\)) "
  42. "(NIL|\\(.*\\)) (NIL|\\(.*\\))\r\n", NULL, 0, NULL },
  43. { "\\* STATUS .* \\(([0-9A-Z ]*)\\) *\r\n", NULL, 0, NULL },
  44. { "MESSAGES ([0-9]+)", NULL, 0, NULL },
  45. { "RECENT ([0-9]+)", NULL, 0, NULL },
  46. { "UNSEEN ([0-9]+)", NULL, 0, NULL },
  47. { "UIDNEXT ([0-9]+)", NULL, 0, NULL },
  48. { "\\* ([0-9]+) EXISTS\r\n", NULL, 0, NULL },
  49. { "\\* ([0-9]+) RECENT\r\n", NULL, 0, NULL },
  50. { "\\* (LIST|LSUB) \\((.*)\\) (\".\"|NIL) "
  51. "(\"(.+)\"|(.+)|\\{([0-9]+)\\}\r\n(.*))\r\n", NULL, 0, NULL },
  52. { "\\* SEARCH ?([0-9 ]*)\r\n", NULL, 0, NULL },
  53. { "\\* [0-9]+ FETCH \\((.*)\\)\r\n", NULL, 0, NULL },
  54. { "FLAGS \\((.*)\\)", NULL, 0, NULL },
  55. { "INTERNALDATE \"(.*)\"", NULL, 0, NULL },
  56. { "RFC822.SIZE ([0-9]+)", NULL, 0, NULL },
  57. { "BODYSTRUCTURE (\\(.+\\))", NULL, 0, NULL },
  58. { "\\* [0-9]+ FETCH \\(.*BODY\\[.*\\] (\\{([0-9]+)\\}\r\n|\"(.*)\")",
  59. NULL, 0, NULL },
  60. { "\\* [0-9]+ (RECENT|EXISTS)\r\n", NULL, 0, NULL },
  61. { NULL, NULL, 0, NULL }
  62. };
  63. int receive_response(session *ssn, char *buf, long timeout, int timeoutfail);
  64. int check_tag(char *buf, session *ssn, int tag);
  65. int check_bye(char *buf);
  66. int check_continuation(char *buf);
  67. int check_trycreate(char *buf);
  68. /*
  69. * Read data the server sent.
  70. */
  71. int
  72. receive_response(session *ssn, char *buf, long timeout, int timeoutfail)
  73. {
  74. ssize_t n;
  75. if ((n = socket_read(ssn, buf, INPUT_BUF, timeout ? timeout :
  76. (long)(get_option_number("timeout")), timeoutfail)) == -1)
  77. return -1;
  78. if (opts.debug) {
  79. int i;
  80. debug("getting response (%d):\n\n", ssn->socket);
  81. for (i = 0; i < n; i++)
  82. debugc(buf[i]);
  83. debug("\n");
  84. }
  85. return n;
  86. }
  87. /*
  88. * Search for tagged response in the data that the server sent.
  89. */
  90. int
  91. check_tag(char *buf, session *ssn, int tag)
  92. {
  93. int r;
  94. char t[4 + 1];
  95. regexp *re;
  96. r = STATUS_RESPONSE_NONE;
  97. snprintf(t, sizeof(t), "%04X", tag);
  98. re = &responses[DATA_RESPONSE_TAGGED];
  99. if (!regexec(re->preg, buf, re->nmatch, re->pmatch, 0)) {
  100. if (!strncasecmp(buf + re->pmatch[1].rm_so, t,
  101. strlen(t))) {
  102. if (!strncasecmp(buf + re->pmatch[2].rm_so,
  103. "OK", strlen("OK")))
  104. r = STATUS_RESPONSE_OK;
  105. else if (!strncasecmp(buf + re->pmatch[2].rm_so,
  106. "NO", strlen("NO")))
  107. r = STATUS_RESPONSE_NO;
  108. else if (!strncasecmp(buf + re->pmatch[2].rm_so,
  109. "BAD", strlen("BAD")))
  110. r = STATUS_RESPONSE_BAD;
  111. }
  112. }
  113. if (r != STATUS_RESPONSE_NONE)
  114. verbose("S (%d): %s", ssn->socket, buf + re->pmatch[0].rm_so);
  115. if (r == STATUS_RESPONSE_NO || r == STATUS_RESPONSE_BAD)
  116. error("IMAP (%d): %s", ssn->socket, buf + re->pmatch[0].rm_so);
  117. return r;
  118. }
  119. /*
  120. * Check if server sent a BYE response (connection is closed immediately).
  121. */
  122. int
  123. check_bye(char *buf)
  124. {
  125. if (xstrcasestr(buf, "* BYE") &&
  126. !xstrcasestr(buf, " LOGOUT "))
  127. return 1;
  128. else
  129. return 0;
  130. }
  131. /*
  132. * Check if server sent a PREAUTH response (connection already authenticated
  133. * by external means).
  134. */
  135. int
  136. check_preauth(char *buf)
  137. {
  138. if (xstrcasestr(ibuf.data, "* PREAUTH"))
  139. return 1;
  140. else
  141. return 0;
  142. }
  143. /*
  144. * Check if the server sent a continuation request.
  145. */
  146. int
  147. check_continuation(char *buf)
  148. {
  149. if ((buf[0] == '+' && buf[1] == ' ') || xstrcasestr(buf, "\r\n+ "))
  150. return 1;
  151. else
  152. return 0;
  153. }
  154. /*
  155. * Check if the server sent a TRYCREATE response.
  156. */
  157. int
  158. check_trycreate(char *buf)
  159. {
  160. if (xstrcasestr(buf, "[TRYCREATE]"))
  161. return 1;
  162. else
  163. return 0;
  164. }
  165. /*
  166. * Get server data and make sure there is a tagged response inside them.
  167. */
  168. int
  169. response_generic(session *ssn, int tag)
  170. {
  171. int r;
  172. ssize_t n;
  173. if (tag == -1)
  174. return -1;
  175. buffer_reset(&ibuf);
  176. do {
  177. buffer_check(&ibuf, ibuf.len + INPUT_BUF);
  178. if ((n = receive_response(ssn, ibuf.data + ibuf.len, 0, 1)) == -1)
  179. return -1;
  180. ibuf.len += n;
  181. if (check_bye(ibuf.data))
  182. return -1;
  183. } while ((r = check_tag(ibuf.data, ssn, tag)) == STATUS_RESPONSE_NONE);
  184. if (r == STATUS_RESPONSE_NO &&
  185. (check_trycreate(ibuf.data) || get_option_boolean("create")))
  186. return STATUS_RESPONSE_TRYCREATE;
  187. return r;
  188. }
  189. /*
  190. * Get server data and make sure there is a continuation response inside them.
  191. */
  192. int
  193. response_continuation(session *ssn)
  194. {
  195. ssize_t n;
  196. buffer_reset(&ibuf);
  197. do {
  198. buffer_check(&ibuf, ibuf.len + INPUT_BUF);
  199. if ((n = receive_response(ssn, ibuf.data + ibuf.len, 0, 1)) == -1)
  200. return -1;
  201. ibuf.len += n;
  202. if (check_bye(ibuf.data))
  203. return -1;
  204. } while (!check_continuation(ibuf.data));
  205. return STATUS_RESPONSE_CONTINUE;
  206. }
  207. /*
  208. * Process the greeting that server sends during connection.
  209. */
  210. int
  211. response_greeting(session *ssn)
  212. {
  213. buffer_reset(&ibuf);
  214. if (receive_response(ssn, ibuf.data, 0, 1) == -1)
  215. return -1;
  216. verbose("S (%d): %s", ssn->socket, ibuf.data);
  217. if (check_bye(ibuf.data))
  218. return -1;
  219. if (check_preauth(ibuf.data))
  220. return STATUS_RESPONSE_PREAUTH;
  221. return STATUS_RESPONSE_NONE;
  222. }
  223. /*
  224. * Process the data that server sent due to IMAP CAPABILITY client request.
  225. */
  226. int
  227. response_capability(session *ssn, int tag)
  228. {
  229. int r;
  230. char *s;
  231. regexp *re;
  232. if ((r = response_generic(ssn, tag)) == -1)
  233. return -1;
  234. ssn->protocol = PROTOCOL_NONE;
  235. re = &responses[DATA_RESPONSE_CAPABILITY];
  236. if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0)) {
  237. s = xstrndup(ibuf.data + re->pmatch[1].rm_so,
  238. re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
  239. if (xstrcasestr(s, "IMAP4rev1"))
  240. ssn->protocol = PROTOCOL_IMAP4REV1;
  241. else if (xstrcasestr(s, "IMAP4"))
  242. ssn->protocol = PROTOCOL_IMAP4;
  243. else {
  244. error("server supports neither the IMAP4rev1 nor the "
  245. "IMAP4 protocol\n");
  246. return -1;
  247. }
  248. ssn->capabilities = CAPABILITY_NONE;
  249. if (xstrcasestr(s, "NAMESPACE"))
  250. ssn->capabilities |= CAPABILITY_NAMESPACE;
  251. #ifndef NO_CRAMMD5
  252. if (xstrcasestr(s, "AUTH=CRAM-MD5"))
  253. ssn->capabilities |= CAPABILITY_CRAMMD5;
  254. #endif
  255. #ifndef NO_SSLTLS
  256. if (xstrcasestr(s, "STARTTLS"))
  257. ssn->capabilities |= CAPABILITY_STARTTLS;
  258. #endif
  259. if (xstrcasestr(s, "CHILDREN"))
  260. ssn->capabilities |= CAPABILITY_CHILDREN;
  261. if (xstrcasestr(s, "IDLE"))
  262. ssn->capabilities |= CAPABILITY_IDLE;
  263. xfree(s);
  264. }
  265. return r;
  266. }
  267. #ifndef NO_CRAMMD5
  268. /*
  269. * Process the data that server sent due to IMAP AUTHENTICATE client request.
  270. */
  271. int
  272. response_authenticate(session *ssn, int tag, unsigned char **cont)
  273. {
  274. int r;
  275. regexp *re;
  276. re = &responses[DATA_RESPONSE_AUTHENTICATE];
  277. if ((r = response_continuation(ssn)) == STATUS_RESPONSE_CONTINUE &&
  278. !regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0))
  279. *cont = (unsigned char *)xstrndup(ibuf.data + re->pmatch[1].rm_so,
  280. re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
  281. return r;
  282. }
  283. #endif
  284. /*
  285. * Process the data that server sent due to IMAP NAMESPACE client request.
  286. */
  287. int
  288. response_namespace(session *ssn, int tag)
  289. {
  290. int r, n;
  291. regexp *re;
  292. if ((r = response_generic(ssn, tag)) == -1)
  293. return -1;
  294. ssn->ns.prefix = NULL;
  295. ssn->ns.delim = '\0';
  296. re = &responses[DATA_RESPONSE_NAMESPACE];
  297. if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0)) {
  298. n = re->pmatch[2].rm_eo - re->pmatch[2].rm_so;
  299. if (n > 0)
  300. ssn->ns.prefix = xstrndup(ibuf.data +
  301. re->pmatch[2].rm_so, n);
  302. ssn->ns.delim = *(ibuf.data + re->pmatch[3].rm_so);
  303. }
  304. debug("namespace (%d): '%s' '%c'\n", ssn->socket,
  305. (ssn->ns.prefix ? ssn->ns.prefix : ""), ssn->ns.delim);
  306. return r;
  307. }
  308. /*
  309. * Process the data that server sent due to IMAP STATUS client request.
  310. */
  311. int
  312. response_status(session *ssn, int tag, unsigned int *exist,
  313. unsigned int *recent, unsigned int *unseen, unsigned int *uidnext)
  314. {
  315. int r;
  316. char *s;
  317. regexp *re;
  318. if ((r = response_generic(ssn, tag)) == -1)
  319. return -1;
  320. re = &responses[DATA_RESPONSE_STATUS];
  321. if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0)) {
  322. s = xstrndup(ibuf.data + re->pmatch[1].rm_so,
  323. re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
  324. re = &responses[DATA_RESPONSE_STATUS_MESSAGES];
  325. if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
  326. *exist = strtol(s + re->pmatch[1].rm_so, NULL, 10);
  327. re = &responses[DATA_RESPONSE_STATUS_RECENT];
  328. if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
  329. *recent = strtol(s + re->pmatch[1].rm_so, NULL, 10);
  330. re = &responses[DATA_RESPONSE_STATUS_UNSEEN];
  331. if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
  332. *unseen = strtol(s + re->pmatch[1].rm_so, NULL, 10);
  333. re = &responses[DATA_RESPONSE_STATUS_UIDNEXT];
  334. if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
  335. *uidnext = strtol(s + re->pmatch[1].rm_so, NULL, 10);
  336. xfree(s);
  337. }
  338. return r;
  339. }
  340. /*
  341. * Process the data that server sent due to IMAP EXAMINE client request.
  342. */
  343. int
  344. response_examine(session *ssn, int tag, unsigned int *exist,
  345. unsigned int *recent)
  346. {
  347. int r;
  348. regexp *re;
  349. if ((r = response_generic(ssn, tag)) == -1)
  350. return -1;
  351. re = &responses[DATA_RESPONSE_EXAMINE_EXISTS];
  352. if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0))
  353. *exist = strtol(ibuf.data + re->pmatch[1].rm_so, NULL, 10);
  354. re = &responses[DATA_RESPONSE_EXAMINE_RECENT];
  355. if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0))
  356. *recent = strtol(ibuf.data + re->pmatch[1].rm_so, NULL, 10);
  357. return r;
  358. }
  359. /*
  360. * Process the data that server sent due to IMAP SELECT client request.
  361. */
  362. int
  363. response_select(session *ssn, int tag)
  364. {
  365. int r;
  366. if ((r = response_generic(ssn, tag)) == -1)
  367. return -1;
  368. if (xstrcasestr(ibuf.data, "[READ-ONLY]"))
  369. return STATUS_RESPONSE_READONLY;
  370. return r;
  371. }
  372. /*
  373. * Process the data that server sent due to IMAP LIST or IMAP LSUB client
  374. * request.
  375. */
  376. int
  377. response_list(session *ssn, int tag, char **mboxs, char **folders)
  378. {
  379. int r, n;
  380. char *b, *a, *s, *m, *f;
  381. const char *v;
  382. regexp *re;
  383. if ((r = response_generic(ssn, tag)) == -1)
  384. return -1;
  385. m = *mboxs = (char *)xmalloc((ibuf.len + 1) * sizeof(char));
  386. f = *folders = (char *)xmalloc((ibuf.len + 1) * sizeof(char));
  387. *m = *f = '\0';
  388. re = &responses[DATA_RESPONSE_LIST];
  389. b = ibuf.data;
  390. while (!regexec(re->preg, b, re->nmatch, re->pmatch, 0)) {
  391. a = xstrndup(b + re->pmatch[2].rm_so,
  392. re->pmatch[2].rm_eo - re->pmatch[2].rm_so);
  393. if (re->pmatch[5].rm_so != -1 && re->pmatch[5].rm_so != -1)
  394. s = xstrndup(b + re->pmatch[5].rm_so,
  395. re->pmatch[5].rm_eo - re->pmatch[5].rm_so);
  396. else if (re->pmatch[6].rm_so != -1 &&
  397. re->pmatch[6].rm_so != -1)
  398. s = xstrndup(b + re->pmatch[6].rm_so,
  399. re->pmatch[6].rm_eo - re->pmatch[6].rm_so);
  400. else
  401. s = xstrndup(b + re->pmatch[8].rm_so, strtoul(b +
  402. re->pmatch[7].rm_so, NULL, 10));
  403. v = reverse_namespace(s, ssn->ns.prefix, ssn->ns.delim);
  404. n = strlen(v);
  405. if (!xstrcasestr(a, "\\NoSelect")) {
  406. xstrncpy(m, v, ibuf.len - (m - *mboxs));
  407. m += n;
  408. xstrncpy(m, "\n", ibuf.len - (m - *mboxs));
  409. m += strlen("\n");
  410. }
  411. if (!xstrcasestr(a, "\\NoInferiors") &&
  412. (!(ssn->capabilities & CAPABILITY_CHILDREN) ||
  413. ((ssn->capabilities & CAPABILITY_CHILDREN) &&
  414. (xstrcasestr(a, "\\HasChildren")) &&
  415. !xstrcasestr(a, "\\HasNoChildren")))) {
  416. xstrncpy(f, v, ibuf.len - (f - *folders));
  417. f += n;
  418. xstrncpy(f, "\n", ibuf.len - (f - *folders));
  419. f += strlen("\n");
  420. }
  421. b += re->pmatch[0].rm_eo;
  422. xfree(a);
  423. xfree(s);
  424. }
  425. return r;
  426. }
  427. /*
  428. * Process the data that server sent due to IMAP SEARCH client request.
  429. */
  430. int
  431. response_search(session *ssn, int tag, char **mesgs)
  432. {
  433. int r;
  434. unsigned int min;
  435. regexp *re;
  436. char *b, *m;
  437. if ((r = response_generic(ssn, tag)) == -1)
  438. return -1;
  439. re = &responses[DATA_RESPONSE_SEARCH];
  440. b = ibuf.data;
  441. m = NULL;
  442. while (!regexec(re->preg, b, re->nmatch, re->pmatch, 0)) {
  443. if (!*mesgs) {
  444. m = *mesgs = (char *)xmalloc((ibuf.len + 1) *
  445. sizeof(char));
  446. *m = '\0';
  447. }
  448. min = (unsigned int)(re->pmatch[1].rm_eo - re->pmatch[1].rm_so) < ibuf.len ?
  449. (unsigned int)(re->pmatch[1].rm_eo - re->pmatch[1].rm_so) :
  450. ibuf.len;
  451. xstrncpy(m, b + re->pmatch[1].rm_so, min);
  452. m += min;
  453. xstrncpy(m++, " ", ibuf.len - min);
  454. b += re->pmatch[0].rm_eo;
  455. }
  456. return r;
  457. }
  458. /*
  459. * Process the data that server sent due to IMAP FETCH FAST client request.
  460. */
  461. int
  462. response_fetchfast(session *ssn, int tag, char **flags, char **date,
  463. char **size)
  464. {
  465. int r;
  466. char *s;
  467. regexp *re;
  468. if ((r = response_generic(ssn, tag)) == -1)
  469. return -1;
  470. re = &responses[DATA_RESPONSE_FETCH];
  471. if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0)) {
  472. s = xstrndup(ibuf.data + re->pmatch[1].rm_so,
  473. re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
  474. re = &responses[DATA_RESPONSE_FETCH_FLAGS];
  475. if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
  476. *flags = xstrndup(s + re->pmatch[1].rm_so,
  477. re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
  478. re = &responses[DATA_RESPONSE_FETCH_DATE];
  479. if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
  480. *date = xstrndup(s + re->pmatch[1].rm_so,
  481. re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
  482. re = &responses[DATA_RESPONSE_FETCH_SIZE];
  483. if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
  484. *size = xstrndup(s + re->pmatch[1].rm_so,
  485. re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
  486. xfree(s);
  487. }
  488. return r;
  489. }
  490. /*
  491. * Process the data that server sent due to IMAP FETCH FLAGS client request.
  492. */
  493. int
  494. response_fetchflags(session *ssn, int tag, char **flags)
  495. {
  496. int r;
  497. char *s;
  498. regexp *re;
  499. if ((r = response_generic(ssn, tag)) == -1)
  500. return -1;
  501. re = &responses[DATA_RESPONSE_FETCH];
  502. if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0)) {
  503. s = xstrndup(ibuf.data + re->pmatch[1].rm_so,
  504. re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
  505. re = &responses[DATA_RESPONSE_FETCH_FLAGS];
  506. if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
  507. *flags = xstrndup(s + re->pmatch[1].rm_so,
  508. re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
  509. xfree(s);
  510. }
  511. return r;
  512. }
  513. /*
  514. * Process the data that server sent due to IMAP FETCH INTERNALDATE client
  515. * request.
  516. */
  517. int
  518. response_fetchdate(session *ssn, int tag, char **date)
  519. {
  520. int r;
  521. char *s;
  522. regexp *re;
  523. if ((r = response_generic(ssn, tag)) == -1)
  524. return -1;
  525. re = &responses[DATA_RESPONSE_FETCH];
  526. if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0)) {
  527. s = xstrndup(ibuf.data + re->pmatch[1].rm_so,
  528. re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
  529. re = &responses[DATA_RESPONSE_FETCH_DATE];
  530. if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
  531. *date = xstrndup(s + re->pmatch[1].rm_so,
  532. re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
  533. xfree(s);
  534. }
  535. return r;
  536. }
  537. /*
  538. * Process the data that server sent due to IMAP FETCH RFC822.SIZE client
  539. * request.
  540. */
  541. int
  542. response_fetchsize(session *ssn, int tag, char **size)
  543. {
  544. int r;
  545. char *s;
  546. regexp *re;
  547. if ((r = response_generic(ssn, tag)) == -1)
  548. return -1;
  549. re = &responses[DATA_RESPONSE_FETCH];
  550. if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0)) {
  551. s = xstrndup(ibuf.data + re->pmatch[1].rm_so,
  552. re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
  553. re = &responses[DATA_RESPONSE_FETCH_SIZE];
  554. if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
  555. *size = xstrndup(s + re->pmatch[1].rm_so,
  556. re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
  557. xfree(s);
  558. }
  559. return r;
  560. }
  561. /*
  562. * Process the data that server sent due to IMAP FETCH BODYSTRUCTURE client
  563. * request.
  564. */
  565. int
  566. response_fetchstructure(session *ssn, int tag, char **structure)
  567. {
  568. int r;
  569. char *s;
  570. regexp *re;
  571. if ((r = response_generic(ssn, tag)) == -1)
  572. return -1;
  573. re = &responses[DATA_RESPONSE_FETCH];
  574. if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0)) {
  575. s = xstrndup(ibuf.data + re->pmatch[1].rm_so,
  576. re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
  577. re = &responses[DATA_RESPONSE_FETCH_STRUCTURE];
  578. if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0)) {
  579. *structure = xstrndup(s + re->pmatch[1].rm_so,
  580. re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
  581. }
  582. xfree(s);
  583. }
  584. return r;
  585. }
  586. /*
  587. * Process the data that server sent due to IMAP FETCH BODY[] client request,
  588. * ie. FETCH BODY[HEADER], FETCH BODY[TEXT], FETCH BODY[HEADER.FIELDS
  589. * (<fields>)], FETCH BODY[<part>].
  590. */
  591. int
  592. response_fetchbody(session *ssn, int tag, char **body, size_t *len)
  593. {
  594. int r, match;
  595. unsigned int offset;
  596. ssize_t n;
  597. regexp *re;
  598. if (tag == -1)
  599. return -1;
  600. buffer_reset(&ibuf);
  601. match = -1;
  602. offset = 0;
  603. re = &responses[DATA_RESPONSE_FETCH_BODY];
  604. do {
  605. buffer_check(&ibuf, ibuf.len + INPUT_BUF);
  606. if ((n = receive_response(ssn, ibuf.data + ibuf.len, 0, 1)) == -1)
  607. return -1;
  608. ibuf.len += n;
  609. if (match != 0) {
  610. match = regexec(re->preg, ibuf.data, re->nmatch,
  611. re->pmatch, 0);
  612. if (match == 0 && re->pmatch[2].rm_so != -1 &&
  613. re->pmatch[2].rm_eo != -1) {
  614. *len = strtoul(ibuf.data + re->pmatch[2].rm_so,
  615. NULL, 10);
  616. offset = re->pmatch[0].rm_eo + *len;
  617. }
  618. }
  619. if (offset != 0 && ibuf.len >= offset) {
  620. if (check_bye(ibuf.data + offset))
  621. return -1;
  622. }
  623. } while (ibuf.len < offset || (r = check_tag(ibuf.data + offset, ssn,
  624. tag)) == STATUS_RESPONSE_NONE);
  625. if (match == 0) {
  626. if (re->pmatch[2].rm_so != -1 &&
  627. re->pmatch[2].rm_eo != -1) {
  628. *body = ibuf.data + re->pmatch[0].rm_eo;
  629. } else {
  630. *body = ibuf.data + re->pmatch[3].rm_so;
  631. *len = re->pmatch[3].rm_eo - re->pmatch[3].rm_so;
  632. }
  633. }
  634. return r;
  635. }
  636. /*
  637. * Process the data that server sent due to IMAP IDLE client request.
  638. */
  639. int
  640. response_idle(session *ssn, int tag)
  641. {
  642. regexp *re;
  643. re = &responses[DATA_RESPONSE_IDLE];
  644. do {
  645. buffer_reset(&ibuf);
  646. switch (receive_response(ssn, ibuf.data,
  647. get_option_number("keepalive") * 60, 0)) {
  648. case -1:
  649. return -1;
  650. break; /* NOTREACHED */
  651. case 0:
  652. return STATUS_RESPONSE_TIMEOUT;
  653. break; /* NOTREACHED */
  654. }
  655. verbose("S (%d): %s", ssn->socket, ibuf.data);
  656. if (check_bye(ibuf.data))
  657. return -1;
  658. } while (regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0));
  659. return STATUS_RESPONSE_UNTAGGED;
  660. }