pop3c.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. /*
  2. * Copyright 2012 by egnite GmbH
  3. *
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. *
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * 2. Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. * 3. Neither the name of the copyright holders nor the names of
  16. * contributors may be used to endorse or promote products derived
  17. * from this software without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  22. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  23. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  24. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  25. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  26. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  27. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  28. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  29. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  30. * SUCH DAMAGE.
  31. *
  32. * For additional information see http://www.ethernut.de/
  33. */
  34. /*
  35. * \file pro/pop3c.c
  36. * \brief Post office protocol client.
  37. *
  38. * \verbatim
  39. * $Id$
  40. * \endverbatim
  41. */
  42. #include <gorp/md5.h>
  43. #include <stdlib.h>
  44. #include <string.h>
  45. #include <pro/pop3c.h>
  46. #ifndef POP3_TIMEOUT
  47. #define POP3_TIMEOUT 600000
  48. #endif
  49. /*!
  50. * \brief Read a response line from the server.
  51. *
  52. * \return Pointer to a buffer containing the response. In case of a
  53. * broken connection or a line overflow, a NULL pointer is
  54. * returned.
  55. */
  56. const char *NutPop3ReceiveResponse(POP3CLIENTSESSION * si)
  57. {
  58. char *cp;
  59. if (fgets(si->pop3_buff, sizeof(si->pop3_buff), si->pop3_stream)) {
  60. cp = strchr(si->pop3_buff, '\r');
  61. if (cp == NULL) {
  62. cp = strchr(si->pop3_buff, '\n');
  63. }
  64. if (cp) {
  65. *cp = '\0';
  66. return si->pop3_buff;
  67. }
  68. /* Line overflow. */
  69. }
  70. return NULL;
  71. }
  72. static int CheckResponse(const char *response)
  73. {
  74. return (response && *response == '+') ? 0 : -1;
  75. }
  76. /*!
  77. * \brief Send command to the server and return the first response line.
  78. *
  79. * If a multi-line response is expected, the caller may use
  80. * NutPop3ReceiveResponse() to receive additional response lines.
  81. *
  82. * \param si Pointer to the \ref POP3CLIENTSESSION structure, obtained
  83. * from a previous call to NutPop3Connect().
  84. * \param fmt Format string containing conversion specifications like
  85. * printf.
  86. *
  87. * \return Pointer to a buffer containing the response. If an error
  88. * occurred, then a NULL pointer is returned.
  89. */
  90. const char *NutPop3SendCommand(POP3CLIENTSESSION * si, const char *fmt, ...)
  91. {
  92. va_list ap;
  93. va_start(ap, fmt);
  94. vfprintf(si->pop3_stream, (char *) fmt, ap);
  95. va_end(ap);
  96. fputs("\r\n", si->pop3_stream);
  97. fflush(si->pop3_stream);
  98. return NutPop3ReceiveResponse(si);
  99. }
  100. /*!
  101. * \brief Terminate an POP3 session.
  102. *
  103. * Gracefully closes the POP3 connection.
  104. *
  105. * \param si Pointer to the \ref POP3CLIENTSESSION structure, obtained
  106. * from a previous call to NutPop3Connect().
  107. */
  108. void NutPop3Disconnect(POP3CLIENTSESSION * si)
  109. {
  110. if (si->pop3_sock) {
  111. if (si->pop3_stream) {
  112. NutPop3SendCommand(si, "QUIT");
  113. fclose(si->pop3_stream);
  114. }
  115. NutTcpCloseSocket(si->pop3_sock);
  116. }
  117. free(si->pop3_stamp);
  118. free(si);
  119. }
  120. /*!
  121. * \brief Start an POP3 session.
  122. *
  123. * Applications may use the following basic sequence to retrieve an email:
  124. *
  125. * \code
  126. * #include <pro/pop3c.h>
  127. *
  128. * POP3CLIENTSESSION *pop3;
  129. * char *line;
  130. *
  131. * pop3 = NutPop3Connect(ip, 110);
  132. * if (pop3) {
  133. * NutPop3Login(pop3, "luser", "secret");
  134. * if (NutPop3RetrieveMsg(pop3, 1) == 0) {
  135. * do {
  136. * line = NutPop3ReceiveResponse(pop3);
  137. * } while (line && strcmp(line, "."));
  138. * }
  139. * NutPop3Disconnect(pop3);
  140. * }
  141. * \endcode
  142. *
  143. * \param ip IP address of the host to connect.
  144. * \param port Port number to connect. Typically port 110 is used by POP3.
  145. *
  146. * \return A pointer to a newly create \ref POP3CLIENTSESSION structure,
  147. * if the server is connected and ready to accept commands.
  148. * Otherwise a NULL pointer is returned.
  149. */
  150. POP3CLIENTSESSION *NutPop3Connect(uint32_t ip, uint16_t port)
  151. {
  152. POP3CLIENTSESSION *si;
  153. si = calloc(1, sizeof(*si));
  154. if (si) {
  155. si->pop3_sock = NutTcpCreateSocket();
  156. if (si->pop3_sock && NutTcpConnect(si->pop3_sock, ip, port) == 0) {
  157. uint32_t tmo = POP3_TIMEOUT;
  158. NutTcpSetSockOpt(si->pop3_sock, SO_RCVTIMEO, &tmo, sizeof(tmo));
  159. si->pop3_stream = _fdopen((int) ((intptr_t) si->pop3_sock), "r+b");
  160. if (si->pop3_stream) {
  161. const char *rsp = NutPop3ReceiveResponse(si);
  162. if (rsp) {
  163. char *cp = strchr(rsp, '<');
  164. if (cp) {
  165. si->pop3_stamp = strdup(cp);
  166. if (si->pop3_stamp) {
  167. cp = strchr(si->pop3_stamp, '>');
  168. if (cp) {
  169. *++cp = '\0';
  170. } else {
  171. free(si->pop3_stamp);
  172. si->pop3_stamp = NULL;
  173. }
  174. }
  175. }
  176. return si;
  177. }
  178. }
  179. }
  180. NutPop3Disconnect(si);
  181. free(si);
  182. }
  183. return NULL;
  184. }
  185. /*!
  186. * \brief Identify the POP3 client to the server.
  187. *
  188. * \param si Pointer to the \ref POP3CLIENTSESSION structure, obtained
  189. * from a previous call to NutPop3Connect().
  190. * \param user Login name.
  191. * \param pass Login password.
  192. *
  193. * \return 0 on success, -1 otherwise.
  194. */
  195. int NutPop3Login(POP3CLIENTSESSION * si, char *user, char *pass)
  196. {
  197. int rc = -1;
  198. if (si->pop3_stamp) {
  199. size_t len = strlen(si->pop3_stamp) + strlen(pass);
  200. char *buff = malloc(len + 1);
  201. MD5CONTEXT *ctx = calloc(1, sizeof(*ctx));
  202. uint8_t *digest = malloc(16);
  203. if (buff && ctx && digest) {
  204. strcpy(buff, si->pop3_stamp);
  205. strcat(buff, pass);
  206. NutMD5Init(ctx);
  207. NutMD5Update(ctx, (uint8_t *) buff, len);
  208. NutMD5Final(ctx, digest);
  209. rc = CheckResponse(NutPop3SendCommand(si, "APOP %s %s", user, digest));
  210. }
  211. free(buff);
  212. free(ctx);
  213. free(digest);
  214. } else {
  215. if (CheckResponse(NutPop3SendCommand(si, "USER %s", user)) == 0) {
  216. rc = CheckResponse(NutPop3SendCommand(si, "PASS %s", pass));
  217. }
  218. }
  219. return rc;
  220. }
  221. /*!
  222. * \brief Start reading a message.
  223. *
  224. * When this function returns 0, then the caller should use
  225. * NutPop3ReceiveResponse() to retrieve the contents of the message line
  226. * by line until a line containing a single dot is returned. If the
  227. * requested message is not available, then -1 is returned.
  228. *
  229. * \param si Pointer to the \ref POP3CLIENTSESSION structure, obtained
  230. * from a previous call to NutPop3Connect().
  231. * \param msg Index of the message to retrieve, starting at 1.
  232. *
  233. * \return 0 on success, the caller may use NutPop3ReceiveResponse()
  234. * to retrieve the contents of the message line by line until
  235. * a line with a single dot is returned. If an error occurs or
  236. * if the requested message is not available, then -1 is
  237. * returned.
  238. *
  239. * \return 0 on success, -1 otherwise.
  240. */
  241. int NutPop3RetrieveMsg(POP3CLIENTSESSION * si, int msg)
  242. {
  243. return CheckResponse(NutPop3SendCommand(si, "RETR %d", msg));
  244. }
  245. /*!
  246. * \brief Delete a message.
  247. *
  248. * \param si Pointer to the \ref POP3CLIENTSESSION structure, obtained
  249. * from a previous call to NutPop3Connect().
  250. * \param msg Index of the message to remove, starting at 1.
  251. *
  252. * \return 0 on success, -1 otherwise.
  253. */
  254. int NutPop3DeleteMsg(POP3CLIENTSESSION * si, int msg)
  255. {
  256. return CheckResponse(NutPop3SendCommand(si, "DELE %d", msg));
  257. }