NeoMutt  2025-12-11-911-gd8d604
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
dlg_list.c
Go to the documentation of this file.
1
22
23#include "config.h"
24#include <stddef.h>
25#include <stdio.h>
26#include "mutt/lib.h"
27#include "email/lib.h"
28#include "core/lib.h"
29#include "gui/lib.h"
30#include "index/lib.h"
31#include "key/lib.h"
32#include "menu/lib.h"
33#include "send/lib.h"
34#include "module_data.h"
35#include "mutt_logging.h"
36#include "mx.h"
37
39static const struct Mapping ListHelp[] = {
40 // clang-format off
41 { N_("Exit"), OP_EXIT },
42 { N_("Archive"), OP_LIST_ARCHIVE },
43 { N_("Help"), OP_LIST_HELP },
44 { N_("Owner"), OP_LIST_OWNER },
45 { N_("Post"), OP_LIST_POST },
46 { N_("Subscribe"), OP_LIST_SUBSCRIBE },
47 { N_("Unsubscribe"), OP_LIST_UNSUBSCRIBE },
48 { NULL, 0 },
49 // clang-format on
50};
51
56{
57 const char *name;
58 int op;
59 size_t offset;
60};
61
63static const struct ListAction ListActions[] = {
64 // clang-format off
65 { N_("Help"), OP_LIST_HELP, offsetof(struct Rfc2369ListHeaders, help) },
66 { N_("Post"), OP_LIST_POST, offsetof(struct Rfc2369ListHeaders, post) },
67 { N_("Subscribe"), OP_LIST_SUBSCRIBE, offsetof(struct Rfc2369ListHeaders, subscribe) },
68 { N_("Unsubscribe"), OP_LIST_UNSUBSCRIBE, offsetof(struct Rfc2369ListHeaders, unsubscribe) },
69 { N_("Archives"), OP_LIST_ARCHIVE, offsetof(struct Rfc2369ListHeaders, archive) },
70 { N_("Owner"), OP_LIST_OWNER, offsetof(struct Rfc2369ListHeaders, owner) },
71 // clang-format on
72};
73
78{
79 struct Menu *menu;
80 struct Mailbox *mailbox;
83 bool done;
84};
85
92static char **get_action_value(struct Rfc2369ListHeaders *headers,
93 const struct ListAction *action)
94{
95 return (char **) (((char *) headers) + action->offset);
96}
97
101static void list_data_free(struct Menu *menu, void **ptr)
102{
103 struct ListData *ld = *ptr;
104 if (!ld)
105 return;
106
108 FREE(ptr);
109}
110
114static int list_make_entry(struct Menu *menu, int line, int max_cols, struct Buffer *buf)
115{
116 struct ListData *ld = menu->mdata;
117 if (!ld || (line < 0) || (line >= (int) countof(ListActions)))
118 return 0;
119
120 const char *name = _(ListActions[line].name);
121 const char *value = *get_action_value(&ld->headers, &ListActions[line]);
122
123 buf_strcpy(buf, name);
124 for (int i = mutt_strwidth(name); i < ld->label_width; i++)
125 buf_addch(buf, ' ');
126 buf_addstr(buf, ": ");
127 buf_addstr(buf, value ? value : "--");
128
129 return mutt_strwidth(buf_string(buf));
130}
131
139static bool compose_list_action(struct ListData *ld, const struct ListAction *action)
140{
141 char *mailto = *get_action_value(&ld->headers, action);
142 if (!mailto)
143 {
144 mutt_error(_("No list action available for %s"), _(action->name));
145 return false;
146 }
147
148 if (url_check_scheme(mailto) != U_MAILTO)
149 {
150 mutt_error(_("List actions only support mailto: URIs. (Try a browser?)"));
151 return true;
152 }
153
154 struct Email *e = email_new();
155 e->env = mutt_env_new();
156
157 char *body = NULL;
158 if (!mutt_parse_mailto(e->env, &body, mailto))
159 {
160 mutt_error(_("Could not parse mailto: URI"));
161 FREE(&body);
162 email_free(&e);
163 return true;
164 }
165
166 e->body = mutt_body_new();
167 char ctype[] = "text/plain";
168 mutt_parse_content_type(ctype, e->body);
169 e->body->use_disp = false;
171
172 struct Buffer *tempfile = buf_pool_get();
173 buf_mktemp_draft(tempfile);
174 FILE *fp = mutt_file_fopen(buf_string(tempfile), "w+");
175 if (!fp)
176 {
177 mutt_perror("%s", buf_string(tempfile));
178 FREE(&body);
179 email_free(&e);
180 buf_pool_release(&tempfile);
181 return true;
182 }
183
184 if (body)
185 fprintf(fp, "%s\n", body);
186 mutt_file_fclose(&fp);
187 FREE(&body);
188
189 e->body->filename = buf_strdup(tempfile);
190 e->body->unlink = true;
191 buf_pool_release(&tempfile);
192
193 (void) mutt_send_message(SEND_DRAFT_FILE, e, NULL, ld->mailbox, NULL, NeoMutt->sub);
194 return true;
195}
196
203static int op_exit(struct ListData *ld, const struct KeyEvent *event)
204{
205 ld->done = true;
206 return FR_SUCCESS;
207}
208
215static int op_select_action(struct ListData *ld, const struct KeyEvent *event)
216{
217 const int index = menu_get_index(ld->menu);
218 if ((index < 0) || (index >= (int) countof(ListActions)))
219 return FR_NO_ACTION;
220
221 ld->done = compose_list_action(ld, &ListActions[index]);
222 return FR_SUCCESS;
223}
224
229{
230 int op;
231 int (*function)(struct ListData *ld, const struct KeyEvent *event);
232};
233
235static const struct ListFunction ListFunctions[] = {
236 // clang-format off
237 { OP_EXIT, op_exit },
238 { OP_GENERIC_SELECT_ENTRY, op_select_action },
239 { OP_LIST_ARCHIVE, op_select_action },
240 { OP_LIST_HELP, op_select_action },
241 { OP_LIST_OWNER, op_select_action },
242 { OP_LIST_POST, op_select_action },
243 { OP_LIST_SUBSCRIBE, op_select_action },
244 { OP_LIST_UNSUBSCRIBE, op_select_action },
245 { 0, NULL },
246 // clang-format on
247};
248
252static int list_function_dispatcher(struct MuttWindow *win, const struct KeyEvent *event)
253{
254 struct MuttWindow *dlg = dialog_find(win);
255 if (!event || !dlg || !dlg->wdata)
256 {
258 return FR_ERROR;
259 }
260
261 struct Menu *menu = dlg->wdata;
262 struct ListData *ld = menu->mdata;
263 if (!ld)
264 {
266 return FR_ERROR;
267 }
268
269 int rc = FR_UNKNOWN;
270 for (size_t i = 0; ListFunctions[i].op != OP_NULL; i++)
271 {
272 if (ListFunctions[i].op == event->op)
273 {
274 rc = ListFunctions[i].function(ld, event);
275 break;
276 }
277 }
278
279 if (rc == FR_UNKNOWN)
280 return rc;
281
282 mutt_debug(LL_DEBUG1, "Handled %s (%d) -> %s\n", opcodes_get_name(event->op),
285 return rc;
286}
287
293void dlg_list(struct Mailbox *m, struct Email *e)
294{
295 if (!m || !e)
296 return;
297
299 ASSERT(mod_data);
300
301 struct ListData *ld = MUTT_MEM_CALLOC(1, struct ListData);
302 ld->mailbox = m;
303
304 struct Message *msg = mx_msg_open(m, e);
305 if (!msg)
306 {
307 FREE(&ld);
308 return;
309 }
310
311 if (!mutt_file_seek(msg->fp, e->offset, SEEK_SET))
312 {
313 mutt_error(_("Unable to read mailing list headers"));
314 mx_msg_close(m, &msg);
315 FREE(&ld);
316 return;
317 }
318
320 mx_msg_close(m, &msg);
321
322 for (size_t i = 0; i < countof(ListActions); i++)
324
325 struct SimpleDialogWindows sdw = simple_dialog_new(mod_data->menu_list,
327 struct Menu *menu = sdw.menu;
328 ld->menu = menu;
329 menu->max = countof(ListActions);
331 menu->mdata = ld;
333
334 sbar_set_title(sdw.sbar, _("Available mailing list actions"));
335
336 struct MuttWindow *old_focus = window_set_focus(menu->win);
337 // ---------------------------------------------------------------------------
338 // Event Loop
339 int op = OP_NULL;
340 struct KeyEvent event = { 0, OP_NULL };
341 do
342 {
343 menu_tagging_dispatcher(menu->win, &event);
344 window_redraw(NULL);
345
346 event = km_dokey(mod_data->menu_list, GETCH_NONE);
347 op = event.op;
348 mutt_debug(LL_DEBUG1, "Got op %s (%d)\n", opcodes_get_name(op), op);
349 if (op < 0)
350 continue;
351 if (op == OP_NULL)
352 {
353 km_error_key(mod_data->menu_list);
354 continue;
355 }
357
358 int rc = list_function_dispatcher(sdw.dlg, &event);
359 if (rc == FR_UNKNOWN)
360 rc = menu_function_dispatcher(menu->win, &event);
361 if (rc == FR_UNKNOWN)
362 rc = global_function_dispatcher(menu->win, &event);
363 } while (!ld->done);
364 // ---------------------------------------------------------------------------
365
366 window_set_focus(old_focus);
368}
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:241
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:226
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:571
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
Convenience wrapper for the core headers.
size_t mutt_strwidth(const char *s)
Measure a string's width in screen cells.
Definition curs_lib.c:446
struct MuttWindow * dialog_find(struct MuttWindow *win)
Find the parent Dialog of a Window.
Definition dialog.c:89
const char * dispatcher_get_retval_name(int rv)
Get the name of a return value.
Definition dispatcher.c:55
void dispatcher_flush_on_error(int rv)
Flush pending keys after a dispatch error.
Definition dispatcher.c:65
@ FR_SUCCESS
Valid function - successfully performed.
Definition dispatcher.h:40
@ FR_UNKNOWN
Unknown function.
Definition dispatcher.h:34
@ FR_ERROR
Valid function - error occurred.
Definition dispatcher.h:39
@ FR_NO_ACTION
Valid function - no action performed.
Definition dispatcher.h:38
static const struct Mapping ListHelp[]
Help Bar for the Mailing-list action dialog.
Definition dlg_list.c:39
static int op_exit(struct ListData *ld, const struct KeyEvent *event)
Exit the list dialog.
Definition dlg_list.c:203
static char ** get_action_value(struct Rfc2369ListHeaders *headers, const struct ListAction *action)
Get the stored value for a mailing-list action.
Definition dlg_list.c:92
static int op_select_action(struct ListData *ld, const struct KeyEvent *event)
Execute the selected mailing-list action.
Definition dlg_list.c:215
static const struct ListAction ListActions[]
Mailing-list actions shown in the dialog.
Definition dlg_list.c:63
static bool compose_list_action(struct ListData *ld, const struct ListAction *action)
Compose a message for a mailing-list action.
Definition dlg_list.c:139
static const struct ListFunction ListFunctions[]
Functions handled by the list dialog.
Definition dlg_list.c:235
struct Body * mutt_body_new(void)
Create a new Body.
Definition body.c:44
struct Email * email_new(void)
Create a new Email.
Definition email.c:77
void email_free(struct Email **ptr)
Free an Email.
Definition email.c:46
Structs that make up an email.
void mutt_parse_content_type(const char *s, struct Body *b)
Parse a content type.
Definition parse.c:433
bool mutt_parse_mailto(struct Envelope *env, char **body, const char *src)
Parse a mailto:// url.
Definition parse.c:1812
void mutt_rfc2369_list_headers_free(struct Rfc2369ListHeaders *headers)
Free RFC 2369 mailing-list header values.
Definition parse.c:639
void mutt_rfc2369_read_headers(FILE *fp, struct Rfc2369ListHeaders *headers)
Read RFC 2369 mailing-list headers.
Definition parse.c:657
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition envelope.c:45
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition file.c:648
#define mutt_file_fclose(FP)
Definition file.h:144
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
struct KeyEvent km_dokey(const struct MenuDefinition *md, GetChFlags flags)
Determine what a keypress should do.
Definition get.c:518
void km_error_key(const struct MenuDefinition *md)
Handle an unbound key sequence.
Definition get.c:328
@ GETCH_NONE
No flags are set.
Definition get.h:38
static int op_exit(struct AliasFunctionData *fdata, const struct KeyEvent *event)
exit this menu - Implements alias_function_t -
Definition functions.c:312
static int list_function_dispatcher(struct MuttWindow *win, const struct KeyEvent *event)
Perform a List dialog function - Implements function_dispatcher_t -.
Definition dlg_list.c:252
int menu_tagging_dispatcher(struct MuttWindow *win, const struct KeyEvent *event)
Perform tagging operations on the Menu - Implements function_dispatcher_t -.
Definition tagging.c:239
int global_function_dispatcher(struct MuttWindow *win, const struct KeyEvent *event)
Perform a Global function - Implements function_dispatcher_t -.
Definition global.c:182
int menu_function_dispatcher(struct MuttWindow *win, const struct KeyEvent *event)
Perform a Menu function - Implements function_dispatcher_t -.
Definition functions.c:366
void dlg_list(struct Mailbox *m, struct Email *e)
Display mailing-list actions for an email -.
Definition dlg_list.c:293
#define mutt_error(...)
Definition logging2.h:94
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
#define mutt_perror(...)
Definition logging2.h:95
static int list_make_entry(struct Menu *menu, int line, int max_cols, struct Buffer *buf)
Format a mailing-list action for the dialog - Implements Menu::make_entry() -.
Definition dlg_list.c:114
static void list_data_free(struct Menu *menu, void **ptr)
Free list dialog data - Implements Menu::mdata_free() -.
Definition dlg_list.c:101
Convenience wrapper for the gui headers.
void simple_dialog_free(struct MuttWindow **ptr)
Destroy a simple index Dialog.
Definition simple.c:169
struct SimpleDialogWindows simple_dialog_new(const struct MenuDefinition *md, enum WindowType wtype, const struct Mapping *help_data)
Create a simple index Dialog.
Definition simple.c:132
GUI manage the main index (list of emails)
Index private Module data.
Manage keymappings.
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
#define countof(x)
Definition memory.h:49
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
#define MAX(a, b)
Return the maximum of two values.
Definition memory.h:38
GUI present the user with a selectable list.
int menu_get_index(struct Menu *menu)
Get the current selection in the Menu.
Definition menu.c:155
@ DISP_INLINE
Content is inline.
Definition mime.h:62
@ MODULE_ID_INDEX
ModuleIndex, Index
Definition module_api.h:72
Convenience wrapper for the library headers.
#define N_(a)
Definition message.h:32
#define _(a)
Definition message.h:28
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
NeoMutt Logging.
void window_redraw(struct MuttWindow *win)
Reflow, recalc and repaint a tree of Windows.
struct MuttWindow * window_set_focus(struct MuttWindow *win)
Set the Window focus.
@ WT_DLG_LIST
List Dialog, dlg_list()
Definition mutt_window.h:87
int mx_msg_close(struct Mailbox *m, struct Message **ptr)
Close a message.
Definition mx.c:1182
struct Message * mx_msg_open(struct Mailbox *m, struct Email *e)
Return a stream pointer for a message.
Definition mx.c:1136
API for mailboxes.
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:663
const char * opcodes_get_name(int op)
Get the name of an opcode.
Definition opcodes.c:48
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
void sbar_set_title(struct MuttWindow *win, const char *title)
Set the title for the Simple Bar.
Definition sbar.c:227
Convenience wrapper for the send headers.
int mutt_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile, struct Mailbox *m, struct EmailArray *ea, struct ConfigSubset *sub)
Send an email.
Definition send.c:2030
@ SEND_DRAFT_FILE
Used by the -H flag.
Definition send.h:56
#define ASSERT(COND)
Definition signal2.h:59
#define NONULL(x)
Definition string2.h:44
bool unlink
If true, filename should be unlink()ed before free()ing this structure.
Definition body.h:68
bool use_disp
Content-Disposition uses filename= ?
Definition body.h:47
unsigned int disposition
content-disposition, ContentDisposition
Definition body.h:42
char * filename
When sending a message, this is the file to which this structure refers.
Definition body.h:59
String manipulation buffer.
Definition buffer.h:36
The envelope/body of an email.
Definition email.h:39
struct Envelope * env
Envelope information.
Definition email.h:68
struct Body * body
List of MIME parts.
Definition email.h:69
LOFF_T offset
Where in the stream does this message begin?
Definition email.h:71
Index private Module data.
Definition module_data.h:32
struct MenuDefinition * menu_list
Mailing-list action menu definition.
Definition module_data.h:35
An event such as a keypress.
Definition get.h:75
int op
Function opcode, e.g. OP_HELP.
Definition get.h:77
A mailing-list action in the dialog.
Definition dlg_list.c:56
const char * name
Label for the action.
Definition dlg_list.c:57
int op
Op code.
Definition dlg_list.c:58
size_t offset
Offset into struct Rfc2369ListHeaders.
Definition dlg_list.c:59
Private data for the Mailing-list action dialog.
Definition dlg_list.c:78
int label_width
Width of the longest action label.
Definition dlg_list.c:82
struct Menu * menu
Dialog menu.
Definition dlg_list.c:79
struct Mailbox * mailbox
Source mailbox.
Definition dlg_list.c:80
struct Rfc2369ListHeaders headers
Parsed List-* headers.
Definition dlg_list.c:81
bool done
Exit the dialog.
Definition dlg_list.c:83
A list dialog function.
Definition dlg_list.c:229
int(* function)(struct ListData *ld, const struct KeyEvent *event)
Function.
Definition dlg_list.c:231
int op
Op code.
Definition dlg_list.c:230
A mailbox.
Definition mailbox.h:81
Mapping between user-readable string and a constant.
Definition mapping.h:33
Definition lib.h:86
struct MuttWindow * win
Window holding the Menu.
Definition lib.h:94
void(* mdata_free)(struct Menu *menu, void **ptr)
Definition lib.h:169
int(* make_entry)(struct Menu *menu, int line, int max_cols, struct Buffer *buf)
Definition lib.h:114
void * mdata
Private data.
Definition lib.h:155
int max
Number of entries in the menu.
Definition lib.h:88
A local copy of an email.
Definition message.h:34
FILE * fp
pointer to the message data
Definition message.h:35
void * wdata
Private data.
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
Mailing-list actions from RFC 2369 headers.
Definition parse.h:41
Tuple for the results of simple_dialog_new()
Definition simple.h:35
struct MuttWindow * sbar
Simple Bar.
Definition simple.h:37
struct Menu * menu
Menu.
Definition simple.h:38
struct MuttWindow * dlg
Main Dialog Window.
Definition simple.h:36
#define buf_mktemp_draft(buf)
Definition tmp.h:34
enum UrlScheme url_check_scheme(const char *str)
Check the protocol of a URL.
Definition url.c:229
@ U_MAILTO
Url is mailto://.
Definition url.h:45