/* Non-zero return indicates an error in retrieving the message, * or in applying the tags. Missing messages are reported, but not * considered errors. */ static int tag_message (unused (void *ctx), notmuch_database_t *notmuch, const char *message_id, tag_op_list_t *tag_ops, tag_op_flag_t flags) { notmuch_status_t status; notmuch_message_t *message = NULL; int ret = 0; status = notmuch_database_find_message (notmuch, message_id, &message); if (status) { fprintf (stderr, "Error applying tags to message %s: %s\n", message_id, notmuch_status_to_string (status)); return 1; } if (message == NULL) { fprintf (stderr, "Warning: cannot apply tags to missing message: %s\n", message_id); /* We consider this a non-fatal error. */ return 0; } /* In order to detect missing messages, this check/optimization is * intentionally done *after* first finding the message. */ if ((flags & TAG_FLAG_REMOVE_ALL) || tag_op_list_size (tag_ops)) ret = tag_op_list_apply (message, tag_ops, flags); notmuch_message_destroy (message); return ret; }
/* * returns message from notmuch database */ static notmuch_message_t *get_nm_message(notmuch_database_t *db, HEADER *hdr) { notmuch_message_t *msg = NULL; char *id = nm_header_get_id(hdr); dprint(2, (debugfile, "nm: find message (%s)\n", id)); if (id && db) notmuch_database_find_message(db, id, &msg); return msg; }
/* * call-seq: DB.find_message(id) => MESSAGE or nil * * Find a message by message id. */ VALUE notmuch_rb_database_find_message (VALUE self, VALUE idv) { const char *id; notmuch_status_t ret; notmuch_database_t *db; notmuch_message_t *message; Data_Get_Notmuch_Database (self, db); SafeStringValue (idv); id = RSTRING_PTR (idv); ret = notmuch_database_find_message (db, id, &message); notmuch_rb_status_raise (ret); if (message) return Data_Wrap_Struct (notmuch_rb_cMessage, NULL, NULL, message); return Qnil; }
int cmd_move_message(ClientData data, Tcl_Interp *interp, int argc, const char *argv[]) { filter_context_t *ctx = FILTER_CONTEXT(data); notmuch_message_t *msg = ctx->current_message; notmuch_status_t nmrc; if (argc != 2) { tcl_result_printf(interp, "wrong # of args: got %d, expected move folder", argc); return TCL_ERROR; } const char *folder = argv[1]; if (folder[0] == '/') { tcl_result_printf(interp, "invalid folder '%s'", folder); return TCL_ERROR; } log_debug("moving message %s to folder %s", notmuch_message_get_message_id(msg), folder); char *folder_path = g_strdup_printf("%s/%s", notmuch_database_get_path(ctx->database), folder); int status = TCL_ERROR; notmuch_filenames_t *fns = notmuch_message_get_filenames(msg); int nmoved = 0; while (notmuch_filenames_valid(fns)) { const char *fn = notmuch_filenames_get(fns); char *new_fn = NULL; if (g_str_has_prefix(fn, folder_path)) { log_debug("file %s already in folder %s", fn, folder); } else if (ctx->dry_run) { log_debug("moving %s to folder %s", fn, folder); } else { log_debug("moving %s to folder %s", fn, folder); int rc = maildir_deliver_link(fn, folder_path, &new_fn); if (rc) { tcl_result_printf(interp, "delivery error: %s", strerror(errno)); goto done; } log_debug("delivered as %s", new_fn); nmrc = notmuch_database_add_message(ctx->database, new_fn, NULL); switch (nmrc) { case NOTMUCH_STATUS_SUCCESS: log_warning("%s: message was not in database", new_fn); case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: log_debug("added new file to database"); break; default: tcl_result_printf(interp, "error adding %s to database: %s", new_fn, notmuch_status_to_string(nmrc)); goto done; } nmrc = notmuch_database_remove_message(ctx->database, fn); switch (nmrc) { case NOTMUCH_STATUS_SUCCESS: log_warning("%s: file was only file for message", fn); case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: log_debug("removed file %s from database", fn); break; default: tcl_result_printf(interp, "error removing %s from database: %s", fn, notmuch_status_to_string(nmrc)); goto done; } if (unlink(fn)) { tcl_result_printf(interp, "error unlinking %s: %s", fn, strerror(errno)); goto done; } nmoved += 1; } notmuch_filenames_move_to_next(fns); } notmuch_filenames_destroy(fns); fns = NULL; if (nmoved == 0) { Tcl_SetObjResult(interp, Tcl_NewIntObj(0)); status = TCL_OK; goto done; } log_debug("syncing maildir flags"); const char *mid = notmuch_message_get_message_id(msg); nmrc = notmuch_database_find_message(ctx->database, mid, &msg); if (nmrc != NOTMUCH_STATUS_SUCCESS) { tcl_result_printf(interp, "cannot re-find message %s", mid); goto done; } notmuch_message_destroy(ctx->current_message); ctx->current_message = msg; nmrc = notmuch_message_tags_to_maildir_flags(msg); if (nmrc != NOTMUCH_STATUS_SUCCESS) { tcl_result_printf(interp, "error syncing tags back to flags"); goto done; } Tcl_SetObjResult(interp, Tcl_NewIntObj(nmoved)); status = TCL_OK; done: if (fns) { notmuch_filenames_destroy(fns); } g_free(folder_path); return status; }
int notmuch_new_command (void *ctx, int argc, char *argv[]) { notmuch_config_t *config; notmuch_database_t *notmuch; add_files_state_t add_files_state; double elapsed; struct timeval tv_now, tv_start; int ret = 0; struct stat st; const char *db_path; char *dot_notmuch_path; struct sigaction action; _filename_node_t *f; int renamed_files, removed_files; notmuch_status_t status; int i; notmuch_bool_t timer_is_active = FALSE; add_files_state.verbose = 0; add_files_state.output_is_a_tty = isatty (fileno (stdout)); for (i = 0; i < argc && argv[i][0] == '-'; i++) { if (STRNCMP_LITERAL (argv[i], "--verbose") == 0) { add_files_state.verbose = 1; } else { fprintf (stderr, "Unrecognized option: %s\n", argv[i]); return 1; } } config = notmuch_config_open (ctx, NULL, NULL); if (config == NULL) return 1; add_files_state.new_tags = notmuch_config_get_new_tags (config, &add_files_state.new_tags_length); add_files_state.synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config); add_files_state.message_ids_to_sync = _filename_list_create (ctx); db_path = notmuch_config_get_database_path (config); dot_notmuch_path = talloc_asprintf (ctx, "%s/%s", db_path, ".notmuch"); if (stat (dot_notmuch_path, &st)) { int count; count = 0; count_files (db_path, &count); if (interrupted) return 1; printf ("Found %d total files (that's not much mail).\n", count); notmuch = notmuch_database_create (db_path); add_files_state.total_files = count; } else { notmuch = notmuch_database_open (db_path, NOTMUCH_DATABASE_MODE_READ_WRITE); if (notmuch == NULL) return 1; if (notmuch_database_needs_upgrade (notmuch)) { printf ("Welcome to a new version of notmuch! Your database will now be upgraded.\n"); gettimeofday (&add_files_state.tv_start, NULL); notmuch_database_upgrade (notmuch, upgrade_print_progress, &add_files_state); printf ("Your notmuch database has now been upgraded to database format version %u.\n", notmuch_database_get_version (notmuch)); } add_files_state.total_files = 0; } if (notmuch == NULL) return 1; /* Setup our handler for SIGINT. We do this after having * potentially done a database upgrade we this interrupt handler * won't support. */ memset (&action, 0, sizeof (struct sigaction)); action.sa_handler = handle_sigint; sigemptyset (&action.sa_mask); action.sa_flags = SA_RESTART; sigaction (SIGINT, &action, NULL); talloc_free (dot_notmuch_path); dot_notmuch_path = NULL; add_files_state.processed_files = 0; add_files_state.added_messages = 0; gettimeofday (&add_files_state.tv_start, NULL); add_files_state.removed_files = _filename_list_create (ctx); add_files_state.removed_directories = _filename_list_create (ctx); if (! debugger_is_active () && add_files_state.output_is_a_tty && ! add_files_state.verbose) { setup_progress_printing_timer (); timer_is_active = TRUE; } ret = add_files (notmuch, db_path, &add_files_state); removed_files = 0; renamed_files = 0; gettimeofday (&tv_start, NULL); for (f = add_files_state.removed_files->head; f; f = f->next) { status = notmuch_database_remove_message (notmuch, f->filename); if (status == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID) renamed_files++; else removed_files++; if (do_print_progress) { do_print_progress = 0; generic_print_progress ("Cleaned up", "messages", tv_start, removed_files + renamed_files, add_files_state.removed_files->count); } } gettimeofday (&tv_start, NULL); for (f = add_files_state.removed_directories->head, i = 0; f; f = f->next, i++) { _remove_directory (ctx, notmuch, f->filename, &renamed_files, &removed_files); if (do_print_progress) { do_print_progress = 0; generic_print_progress ("Cleaned up", "directories", tv_start, i, add_files_state.removed_directories->count); } } talloc_free (add_files_state.removed_files); talloc_free (add_files_state.removed_directories); /* Now that removals are done (hence the database is aware of all * renames), we can synchronize maildir_flags to tags for all * messages that had new filenames appear on this run. */ gettimeofday (&tv_start, NULL); if (add_files_state.synchronize_flags) { _filename_node_t *node; notmuch_message_t *message; for (node = add_files_state.message_ids_to_sync->head, i = 0; node; node = node->next, i++) { message = notmuch_database_find_message (notmuch, node->filename); notmuch_message_maildir_flags_to_tags (message); notmuch_message_destroy (message); if (do_print_progress) { do_print_progress = 0; generic_print_progress ( "Synchronized tags for", "messages", tv_start, i, add_files_state.message_ids_to_sync->count); } } } talloc_free (add_files_state.message_ids_to_sync); add_files_state.message_ids_to_sync = NULL; if (timer_is_active) stop_progress_printing_timer (); gettimeofday (&tv_now, NULL); elapsed = notmuch_time_elapsed (add_files_state.tv_start, tv_now); if (add_files_state.processed_files) { printf ("Processed %d %s in ", add_files_state.processed_files, add_files_state.processed_files == 1 ? "file" : "total files"); notmuch_time_print_formatted_seconds (elapsed); if (elapsed > 1) { printf (" (%d files/sec.).\033[K\n", (int) (add_files_state.processed_files / elapsed)); } else { printf (".\033[K\n"); } } if (add_files_state.added_messages) { printf ("Added %d new %s to the database.", add_files_state.added_messages, add_files_state.added_messages == 1 ? "message" : "messages"); } else { printf ("No new mail."); } if (removed_files) { printf (" Removed %d %s.", removed_files, removed_files == 1 ? "message" : "messages"); } if (renamed_files) { printf (" Detected %d file %s.", renamed_files, renamed_files == 1 ? "rename" : "renames"); } printf ("\n"); if (ret) { printf ("\nNote: At least one error was encountered: %s\n", notmuch_status_to_string (ret)); } notmuch_database_close (notmuch); return ret || interrupted; }
static int tag_message (notmuch_database_t *notmuch, const char *message_id, char *file_tags, notmuch_bool_t remove_all, notmuch_bool_t synchronize_flags) { notmuch_status_t status; notmuch_tags_t *db_tags; char *db_tags_str; notmuch_message_t *message = NULL; const char *tag; char *next; int ret = 0; status = notmuch_database_find_message (notmuch, message_id, &message); if (status || message == NULL) { fprintf (stderr, "Warning: Cannot apply tags to %smessage: %s\n", message ? "" : "missing ", message_id); if (status) fprintf (stderr, "%s\n", notmuch_status_to_string(status)); return 1; } /* In order to detect missing messages, this check/optimization is * intentionally done *after* first finding the message. */ if (!remove_all && (file_tags == NULL || *file_tags == '\0')) goto DONE; db_tags_str = NULL; for (db_tags = notmuch_message_get_tags (message); notmuch_tags_valid (db_tags); notmuch_tags_move_to_next (db_tags)) { tag = notmuch_tags_get (db_tags); if (db_tags_str) db_tags_str = talloc_asprintf_append (db_tags_str, " %s", tag); else db_tags_str = talloc_strdup (message, tag); } if (((file_tags == NULL || *file_tags == '\0') && (db_tags_str == NULL || *db_tags_str == '\0')) || (file_tags && db_tags_str && strcmp (file_tags, db_tags_str) == 0)) goto DONE; notmuch_message_freeze (message); if (remove_all) notmuch_message_remove_all_tags (message); next = file_tags; while (next) { tag = strsep (&next, " "); if (*tag == '\0') continue; status = notmuch_message_add_tag (message, tag); if (status) { fprintf (stderr, "Error applying tag %s to message %s:\n", tag, message_id); fprintf (stderr, "%s\n", notmuch_status_to_string (status)); ret = 1; } } notmuch_message_thaw (message); if (synchronize_flags) notmuch_message_tags_to_maildir_flags (message); DONE: if (message) notmuch_message_destroy (message); return ret; }