diff --git a/cmdline/cmdline.c b/cmdline/cmdline.c index 7f9c8cac..837168fa 100644 --- a/cmdline/cmdline.c +++ b/cmdline/cmdline.c @@ -287,7 +287,7 @@ static void log_msglist(dc_context_t* context, dc_array_t* msglist) case DC_STATE_OUT_PENDING: statestr = " o"; break; case DC_STATE_OUT_DELIVERED: statestr = " √"; break; case DC_STATE_OUT_MDN_RCVD: statestr = " √√"; break; - case DC_STATE_OUT_ERROR: statestr = " ERR"; break; + case DC_STATE_OUT_FAILED: statestr = " !!"; break; } char* temp2 = dc_timestamp_to_str(dc_msg_get_timestamp(msg)); @@ -701,7 +701,7 @@ char* dc_cmdline(dc_context_t* context, const char* cmdline) case DC_STATE_OUT_PENDING: statestr = " o"; break; case DC_STATE_OUT_DELIVERED: statestr = " √"; break; case DC_STATE_OUT_MDN_RCVD: statestr = " √√"; break; - case DC_STATE_OUT_ERROR: statestr = " ERR"; break; + case DC_STATE_OUT_FAILED: statestr = " !!"; break; } char* timestr = dc_timestamp_to_str(dc_lot_get_timestamp(lot)); diff --git a/cmdline/stress.c b/cmdline/stress.c index 75140f4f..054a5b26 100644 --- a/cmdline/stress.c +++ b/cmdline/stress.c @@ -363,7 +363,7 @@ void stress_functions(dc_context_t* context) assert( strcmp("noticed=" DC_STRINGIFY(DC_STATE_IN_NOTICED), "noticed=13")==0 ); assert( strcmp("seen=" DC_STRINGIFY(DC_STATE_IN_SEEN), "seen=16")==0 ); assert( strcmp("pending=" DC_STRINGIFY(DC_STATE_OUT_PENDING), "pending=20")==0 ); - assert( strcmp("error=" DC_STRINGIFY(DC_STATE_OUT_ERROR), "error=24")==0 ); + assert( strcmp("failed=" DC_STRINGIFY(DC_STATE_OUT_FAILED), "failed=24")==0 ); assert( strcmp("delivered=" DC_STRINGIFY(DC_STATE_OUT_DELIVERED), "delivered=26")==0 ); assert( strcmp("mdn_rcvd=" DC_STRINGIFY(DC_STATE_OUT_MDN_RCVD), "mdn_rcvd=28")==0 ); diff --git a/src/dc_job.c b/src/dc_job.c index efa41316..079ae57d 100644 --- a/src/dc_job.c +++ b/src/dc_job.c @@ -61,7 +61,7 @@ static int connect_to_imap(dc_context_t* context, dc_job_t* job /*may be NULL if dc_loginparam_read(param, context->sql, "configured_" /*the trailing underscore is correct*/); if (!dc_imap_connect(context->imap, param)) { - dc_job_try_again_later(job, DC_STANDARD_DELAY); + dc_job_try_again_later(job, DC_STANDARD_DELAY, NULL); goto cleanup; } @@ -84,7 +84,7 @@ static void dc_job_do_DC_JOB_SEND_MSG_TO_IMAP(dc_context_t* context, dc_job_t* j if (!dc_imap_is_connected(context->imap)) { connect_to_imap(context, NULL); if (!dc_imap_is_connected(context->imap)) { - dc_job_try_again_later(job, DC_STANDARD_DELAY); + dc_job_try_again_later(job, DC_STANDARD_DELAY, NULL); goto cleanup; } } @@ -100,7 +100,7 @@ static void dc_job_do_DC_JOB_SEND_MSG_TO_IMAP(dc_context_t* context, dc_job_t* j } if (!dc_imap_append_msg(context->imap, mimefactory.msg->timestamp, mimefactory.out->str, mimefactory.out->len, &server_folder, &server_uid)) { - dc_job_try_again_later(job, DC_AT_ONCE); + dc_job_try_again_later(job, DC_AT_ONCE, NULL); goto cleanup; } else { @@ -135,14 +135,14 @@ static void dc_job_do_DC_JOB_DELETE_MSG_ON_IMAP(dc_context_t* context, dc_job_t* if (!dc_imap_is_connected(context->imap)) { connect_to_imap(context, NULL); if (!dc_imap_is_connected(context->imap)) { - dc_job_try_again_later(job, DC_STANDARD_DELAY); + dc_job_try_again_later(job, DC_STANDARD_DELAY, NULL); goto cleanup; } } if (!dc_imap_delete_msg(context->imap, msg->rfc724_mid, msg->server_folder, msg->server_uid)) { - dc_job_try_again_later(job, DC_AT_ONCE); + dc_job_try_again_later(job, DC_AT_ONCE, NULL); goto cleanup; } } @@ -221,7 +221,7 @@ static void dc_job_do_DC_JOB_MARKSEEN_MSG_ON_IMAP(dc_context_t* context, dc_job_ if (!dc_imap_is_connected(context->imap)) { connect_to_imap(context, NULL); if (!dc_imap_is_connected(context->imap)) { - dc_job_try_again_later(job, DC_STANDARD_DELAY); + dc_job_try_again_later(job, DC_STANDARD_DELAY, NULL); goto cleanup; } } @@ -258,7 +258,7 @@ static void dc_job_do_DC_JOB_MARKSEEN_MSG_ON_IMAP(dc_context_t* context, dc_job_ } else { - dc_job_try_again_later(job, DC_AT_ONCE); + dc_job_try_again_later(job, DC_AT_ONCE, NULL); } cleanup: @@ -278,13 +278,13 @@ static void dc_job_do_DC_JOB_MARKSEEN_MDN_ON_IMAP(dc_context_t* context, dc_job_ if (!dc_imap_is_connected(context->imap)) { connect_to_imap(context, NULL); if (!dc_imap_is_connected(context->imap)) { - dc_job_try_again_later(job, DC_STANDARD_DELAY); + dc_job_try_again_later(job, DC_STANDARD_DELAY, NULL); goto cleanup; } } if (dc_imap_markseen_msg(context->imap, server_folder, server_uid, DC_MS_ALSO_MOVE, &new_server_folder, &new_server_uid, &out_ms_flags)==0) { - dc_job_try_again_later(job, DC_AT_ONCE); + dc_job_try_again_later(job, DC_AT_ONCE, NULL); } cleanup: @@ -298,17 +298,6 @@ cleanup: ******************************************************************************/ -static void mark_as_error(dc_context_t* context, dc_msg_t* msg) -{ - if (context==NULL || msg==NULL) { - return; - } - - dc_update_msg_state(context, msg->id, DC_STATE_OUT_ERROR); - context->cb(context, DC_EVENT_MSGS_CHANGED, msg->chat_id, 0); -} - - static void dc_job_do_DC_JOB_SEND_MSG_TO_SMTP(dc_context_t* context, dc_job_t* job) { dc_mimefactory_t mimefactory; @@ -321,7 +310,7 @@ static void dc_job_do_DC_JOB_SEND_MSG_TO_SMTP(dc_context_t* context, dc_job_t* j int connected = dc_smtp_connect(context->smtp, loginparam); dc_loginparam_unref(loginparam); if (!connected) { - dc_job_try_again_later(job, DC_STANDARD_DELAY); + dc_job_try_again_later(job, DC_STANDARD_DELAY, NULL); goto cleanup; } } @@ -336,28 +325,26 @@ static void dc_job_do_DC_JOB_SEND_MSG_TO_SMTP(dc_context_t* context, dc_job_t* j /* check if the message is ready (normally, only video files may be delayed this way) */ if (mimefactory.increation) { dc_log_info(context, 0, "File is in creation, retrying later."); - dc_job_try_again_later(job, DC_INCREATION_POLL); + dc_job_try_again_later(job, DC_INCREATION_POLL, NULL); goto cleanup; } /* send message - it's okay if there are no recipients, this is a group with only OURSELF; we only upload to IMAP in this case */ if (clist_count(mimefactory.recipients_addr) > 0) { if (!dc_mimefactory_render(&mimefactory)) { - mark_as_error(context, mimefactory.msg); - dc_log_error(context, 0, "Empty message."); /* should not happen */ + dc_set_msg_failed(context, job->foreign_id, "Empty message."); goto cleanup; /* no redo, no IMAP - there won't be more recipients next time. */ } /* have we guaranteed encryption but cannot fulfill it for any reason? Do not send the message then.*/ if (dc_param_get_int(mimefactory.msg->param, DC_PARAM_GUARANTEE_E2EE, 0) && !mimefactory.out_encrypted) { - mark_as_error(context, mimefactory.msg); - dc_log_error(context, 0, "End-to-end-encryption unavailable unexpectedly."); + dc_set_msg_failed(context, job->foreign_id, "End-to-end-encryption unavailable unexpectedly."); goto cleanup; /* unrecoverable */ } if (!dc_smtp_send_msg(context->smtp, mimefactory.recipients_addr, mimefactory.out->str, mimefactory.out->len)) { dc_smtp_disconnect(context->smtp); - dc_job_try_again_later(job, DC_AT_ONCE); /* DC_AT_ONCE is only the _initial_ delay, if the second try failes, the delay gets larger */ + dc_job_try_again_later(job, DC_AT_ONCE, context->smtp->error); goto cleanup; } } @@ -419,7 +406,7 @@ static void dc_job_do_DC_JOB_SEND_MDN(dc_context_t* context, dc_job_t* job) int connected = dc_smtp_connect(context->smtp, loginparam); dc_loginparam_unref(loginparam); if (!connected) { - dc_job_try_again_later(job, DC_STANDARD_DELAY); + dc_job_try_again_later(job, DC_STANDARD_DELAY, NULL); goto cleanup; } } @@ -433,7 +420,7 @@ static void dc_job_do_DC_JOB_SEND_MDN(dc_context_t* context, dc_job_t* job) if (!dc_smtp_send_msg(context->smtp, mimefactory.recipients_addr, mimefactory.out->str, mimefactory.out->len)) { dc_smtp_disconnect(context->smtp); - dc_job_try_again_later(job, DC_AT_ONCE); /* DC_AT_ONCE is only the _initial_ delay, if the second try failes, the delay gets larger */ + dc_job_try_again_later(job, DC_AT_ONCE, NULL); goto cleanup; } @@ -508,13 +495,37 @@ void dc_job_add(dc_context_t* context, int action, int foreign_id, const char* p } -void dc_job_try_again_later(dc_job_t* job, int try_again) +static void dc_job_update(dc_context_t* context, const dc_job_t* job) +{ + sqlite3_stmt* update_stmt = dc_sqlite3_prepare(context->sql, + "UPDATE jobs SET desired_timestamp=0, param=? WHERE id=?;"); + sqlite3_bind_text (update_stmt, 1, job->param->packed, -1, SQLITE_STATIC); + sqlite3_bind_int (update_stmt, 2, job->job_id); + sqlite3_step(update_stmt); + sqlite3_finalize(update_stmt); +} + + +static void dc_job_delete(dc_context_t* context, const dc_job_t* job) +{ + sqlite3_stmt* delete_stmt = dc_sqlite3_prepare(context->sql, + "DELETE FROM jobs WHERE id=?;"); + sqlite3_bind_int(delete_stmt, 1, job->job_id); + sqlite3_step(delete_stmt); + sqlite3_finalize(delete_stmt); +} + + +void dc_job_try_again_later(dc_job_t* job, int try_again, const char* pending_error) { if (job==NULL) { return; } job->try_again = try_again; + + free(job->pending_error); + job->pending_error = dc_strdup_keep_null(pending_error); } @@ -553,9 +564,9 @@ static void dc_job_perform(dc_context_t* context, int thread) sqlite3_bind_int64(select_stmt, 2, time(NULL)); while (sqlite3_step(select_stmt)==SQLITE_ROW) { - job.job_id = sqlite3_column_int (select_stmt, 0); - job.action = sqlite3_column_int (select_stmt, 1); - job.foreign_id = sqlite3_column_int (select_stmt, 2); + job.job_id = sqlite3_column_int (select_stmt, 0); + job.action = sqlite3_column_int (select_stmt, 1); + job.foreign_id = sqlite3_column_int (select_stmt, 2); dc_param_set_packed(job.param, (char*)sqlite3_column_text(select_stmt, 3)); dc_log_info(context, 0, "%s-job #%i, action %i started...", THREAD_STR, (int)job.job_id, (int)job.action); @@ -602,40 +613,41 @@ static void dc_job_perform(dc_context_t* context, int thread) } else if (job.try_again==DC_AT_ONCE || job.try_again==DC_STANDARD_DELAY) { - int tries = dc_param_get_int(job.param, DC_PARAM_TIMES, 0) + 1; - dc_param_set_int(job.param, DC_PARAM_TIMES, tries); + // Define the number of job-retries, each retry may result in 2 tries (for fast network-failure-recover). + // The first job-retries are done asap, the last retry is delayed about a minute. + // Network errors do not count as failed tries. + #define JOB_RETRIES 3 - sqlite3_stmt* update_stmt = dc_sqlite3_prepare(context->sql, - "UPDATE jobs SET desired_timestamp=0, param=? WHERE id=?;"); - sqlite3_bind_text (update_stmt, 1, job.param->packed, -1, SQLITE_STATIC); - sqlite3_bind_int (update_stmt, 2, job.job_id); - sqlite3_step(update_stmt); - sqlite3_finalize(update_stmt); - dc_log_info(context, 0, "%s-job #%i not succeeded, trying again asap.", THREAD_STR, (int)job.job_id); + int is_online = dc_is_online(context)? 1 : 0; + int tries_while_online = dc_param_get_int(job.param, DC_PARAM_TIMES, 0) + is_online; - // if the job did not succeeded AND this is a smtp-job AND we're online, try over after a mini-delay of one second. - // if we're not online, the ui calls interrupt idle as soon as we're online again. - // if nothing of this happens, after DC_SMTP_IDLE_SEC (60) we try again. - if (thread==DC_SMTP_THREAD - && dc_is_online(context)) - { - pthread_mutex_lock(&context->smtpidle_condmutex); - context->perform_smtp_jobs_needed = DC_JOBS_NEEDED_AVOID_DOS; - pthread_mutex_unlock(&context->smtpidle_condmutex); + if( tries_while_online < JOB_RETRIES ) { + dc_param_set_int(job.param, DC_PARAM_TIMES, tries_while_online); + dc_job_update(context, &job); + dc_log_info(context, 0, "%s-job #%i not succeeded on try #%i.", THREAD_STR, (int)job.job_id, tries_while_online); + + if (thread==DC_SMTP_THREAD && is_online && tries_while_online<(JOB_RETRIES-1)) { + pthread_mutex_lock(&context->smtpidle_condmutex); + context->perform_smtp_jobs_needed = DC_JOBS_NEEDED_AVOID_DOS; + pthread_mutex_unlock(&context->smtpidle_condmutex); + } + } + else { + if (job.action==DC_JOB_SEND_MSG_TO_SMTP) { // in all other cases, the messages is already sent + dc_set_msg_failed(context, job.foreign_id, job.pending_error); + } + dc_job_delete(context, &job); } } else { - sqlite3_stmt* delete_stmt = dc_sqlite3_prepare(context->sql, - "DELETE FROM jobs WHERE id=?;"); - sqlite3_bind_int(delete_stmt, 1, job.job_id); - sqlite3_step(delete_stmt); - sqlite3_finalize(delete_stmt); + dc_job_delete(context, &job); } } cleanup: dc_param_unref(job.param); + free(job.pending_error); sqlite3_finalize(select_stmt); } diff --git a/src/dc_job.h b/src/dc_job.h index 344becf8..6183502f 100644 --- a/src/dc_job.h +++ b/src/dc_job.h @@ -69,7 +69,9 @@ typedef struct dc_job_t int action; uint32_t foreign_id; dc_param_t* param; + int try_again; + char* pending_error; // discarded if the retry succeeds } dc_job_t; @@ -80,7 +82,7 @@ void dc_job_kill_actions (dc_context_t*, int action1, int action2); #define DC_AT_ONCE -1 #define DC_INCREATION_POLL 2 // this value does not increase the number of tries #define DC_STANDARD_DELAY 3 -void dc_job_try_again_later (dc_job_t*, int try_again); +void dc_job_try_again_later (dc_job_t*, int try_again, const char* error); // the other dc_job_do_DC_JOB_*() functions are declared static in the c-file diff --git a/src/dc_msg.c b/src/dc_msg.c index b3b8b4df..e55b3f1b 100644 --- a/src/dc_msg.c +++ b/src/dc_msg.c @@ -193,8 +193,8 @@ int dc_msg_get_type(const dc_msg_t* msg) * Outgoing message states: * - DC_STATE_OUT_PENDING (20) - The user has send the "send" button but the * message is not yet sent and is pending in some way. Maybe we're offline (no checkmark). - * - DC_STATE_OUT_ERROR (24) - _Unrecoverable_ error (_recoverable_ errors result in pending messages) - * - DC_STATE_OUT_DELIVERED (26) - Outgoing message successfully delivered to server (one checkmark). Note, that already delivered messages may get into the state DC_STATE_OUT_ERROR if we get such a hint from the server. + * - DC_STATE_OUT_FAILED (24) - _Unrecoverable_ error (_recoverable_ errors result in pending messages), you'll receive the event #DC_EVENT_MSG_FAILED. + * - DC_STATE_OUT_DELIVERED (26) - Outgoing message successfully delivered to server (one checkmark). Note, that already delivered messages may get into the state DC_STATE_OUT_FAILED if we get such a hint from the server. * If a sent message changes to this state, you'll receive the event #DC_EVENT_MSG_DELIVERED. * - DC_STATE_OUT_MDN_RCVD (28) - Outgoing message read by the recipient (two checkmarks; this requires goodwill on the receiver's side) * If a sent message changes to this state, you'll receive the event #DC_EVENT_MSG_READ. @@ -1158,6 +1158,36 @@ void dc_update_msg_state(dc_context_t* context, uint32_t msg_id, int state) } +void dc_set_msg_failed(dc_context_t* context, uint32_t msg_id, const char* error) +{ + dc_msg_t* msg = dc_msg_new(); + sqlite3_stmt* stmt = NULL; + + if (!dc_msg_load_from_db(msg, context, msg_id)) { + goto cleanup; + } + + msg->state = DC_STATE_OUT_FAILED; + if (error) { + dc_param_set(msg->param, DC_PARAM_ERROR, error); + dc_log_error(context, 0, "%s", error); + } + + stmt = dc_sqlite3_prepare(context->sql, + "UPDATE msgs SET state=?, param=? WHERE id=?;"); + sqlite3_bind_int (stmt, 1, msg->state); + sqlite3_bind_text(stmt, 2, msg->param->packed, -1, SQLITE_STATIC); + sqlite3_bind_int (stmt, 3, msg_id); + sqlite3_step(stmt); + + context->cb(context, DC_EVENT_MSG_FAILED, msg->chat_id, msg_id); + +cleanup: + sqlite3_finalize(stmt); + dc_msg_unref(msg); +} + + size_t dc_get_real_msg_cnt(dc_context_t* context) { sqlite3_stmt* stmt = NULL; @@ -1406,7 +1436,7 @@ char* dc_get_msg_info(dc_context_t* context, uint32_t msg_id) case DC_STATE_IN_NOTICED: p = dc_strdup("Noticed"); break; case DC_STATE_IN_SEEN: p = dc_strdup("Seen"); break; case DC_STATE_OUT_DELIVERED: p = dc_strdup("Delivered"); break; - case DC_STATE_OUT_ERROR: p = dc_strdup("Error"); break; + case DC_STATE_OUT_FAILED: p = dc_strdup("Failed"); break; case DC_STATE_OUT_MDN_RCVD: p = dc_strdup("Read"); break; case DC_STATE_OUT_PENDING: p = dc_strdup("Pending"); break; default: p = dc_mprintf("%i", msg->state); break; @@ -1431,6 +1461,12 @@ char* dc_get_msg_info(dc_context_t* context, uint32_t msg_id) } dc_strbuilder_cat(&ret, "\n"); + + if ((p=dc_param_get(msg->param, DC_PARAM_ERROR, NULL))!=NULL) { + dc_strbuilder_catf(&ret, "Error: %s\n", p); + free(p); + } + /* add sender (only for info messages as the avatar may not be shown for them) */ if (dc_msg_is_info(msg)) { dc_strbuilder_cat(&ret, "Sender: "); @@ -1439,9 +1475,9 @@ char* dc_get_msg_info(dc_context_t* context, uint32_t msg_id) } /* add file info */ - char* file = dc_param_get(msg->param, DC_PARAM_FILE, NULL); - if (file) { - p = dc_mprintf("\nFile: %s, %i bytes\n", file, (int)dc_get_filebytes(file)); dc_strbuilder_cat(&ret, p); free(p); + if ((p=dc_param_get(msg->param, DC_PARAM_FILE, NULL))!=NULL) { + dc_strbuilder_catf(&ret, "\nFile: %s, %i bytes\n", p, (int)dc_get_filebytes(p)); + free(p); } if (msg->type!=DC_MSG_TEXT) { @@ -1459,7 +1495,8 @@ char* dc_get_msg_info(dc_context_t* context, uint32_t msg_id) free(p); } - int w = dc_param_get_int(msg->param, DC_PARAM_WIDTH, 0), h = dc_param_get_int(msg->param, DC_PARAM_HEIGHT, 0); + int w = dc_param_get_int(msg->param, DC_PARAM_WIDTH, 0); + int h = dc_param_get_int(msg->param, DC_PARAM_HEIGHT, 0); if (w!=0 || h!=0) { p = dc_mprintf("Dimension: %i x %i\n", w, h); dc_strbuilder_cat(&ret, p); free(p); } diff --git a/src/dc_msg.h b/src/dc_msg.h index dc1e2fb0..6789eacd 100644 --- a/src/dc_msg.h +++ b/src/dc_msg.h @@ -110,6 +110,7 @@ The value is also used for CC:-summaries */ // Context functions to work with messages void dc_update_msg_chat_id (dc_context_t*, uint32_t msg_id, uint32_t chat_id); void dc_update_msg_state (dc_context_t*, uint32_t msg_id, int state); +void dc_set_msg_failed (dc_context_t*, uint32_t msg_id, const char* error); int dc_mdn_from_ext (dc_context_t*, uint32_t from_id, const char* rfc724_mid, time_t, uint32_t* ret_chat_id, uint32_t* ret_msg_id); /* returns 1 if an event should be send */ size_t dc_get_real_msg_cnt (dc_context_t*); /* the number of messages assigned to real chat (!=deaddrop, !=trash) */ size_t dc_get_deaddrop_msg_cnt (dc_context_t*); diff --git a/src/dc_param.h b/src/dc_param.h index 040c148f..fd127252 100644 --- a/src/dc_param.h +++ b/src/dc_param.h @@ -60,6 +60,7 @@ typedef struct dc_param_t #define DC_PARAM_CMD_ARG2 'F' /* for msgs */ #define DC_PARAM_CMD_ARG3 'G' /* for msgs */ #define DC_PARAM_CMD_ARG4 'H' /* for msgs */ +#define DC_PARAM_ERROR 'L' /* for msgs */ #define DC_PARAM_SERVER_FOLDER 'Z' /* for jobs */ #define DC_PARAM_SERVER_UID 'z' /* for jobs */ diff --git a/src/dc_smtp.c b/src/dc_smtp.c index 868b2228..0f26e4da 100644 --- a/src/dc_smtp.c +++ b/src/dc_smtp.c @@ -53,13 +53,18 @@ void dc_smtp_unref(dc_smtp_t* smtp) } dc_smtp_disconnect(smtp); free(smtp->from); + free(smtp->error); free(smtp); } -/******************************************************************************* - * Connect/Disconnect - ******************************************************************************/ +static void log_error(dc_smtp_t* smtp, const char* what_failed, int r) +{ + char* error_msg = dc_mprintf("%s: %s: %s", what_failed, mailsmtp_strerror(r), smtp->etpan->response); + dc_log_warning(smtp->context, 0, "%s", error_msg); + free(smtp->error); + smtp->error = error_msg; +} int dc_smtp_is_connected(const dc_smtp_t* smtp) @@ -256,11 +261,12 @@ int dc_smtp_send_msg(dc_smtp_t* smtp, const clist* recipients, const char* data_ clistiter* iter = NULL; if (smtp==NULL) { - return 0; + goto cleanup; } if (recipients==NULL || clist_count(recipients)==0 || data_not_terminated==NULL || data_bytes==0) { - return 1; // "null message" send + success = 1; + goto cleanup; // "null message" send } if (smtp->etpan==NULL) { @@ -290,19 +296,19 @@ int dc_smtp_send_msg(dc_smtp_t* smtp, const clist* recipients, const char* data_ if ((r = (smtp->esmtp? mailesmtp_rcpt(smtp->etpan, rcpt, MAILSMTP_DSN_NOTIFY_FAILURE|MAILSMTP_DSN_NOTIFY_DELAY, NULL) : mailsmtp_rcpt(smtp->etpan, rcpt))) != MAILSMTP_NO_ERROR) { - dc_log_error_if(&smtp->log_connect_errors, smtp->context, 0, "Cannot add recipient %s: %s - %s", rcpt, mailsmtp_strerror(r), smtp->etpan->response); + log_error(smtp, "SMTP failed to add recipient", r); goto cleanup; } } // message if ((r = mailsmtp_data(smtp->etpan)) != MAILSMTP_NO_ERROR) { - fprintf(stderr, "mailsmtp_data: %s\n", mailsmtp_strerror(r)); + log_error(smtp, "SMTP failed to set data", r); goto cleanup; } if ((r = mailsmtp_data_message(smtp->etpan, data_not_terminated, data_bytes)) != MAILSMTP_NO_ERROR) { - fprintf(stderr, "mailsmtp_data_message: %s\n", mailsmtp_strerror(r)); + log_error(smtp, "SMTP failed to send message", r); goto cleanup; } diff --git a/src/dc_smtp.h b/src/dc_smtp.h index 2f938a6b..bc9465f6 100644 --- a/src/dc_smtp.h +++ b/src/dc_smtp.h @@ -42,6 +42,8 @@ typedef struct dc_smtp_t int log_usual_error; dc_context_t* context; /* only for logging! */ + + char* error; } dc_smtp_t; dc_smtp_t* dc_smtp_new (dc_context_t*); diff --git a/src/deltachat.h b/src/deltachat.h index 04e1e7e0..5eef5239 100644 --- a/src/deltachat.h +++ b/src/deltachat.h @@ -465,7 +465,7 @@ typedef struct _dc_msg dc_msg_t; #define DC_STATE_IN_NOTICED 13 #define DC_STATE_IN_SEEN 16 #define DC_STATE_OUT_PENDING 20 -#define DC_STATE_OUT_ERROR 24 +#define DC_STATE_OUT_FAILED 24 #define DC_STATE_OUT_DELIVERED 26 // to check if a mail was sent, use dc_msg_is_sent() #define DC_STATE_OUT_MDN_RCVD 28 @@ -621,7 +621,6 @@ time_t dc_lot_get_timestamp (const dc_lot_t*); * Messages or chats changed. One or more messages or chats changed for various * reasons in the database: * - Messages sent, received or removed - * - A message could not be sent (see dc_msg_get_state()/DC_STATE_OUT_ERROR) * - Chats created, deleted or archived * - A draft has been set * @@ -646,7 +645,7 @@ time_t dc_lot_get_timestamp (const dc_lot_t*); /** - * A single message is sent successfully; state changed from DC_STATE_OUT_PENDING to + * A single message is sent successfully. State changed from DC_STATE_OUT_PENDING to * DC_STATE_OUT_DELIVERED, see dc_msg_get_state(). * * @param data1 chat_id @@ -657,7 +656,18 @@ time_t dc_lot_get_timestamp (const dc_lot_t*); /** - * A single message is read by the receiver; state changed from DC_STATE_OUT_DELIVERED to + * A single message could not be sent. State changed from DC_STATE_OUT_PENDING or DC_STATE_OUT_DELIVERED to + * DC_STATE_OUT_FAILED, see dc_msg_get_state(). + * + * @param data1 chat_id + * @param data2 msg_id + * @return 0 + */ +#define DC_EVENT_MSG_FAILED 2012 + + +/** + * A single message is read by the receiver. State changed from DC_STATE_OUT_DELIVERED to * DC_STATE_OUT_MDN_RCVD, see dc_msg_get_state(). * * @param data1 chat_id diff --git a/src/mrmailbox.h b/src/mrmailbox.h index fcd8e477..556958f1 100644 --- a/src/mrmailbox.h +++ b/src/mrmailbox.h @@ -177,7 +177,7 @@ extern "C" { #define MR_STATE_IN_NOTICED DC_STATE_IN_NOTICED #define MR_STATE_IN_SEEN DC_STATE_IN_SEEN #define MR_STATE_OUT_PENDING DC_STATE_OUT_PENDING -#define MR_STATE_OUT_ERROR DC_STATE_OUT_ERROR +#define MR_STATE_OUT_ERROR DC_STATE_OUT_FAILED #define MR_STATE_OUT_DELIVERED DC_STATE_OUT_DELIVERED #define MR_STATE_OUT_MDN_RCVD DC_STATE_OUT_MDN_RCVD #define mrmsg_new dc_msg_new