Delta Chat Core C-Library
mrsmtp.c
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include <libetpan/libetpan.h>
24 #include "mrmailbox_internal.h"
25 #include "mrsmtp.h"
26 
27 
28 #ifndef DEBUG_SMTP
29 #define DEBUG_SMTP 0
30 #endif
31 
32 
33 #define LOCK_SMTP pthread_mutex_lock(&ths->m_mutex); smtp_locked = 1;
34 #define UNLOCK_SMTP if( smtp_locked ) { pthread_mutex_unlock(&ths->m_mutex); smtp_locked = 0; }
35 
36 
37 /*******************************************************************************
38  * Main interface
39  ******************************************************************************/
40 
41 
42 mrsmtp_t* mrsmtp_new(mrmailbox_t* mailbox)
43 {
44  mrsmtp_t* ths;
45  if( (ths=calloc(1, sizeof(mrsmtp_t)))==NULL ) {
46  exit(29);
47  }
48 
49  ths->m_log_connect_errors = 1;
50 
51  ths->m_mailbox = mailbox; /* should be used for logging only */
52  pthread_mutex_init(&ths->m_mutex, NULL);
53  return ths;
54 }
55 
56 
57 void mrsmtp_unref(mrsmtp_t* ths)
58 {
59  if( ths == NULL ) {
60  return;
61  }
62  mrsmtp_disconnect(ths);
63  pthread_mutex_destroy(&ths->m_mutex);
64  free(ths->m_from);
65  free(ths);
66 }
67 
68 
69 /*******************************************************************************
70  * Connect/Disconnect
71  ******************************************************************************/
72 
73 
74 int mrsmtp_is_connected(const mrsmtp_t* ths)
75 {
76  return (ths && ths->m_hEtpan)? 1 : 0;
77 }
78 
79 
80 static void body_progress(size_t current, size_t maximum, void* user_data)
81 {
82  #if DEBUG_SMTP
83  printf("body_progress called with current=%i, maximum=%i.", (int)current, (int)maximum);
84  #endif
85 }
86 
87 
88 #if DEBUG_SMTP
89 static void logger(mailsmtp* smtp, int log_type, const char* buffer__, size_t size, void* user_data)
90 {
91  char* buffer = malloc(size+1);
92  memcpy(buffer, buffer__, size);
93  buffer[size] = 0;
94  printf("SMPT: %i: %s", log_type, buffer__);
95 }
96 #endif
97 
98 
99 int mrsmtp_connect(mrsmtp_t* ths, const mrloginparam_t* lp)
100 {
101  int success = 0, smtp_locked = 0;
102  int r, try_esmtp;
103 
104  if( ths == NULL || lp == NULL ) {
105  return 0;
106  }
107 
108  LOCK_SMTP
109 
110  if( ths->m_mailbox->m_cb(ths->m_mailbox, MR_EVENT_IS_ONLINE, 0, 0)!=1 ) {
111  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, MR_ERR_NONETWORK, NULL);
112  goto cleanup;
113  }
114 
115  if( ths->m_hEtpan ) {
116  mrmailbox_log_warning(ths->m_mailbox, 0, "Already connected to SMTP server.");
117  success = 1; /* otherwise, the handle would get deleted */
118  goto cleanup;
119  }
120 
121  if( lp->m_addr == NULL || lp->m_send_server == NULL || lp->m_send_port == 0 ) {
122  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "Cannot connect to SMTP; bad parameters.");
123  goto cleanup;
124  }
125 
126  free(ths->m_from);
127  ths->m_from = safe_strdup(lp->m_addr);
128 
129  ths->m_hEtpan = mailsmtp_new(0, NULL);
130  if( ths->m_hEtpan == NULL ) {
131  mrmailbox_log_error(ths->m_mailbox, 0, "SMTP-object creationed failed.");
132  goto cleanup;
133  }
134  mailsmtp_set_progress_callback(ths->m_hEtpan, body_progress, ths);
135  #if DEBUG_SMTP
136  mailsmtp_set_logger(ths->m_hEtpan, logger, ths);
137  #endif
138 
139  /* connect to SMTP server */
140  if( lp->m_server_flags&(MR_SMTP_SOCKET_STARTTLS|MR_SMTP_SOCKET_PLAIN) )
141  {
142  mrmailbox_log_info(ths->m_mailbox, 0, "Connecting to SMTP-server \"%s:%i\"...", lp->m_send_server, (int)lp->m_send_port);
143  if( (r=mailsmtp_socket_connect(ths->m_hEtpan, lp->m_send_server, lp->m_send_port)) != MAILSMTP_NO_ERROR ) {
144  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "SMTP-STARTTLS connection to %s:%i failed (%s)", lp->m_send_server, (int)lp->m_send_port, mailsmtp_strerror(r));
145  goto cleanup;
146  }
147  }
148  else
149  {
150  mrmailbox_log_info(ths->m_mailbox, 0, "Connecting to SMTP-server \"%s:%i\" via SSL...", lp->m_send_server, (int)lp->m_send_port);
151  if( (r=mailsmtp_ssl_connect(ths->m_hEtpan, lp->m_send_server, lp->m_send_port)) != MAILSMTP_NO_ERROR ) {
152  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "SMPT-SSL connection to %s:%i failed (%s)", lp->m_send_server, (int)lp->m_send_port, mailsmtp_strerror(r));
153  goto cleanup;
154  }
155  }
156 
157  try_esmtp = 1;
158  ths->m_esmtp = 0;
159  if( try_esmtp && (r=mailesmtp_ehlo(ths->m_hEtpan))==MAILSMTP_NO_ERROR ) {
160  ths->m_esmtp = 1;
161  }
162  else if( !try_esmtp || r==MAILSMTP_ERROR_NOT_IMPLEMENTED ) {
163  r = mailsmtp_helo(ths->m_hEtpan);
164  }
165 
166  if( r != MAILSMTP_NO_ERROR ) {
167  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "SMTP-helo failed (%s)", mailsmtp_strerror(r));
168  goto cleanup;
169  }
170 
171  if( lp->m_server_flags&MR_SMTP_SOCKET_STARTTLS )
172  {
173  mrmailbox_log_info(ths->m_mailbox, 0, "Switching to SMTP-STARTTLS.");
174  if( (r=mailsmtp_socket_starttls(ths->m_hEtpan)) != MAILSMTP_NO_ERROR ) {
175  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "SMTP-STARTTLS failed (%s)", mailsmtp_strerror(r));
176  goto cleanup;
177  }
178 
179  ths->m_esmtp = 0;
180  if( try_esmtp && (r=mailesmtp_ehlo(ths->m_hEtpan))==MAILSMTP_NO_ERROR ) {
181  ths->m_esmtp = 1;
182  }
183  else if( !try_esmtp || r==MAILSMTP_ERROR_NOT_IMPLEMENTED ) {
184  r = mailsmtp_helo(ths->m_hEtpan);
185  }
186 
187  if (r != MAILSMTP_NO_ERROR) {
188  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "SMTP-helo failed (%s)", mailsmtp_strerror(r));
189  goto cleanup;
190  }
191  }
192  mrmailbox_log_info(ths->m_mailbox, 0, "Connection to SMTP-server ok.");
193 
194  if( lp->m_send_user )
195  {
196  mrmailbox_log_info(ths->m_mailbox, 0, "Login to SMTP-server as \"%s\"...", lp->m_send_user);
197 
198  if((r=mailsmtp_auth(ths->m_hEtpan, lp->m_send_user, lp->m_send_pw))!=MAILSMTP_NO_ERROR ) {
199  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "SMTP-login failed for user %s (%s)", lp->m_send_user, mailsmtp_strerror(r));
200  goto cleanup;
201  }
202 
203  mrmailbox_log_info(ths->m_mailbox, 0, "SMTP-Login ok.");
204  }
205 
206  success = 1;
207 
208 cleanup:
209  if( !success ) {
210  if( ths->m_hEtpan ) {
211  mailsmtp_free(ths->m_hEtpan);
212  ths->m_hEtpan = NULL;
213  }
214  }
215 
216  UNLOCK_SMTP
217 
218  return success;
219 }
220 
221 
222 void mrsmtp_disconnect(mrsmtp_t* ths)
223 {
224  int smtp_locked = 0;
225 
226  if( ths == NULL ) {
227  return;
228  }
229 
230  LOCK_SMTP
231 
232  if( ths->m_hEtpan ) {
233  //mailsmtp_quit(ths->m_hEtpan); -- ?
234  mailsmtp_free(ths->m_hEtpan);
235  ths->m_hEtpan = NULL;
236  }
237 
238  UNLOCK_SMTP
239 }
240 
241 
242 /*******************************************************************************
243  * Send a message
244  ******************************************************************************/
245 
246 
247 int mrsmtp_send_msg(mrsmtp_t* ths, const clist* recipients, const char* data_not_terminated, size_t data_bytes)
248 {
249  int success = 0, r, smtp_locked = 0;
250  clistiter* iter;
251 
252  if( ths == NULL ) {
253  return 0;
254  }
255 
256  if( recipients == NULL || clist_count(recipients)==0 || data_not_terminated == NULL || data_bytes == 0 ) {
257  return 1; /* "null message" send */
258  }
259 
260  LOCK_SMTP
261 
262  if( ths->m_hEtpan==NULL ) {
263  goto cleanup;
264  }
265 
266  /* set source */
267  if( (r=(ths->m_esmtp?
268  mailesmtp_mail(ths->m_hEtpan, ths->m_from, 1, "etPanSMTPTest") :
269  mailsmtp_mail(ths->m_hEtpan, ths->m_from))) != MAILSMTP_NO_ERROR )
270  {
271  // this error is very usual - we've simply lost the server connection and reconnect as soon as possible.
272  // so, we do not log the first time this happens
273  mrmailbox_log_error_if(&ths->m_log_usual_error, ths->m_mailbox, 0, "mailsmtp_mail: %s, %s (%i)", ths->m_from, mailsmtp_strerror(r), (int)r);
274  ths->m_log_usual_error = 1;
275  goto cleanup;
276  }
277 
278  ths->m_log_usual_error = 0;
279 
280  /* set recipients */
281  for( iter=clist_begin(recipients); iter!=NULL; iter=clist_next(iter)) {
282  const char* rcpt = clist_content(iter);
283  if( (r = (ths->m_esmtp?
284  mailesmtp_rcpt(ths->m_hEtpan, rcpt, MAILSMTP_DSN_NOTIFY_FAILURE|MAILSMTP_DSN_NOTIFY_DELAY, NULL) :
285  mailsmtp_rcpt(ths->m_hEtpan, rcpt))) != MAILSMTP_NO_ERROR) {
286  mrmailbox_log_error_if(&ths->m_log_connect_errors, ths->m_mailbox, 0, "mailsmtp_rcpt: %s: %s", rcpt, mailsmtp_strerror(r));
287  goto cleanup;
288  }
289  }
290 
291  /* message */
292  if ((r = mailsmtp_data(ths->m_hEtpan)) != MAILSMTP_NO_ERROR) {
293  fprintf(stderr, "mailsmtp_data: %s\n", mailsmtp_strerror(r));
294  goto cleanup;
295  }
296 
297  if ((r = mailsmtp_data_message(ths->m_hEtpan, data_not_terminated, data_bytes)) != MAILSMTP_NO_ERROR) {
298  fprintf(stderr, "mailsmtp_data_message: %s\n", mailsmtp_strerror(r));
299  goto cleanup;
300  }
301 
302  success = 1;
303 
304 cleanup:
305 
306  UNLOCK_SMTP
307 
308  return success;
309 }
310 
An object representing a single mailbox.
Definition: mrmailbox.h:141