1 /**
2 * @file
3 * Create a GraphViz dot file from the NeoMutt objects
4 *
5 * @authors
6 * Copyright (C) 2018-2020 Richard Russon <rich@flatcap.org>
7 *
8 * @copyright
9 * This program is free software: you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 2 of the License, or (at your option) any later
12 * version.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 /**
24 * @page debug_graphviz GraphViz dot file
25 *
26 * Create a GraphViz dot file from the NeoMutt objects
27 */
28
29 #include "config.h"
30 #include <stdbool.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <sys/stat.h>
34 #include <time.h>
35 #include "mutt/lib.h"
36 #include "address/lib.h"
37 #include "config/lib.h"
38 #include "email/lib.h"
39 #include "core/lib.h"
40 #include "conn/lib.h"
41 #include "lib.h"
42 #include "compmbox/lib.h"
43 #include "imap/lib.h"
44 #include "maildir/lib.h"
45 #include "mbox/lib.h"
46 #include "ncrypt/lib.h"
47 #include "nntp/lib.h"
48 #include "notmuch/lib.h"
49 #include "pop/lib.h"
50 #include "context.h"
51 #include "imap/adata.h" // IWYU pragma: keep
52 #include "imap/mdata.h" // IWYU pragma: keep
53 #include "imap/private.h" // IWYU pragma: keep
54 #include "maildir/edata.h" // IWYU pragma: keep
55 #include "maildir/mdata.h" // IWYU pragma: keep
56 #include "maildir/private.h" // IWYU pragma: keep
57 #include "nntp/adata.h" // IWYU pragma: keep
58 #include "nntp/mdata.h" // IWYU pragma: keep
59 #include "notmuch/adata.h" // IWYU pragma: keep
60 #include "notmuch/mdata.h" // IWYU pragma: keep
61 #include "notmuch/private.h" // IWYU pragma: keep
62 #include "pop/adata.h" // IWYU pragma: keep
63 #include "pop/private.h" // IWYU pragma: keep
64
65 // #define GV_HIDE_CONTEXT
66 #define GV_HIDE_CONTEXT_CONTENTS
67 // #define GV_HIDE_MBOX
68 // #define GV_HIDE_NEOMUTT
69 // #define GV_HIDE_CONFIG
70 // #define GV_HIDE_ADATA
71 // #define GV_HIDE_MDATA
72
73 static void dot_email(FILE *fp, struct Email *e, struct ListHead *links);
74 static void dot_envelope(FILE *fp, struct Envelope *env, struct ListHead *links);
75
get_content_type(enum ContentType type)76 const char *get_content_type(enum ContentType type)
77 {
78 switch (type)
79 {
80 case TYPE_OTHER:
81 return "TYPE_OTHER";
82 case TYPE_AUDIO:
83 return "TYPE_AUDIO";
84 case TYPE_APPLICATION:
85 return "TYPE_APPLICATION";
86 case TYPE_IMAGE:
87 return "TYPE_IMAGE";
88 case TYPE_MESSAGE:
89 return "TYPE_MESSAGE";
90 case TYPE_MODEL:
91 return "TYPE_MODEL";
92 case TYPE_MULTIPART:
93 return "TYPE_MULTIPART";
94 case TYPE_TEXT:
95 return "TYPE_TEXT";
96 case TYPE_VIDEO:
97 return "TYPE_VIDEO";
98 case TYPE_ANY:
99 return "TYPE_ANY";
100 default:
101 return "UNKNOWN";
102 }
103 }
104
get_content_encoding(enum ContentEncoding enc)105 const char *get_content_encoding(enum ContentEncoding enc)
106 {
107 switch (enc)
108 {
109 case ENC_OTHER:
110 return "ENC_OTHER";
111 case ENC_7BIT:
112 return "ENC_7BIT";
113 case ENC_8BIT:
114 return "ENC_8BIT";
115 case ENC_QUOTED_PRINTABLE:
116 return "ENC_QUOTED_PRINTABLE";
117 case ENC_BASE64:
118 return "ENC_BASE64";
119 case ENC_BINARY:
120 return "ENC_BINARY";
121 case ENC_UUENCODED:
122 return "ENC_UUENCODED";
123 default:
124 return "UNKNOWN";
125 }
126 }
127
get_content_disposition(enum ContentDisposition disp)128 const char *get_content_disposition(enum ContentDisposition disp)
129 {
130 switch (disp)
131 {
132 case DISP_INLINE:
133 return "DISP_INLINE";
134 case DISP_ATTACH:
135 return "DISP_ATTACH";
136 case DISP_FORM_DATA:
137 return "DISP_FORM_DATA";
138 case DISP_NONE:
139 return "DISP_NONE";
140 default:
141 return "UNKNOWN";
142 }
143 }
144
add_flag(struct Buffer * buf,bool is_set,const char * name)145 void add_flag(struct Buffer *buf, bool is_set, const char *name)
146 {
147 if (!buf || !name)
148 return;
149
150 if (is_set)
151 {
152 if (!mutt_buffer_is_empty(buf))
153 mutt_buffer_addch(buf, ',');
154 mutt_buffer_addstr(buf, name);
155 }
156 }
157
dot_type_bool(FILE * fp,const char * name,bool val)158 static void dot_type_bool(FILE *fp, const char *name, bool val)
159 {
160 static const char *values[] = { "false", "true" };
161 fprintf(fp, "\t\t<tr>\n");
162 fprintf(fp, "\t\t\t<td border=\"0\" align=\"left\">%s</td>\n", name);
163 fprintf(fp, "\t\t\t<td border=\"0\">=</td>\n");
164 fprintf(fp, "\t\t\t<td border=\"0\" align=\"left\">%s</td>\n", values[val]);
165 fprintf(fp, "\t\t</tr>\n");
166 }
167
168 #ifndef GV_HIDE_ADATA
dot_type_char(FILE * fp,const char * name,char ch)169 static void dot_type_char(FILE *fp, const char *name, char ch)
170 {
171 fprintf(fp, "\t\t<tr>\n");
172 fprintf(fp, "\t\t\t<td border=\"0\" align=\"left\">%s</td>\n", name);
173 fprintf(fp, "\t\t\t<td border=\"0\">=</td>\n");
174 fprintf(fp, "\t\t\t<td border=\"0\" align=\"left\">%c</td>\n", ch);
175 fprintf(fp, "\t\t</tr>\n");
176 }
177 #endif
178
dot_type_date(char * buf,size_t buflen,time_t timestamp)179 static void dot_type_date(char *buf, size_t buflen, time_t timestamp)
180 {
181 mutt_date_localtime_format(buf, buflen, "%Y-%m-%d %H:%M:%S", timestamp);
182 }
183
dot_type_file(FILE * fp,const char * name,FILE * struct_fp)184 static void dot_type_file(FILE *fp, const char *name, FILE *struct_fp)
185 {
186 fprintf(fp, "\t\t<tr>\n");
187 fprintf(fp, "\t\t\t<td border=\"0\" align=\"left\">%s</td>\n", name);
188 fprintf(fp, "\t\t\t<td border=\"0\">=</td>\n");
189 if (struct_fp)
190 {
191 fprintf(fp, "\t\t\t<td border=\"0\" align=\"left\">%p (%d)</td>\n",
192 (void *) struct_fp, fileno(struct_fp));
193 }
194 else
195 {
196 fprintf(fp, "\t\t\t<td border=\"0\" align=\"left\">NULL</td>\n");
197 }
198 fprintf(fp, "\t\t</tr>\n");
199 }
200
dot_type_number(FILE * fp,const char * name,int num)201 static void dot_type_number(FILE *fp, const char *name, int num)
202 {
203 fprintf(fp, "\t\t<tr>\n");
204 fprintf(fp, "\t\t\t<td border=\"0\" align=\"left\">%s</td>\n", name);
205 fprintf(fp, "\t\t\t<td border=\"0\">=</td>\n");
206 fprintf(fp, "\t\t\t<td border=\"0\" align=\"left\">%d</td>\n", num);
207 fprintf(fp, "\t\t</tr>\n");
208 }
209
dot_type_string_escape(char * buf,size_t buflen)210 static void dot_type_string_escape(char *buf, size_t buflen)
211 {
212 for (; buf[0]; buf++)
213 {
214 if (buf[0] == '<')
215 mutt_str_inline_replace(buf, buflen, 1, "<");
216 else if (buf[0] == '>')
217 mutt_str_inline_replace(buf, buflen, 1, ">");
218 else if (buf[0] == '&')
219 mutt_str_inline_replace(buf, buflen, 1, "&");
220 }
221 }
222
dot_type_string(FILE * fp,const char * name,const char * str,bool force)223 static void dot_type_string(FILE *fp, const char *name, const char *str, bool force)
224 {
225 if ((!str || (str[0] == '\0')) && !force)
226 return;
227
228 char buf[1024] = "[NULL]";
229
230 if (str)
231 {
232 mutt_str_copy(buf, str, sizeof(buf));
233 dot_type_string_escape(buf, sizeof(buf));
234 }
235
236 bool quoted = ((buf[0] != '[') && (buf[0] != '*'));
237
238 fprintf(fp, "\t\t<tr>\n");
239 fprintf(fp, "\t\t\t<td border=\"0\" align=\"left\">%s</td>\n", name);
240 fprintf(fp, "\t\t\t<td border=\"0\">=</td>\n");
241 if (quoted)
242 fprintf(fp, "\t\t\t<td border=\"0\" align=\"left\">\"%s\"</td>\n", buf);
243 else
244 fprintf(fp, "\t\t\t<td border=\"0\" align=\"left\">%s</td>\n", buf);
245 fprintf(fp, "\t\t</tr>\n");
246 }
247
248 #ifndef GV_HIDE_MDATA
dot_type_umask(char * buf,size_t buflen,int umask)249 static void dot_type_umask(char *buf, size_t buflen, int umask)
250 {
251 snprintf(buf, buflen, "0%03o", umask);
252 }
253 #endif
254
dot_ptr_name(char * buf,size_t buflen,const void * ptr)255 static void dot_ptr_name(char *buf, size_t buflen, const void *ptr)
256 {
257 snprintf(buf, buflen, "obj_%p", ptr);
258 }
259
dot_ptr(FILE * fp,const char * name,void * ptr,const char * colour)260 static void dot_ptr(FILE *fp, const char *name, void *ptr, const char *colour)
261 {
262 fprintf(fp, "\t\t<tr>\n");
263 fprintf(fp, "\t\t\t<td border=\"0\" align=\"left\">%s</td>\n", name);
264 fprintf(fp, "\t\t\t<td border=\"0\">=</td>\n");
265 if (colour && ptr)
266 {
267 fprintf(fp, "\t\t\t<td border=\"0\" align=\"left\" bgcolor=\"%s\">%p</td>\n",
268 colour, ptr);
269 }
270 else
271 {
272 fprintf(fp, "\t\t\t<td border=\"0\" align=\"left\">%p</td>\n", ptr);
273 }
274 fprintf(fp, "\t\t</tr>\n");
275 }
276
dot_add_link(struct ListHead * links,void * src,void * dst,const char * label,bool back,const char * colour)277 static void dot_add_link(struct ListHead *links, void *src, void *dst,
278 const char *label, bool back, const char *colour)
279 {
280 if (!src || !dst)
281 return;
282 if (!colour)
283 colour = "#c0c0c0";
284
285 char obj1[16] = { 0 };
286 char obj2[16] = { 0 };
287 char text[256] = { 0 };
288 char lstr[128] = { 0 };
289
290 dot_ptr_name(obj1, sizeof(obj1), src);
291 dot_ptr_name(obj2, sizeof(obj2), dst);
292
293 if (label)
294 snprintf(lstr, sizeof(lstr), "edgetooltip=\"%s\"", label);
295
296 snprintf(text, sizeof(text), "%s -> %s [ %s %s color=\"%s\" ]", obj1, obj2,
297 back ? "dir=back" : "", lstr, colour);
298 mutt_list_insert_tail(links, mutt_str_dup(text));
299 }
300
dot_graph_header(FILE * fp)301 static void dot_graph_header(FILE *fp)
302 {
303 fprintf(fp, "digraph neomutt\n");
304 fprintf(fp, "{\n\n");
305
306 fprintf(fp, "\tgraph [\n");
307 fprintf(fp, "\t\trankdir=\"TB\"\n");
308 fprintf(fp, "\t\tnodesep=\"0.5\"\n");
309 fprintf(fp, "\t\tranksep=\"0.5\"\n");
310 fprintf(fp, "\t];\n");
311 fprintf(fp, "\n");
312 fprintf(fp, "\tnode [\n");
313 fprintf(fp, "\t\tshape=\"plain\"\n");
314 fprintf(fp, "\t];\n");
315 fprintf(fp, "\n");
316 fprintf(fp, "\tedge [\n");
317 fprintf(fp, "\t\tpenwidth=\"4.5\"\n");
318 fprintf(fp, "\t\tarrowsize=\"1.0\"\n");
319 fprintf(fp, "\t\tcolor=\"#c0c0c0\"\n");
320 fprintf(fp, "\t];\n");
321 fprintf(fp, "\n");
322 }
323
dot_graph_footer(FILE * fp,struct ListHead * links)324 static void dot_graph_footer(FILE *fp, struct ListHead *links)
325 {
326 fprintf(fp, "\n");
327 struct ListNode *np = NULL;
328 STAILQ_FOREACH(np, links, entries)
329 {
330 fprintf(fp, "\t%s;\n", np->data);
331 }
332 fprintf(fp, "\n}\n");
333 }
334
dot_object_header(FILE * fp,const void * ptr,const char * name,const char * colour)335 static void dot_object_header(FILE *fp, const void *ptr, const char *name, const char *colour)
336 {
337 char obj[16] = { 0 };
338 dot_ptr_name(obj, sizeof(obj), ptr);
339
340 if (!colour)
341 colour = "#ffff80";
342
343 fprintf(fp, "\t%s [\n", obj);
344 fprintf(fp, "\t\tlabel=<<table cellspacing=\"0\" border=\"1\" rows=\"*\" "
345 "color=\"#d0d0d0\">\n");
346 fprintf(fp, "\t\t<tr>\n");
347 fprintf(fp, "\t\t\t<td border=\"0\" align=\"left\" bgcolor=\"%s\" port=\"top\" colspan=\"3\"><font color=\"#000000\" point-size=\"20\"><b>%s</b></font> <font point-size=\"12\">(%p)</font></td>\n",
348 colour, name, ptr);
349 fprintf(fp, "\t\t</tr>\n");
350 }
351
dot_object_footer(FILE * fp)352 static void dot_object_footer(FILE *fp)
353 {
354 fprintf(fp, "\t\t</table>>\n");
355 fprintf(fp, "\t];\n");
356 fprintf(fp, "\n");
357 }
358
dot_node(FILE * fp,void * ptr,const char * name,const char * colour)359 static void dot_node(FILE *fp, void *ptr, const char *name, const char *colour)
360 {
361 char obj[16] = { 0 };
362 dot_ptr_name(obj, sizeof(obj), ptr);
363
364 fprintf(fp, "\t%s [\n", obj);
365 fprintf(fp, "\t\tlabel=<<table cellspacing=\"0\" border=\"1\" rows=\"*\" "
366 "color=\"#d0d0d0\">\n");
367 fprintf(fp, "\t\t<tr>\n");
368 fprintf(fp, "\t\t\t<td border=\"0\" bgcolor=\"%s\" port=\"top\"><font color=\"#000000\" point-size=\"20\"><b>%s</b></font></td>\n",
369 colour, name);
370 fprintf(fp, "\t\t</tr>\n");
371 dot_object_footer(fp);
372 }
373
dot_node_link(FILE * fp,void * ptr,const char * name,void * link,const char * colour)374 static void dot_node_link(FILE *fp, void *ptr, const char *name, void *link, const char *colour)
375 {
376 char obj[16] = { 0 };
377 dot_ptr_name(obj, sizeof(obj), ptr);
378
379 fprintf(fp, "\t%s [\n", obj);
380 fprintf(fp, "\t\tlabel=<<table cellspacing=\"0\" border=\"1\" rows=\"*\" "
381 "color=\"#d0d0d0\">\n");
382 fprintf(fp, "\t\t<tr>\n");
383 fprintf(fp, "\t\t\t<td border=\"0\" bgcolor=\"%s\" port=\"top\"><font color=\"#000000\" point-size=\"20\"><b>%s</b></font></td>\n",
384 colour, name);
385 fprintf(fp, "\t\t</tr>\n");
386
387 fprintf(fp, "\t\t<tr>\n");
388 fprintf(fp, "\t\t\t<td border=\"0\" align=\"left\" bgcolor=\"%s\">%p</td>\n", colour, link);
389 fprintf(fp, "\t\t</tr>\n");
390
391 dot_object_footer(fp);
392 }
393
dot_path_fs(char * buf,size_t buflen,const char * path)394 static void dot_path_fs(char *buf, size_t buflen, const char *path)
395 {
396 if (!path)
397 {
398 buf[0] = '\0';
399 return;
400 }
401
402 const char *slash = strrchr(path, '/');
403 if (slash)
404 slash++;
405 else
406 slash = path;
407
408 mutt_str_copy(buf, slash, buflen);
409 }
410
dot_path_imap(char * buf,size_t buflen,const char * path)411 static void dot_path_imap(char *buf, size_t buflen, const char *path)
412 {
413 char tmp[1024] = { 0 };
414 mutt_str_copy(tmp, path, sizeof(tmp));
415
416 struct Url *u = url_parse(tmp);
417
418 if (u->path && (u->path[0] != '\0'))
419 mutt_str_copy(buf, u->path, buflen);
420 else
421 snprintf(buf, buflen, "%s:%s", u->host, u->user);
422
423 url_free(&u);
424 }
425
426 #ifndef GV_HIDE_CONFIG
dot_config(FILE * fp,const char * name,int type,struct ConfigSubset * sub,struct ListHead * links)427 static void dot_config(FILE *fp, const char *name, int type,
428 struct ConfigSubset *sub, struct ListHead *links)
429 {
430 if (!sub)
431 return;
432
433 struct Buffer value = mutt_buffer_make(256);
434 dot_object_header(fp, (void *) name, "Config", "#ffff80");
435 dot_type_string(fp, "scope", sub->name, true);
436
437 if (sub->name)
438 {
439 char scope[256];
440 snprintf(scope, sizeof(scope), "%s:", sub->name);
441
442 struct HashElem **list = get_elem_list(sub->cs);
443 for (size_t i = 0; list[i]; i++)
444 {
445 struct HashElem *item = list[i];
446 if ((item->type & type) == 0)
447 continue;
448
449 const char *iname = item->key.strkey;
450 size_t slen = strlen(scope);
451 if (mutt_str_startswith(iname, scope) != 0)
452 {
453 if (strchr(iname + slen, ':'))
454 continue;
455 if ((DTYPE(item->type) == DT_STRING) && (item->type & DT_SENSITIVE))
456 {
457 dot_type_string(fp, iname + slen, "***", true);
458 }
459 else
460 {
461 mutt_buffer_reset(&value);
462 cs_subset_he_string_get(sub, item, &value);
463 dot_type_string(fp, iname + slen, value.data, true);
464 }
465 }
466 }
467 FREE(&list);
468 }
469 else
470 {
471 struct HashElem **list = get_elem_list(sub->cs);
472 int i = 0;
473 for (; list[i]; i++)
474 ; // do nothing
475
476 dot_type_number(fp, "count", i);
477 FREE(&list);
478 }
479
480 dot_object_footer(fp);
481 mutt_buffer_dealloc(&value);
482 }
483 #endif
484
dot_comp(FILE * fp,struct CompressInfo * ci,struct ListHead * links)485 static void dot_comp(FILE *fp, struct CompressInfo *ci, struct ListHead *links)
486 {
487 dot_object_header(fp, ci, "CompressInfo", "#c0c060");
488 dot_type_string(fp, "append", ci->cmd_append, true);
489 dot_type_string(fp, "close", ci->cmd_close, true);
490 dot_type_string(fp, "open", ci->cmd_open, true);
491 dot_object_footer(fp);
492 }
493
dot_mailbox_type(FILE * fp,const char * name,enum MailboxType type)494 static void dot_mailbox_type(FILE *fp, const char *name, enum MailboxType type)
495 {
496 const char *typestr = NULL;
497
498 switch (type)
499 {
500 case MUTT_MBOX:
501 typestr = "MBOX";
502 break;
503 case MUTT_MMDF:
504 typestr = "MMDF";
505 break;
506 case MUTT_MH:
507 typestr = "MH";
508 break;
509 case MUTT_MAILDIR:
510 typestr = "MAILDIR";
511 break;
512 case MUTT_NNTP:
513 typestr = "NNTP";
514 break;
515 case MUTT_IMAP:
516 typestr = "IMAP";
517 break;
518 case MUTT_NOTMUCH:
519 typestr = "NOTMUCH";
520 break;
521 case MUTT_POP:
522 typestr = "POP";
523 break;
524 case MUTT_COMPRESSED:
525 typestr = "COMPRESSED";
526 break;
527 default:
528 typestr = "UNKNOWN";
529 }
530
531 fprintf(fp, "\t\t<tr>\n");
532 fprintf(fp, "\t\t\t<td border=\"0\" align=\"left\">%s</td>\n", name);
533 fprintf(fp, "\t\t\t<td border=\"0\">=</td>\n");
534 fprintf(fp, "\t\t\t<td border=\"0\" align=\"left\">%s</td>\n", typestr);
535 fprintf(fp, "\t\t</tr>\n");
536 }
537
538 #ifndef GV_HIDE_MDATA
dot_mailbox_imap(FILE * fp,struct ImapMboxData * mdata,struct ListHead * links)539 static void dot_mailbox_imap(FILE *fp, struct ImapMboxData *mdata, struct ListHead *links)
540 {
541 dot_object_header(fp, mdata, "ImapMboxData", "#60c060");
542 dot_type_string(fp, "name", mdata->name, true);
543 dot_type_string(fp, "munge_name", mdata->munge_name, true);
544 dot_type_string(fp, "real_name", mdata->real_name, true);
545 dot_object_footer(fp);
546 }
547
dot_mailbox_maildir(FILE * fp,struct MaildirMboxData * mdata,struct ListHead * links)548 static void dot_mailbox_maildir(FILE *fp, struct MaildirMboxData *mdata, struct ListHead *links)
549 {
550 char buf[64] = { 0 };
551
552 dot_object_header(fp, mdata, "MaildirMboxData", "#60c060");
553
554 dot_type_date(buf, sizeof(buf), mdata->mtime_cur.tv_sec);
555 dot_type_string(fp, "mtime_cur", buf, true);
556
557 dot_type_umask(buf, sizeof(buf), mdata->mh_umask);
558 dot_type_string(fp, "mh_umask", buf, true);
559 dot_object_footer(fp);
560 }
561
dot_mailbox_mbox(FILE * fp,struct MboxAccountData * mdata,struct ListHead * links)562 static void dot_mailbox_mbox(FILE *fp, struct MboxAccountData *mdata, struct ListHead *links)
563 {
564 char buf[64] = { 0 };
565
566 dot_object_header(fp, mdata, "MboxAccountData", "#60c060");
567 dot_ptr(fp, "fp", mdata->fp, NULL);
568
569 dot_type_date(buf, sizeof(buf), mdata->atime.tv_sec);
570 dot_type_string(fp, "atime", buf, true);
571
572 dot_object_footer(fp);
573 }
574
dot_mailbox_nntp(FILE * fp,struct NntpMboxData * mdata,struct ListHead * links)575 static void dot_mailbox_nntp(FILE *fp, struct NntpMboxData *mdata, struct ListHead *links)
576 {
577 dot_object_header(fp, mdata, "NntpMboxData", "#60c060");
578 dot_type_string(fp, "group", mdata->group, true);
579 dot_type_string(fp, "desc", mdata->desc, true);
580
581 dot_type_number(fp, "first_message", mdata->first_message);
582 dot_type_number(fp, "last_message", mdata->last_message);
583 dot_type_number(fp, "last_loaded", mdata->last_loaded);
584 dot_type_number(fp, "last_cached", mdata->last_cached);
585 dot_type_number(fp, "unread", mdata->unread);
586
587 dot_type_bool(fp, "subscribed", mdata->subscribed);
588 dot_type_bool(fp, "has_new_mail", mdata->has_new_mail);
589 dot_type_bool(fp, "allowed", mdata->allowed);
590 dot_type_bool(fp, "deleted", mdata->deleted);
591
592 dot_object_footer(fp);
593 }
594
dot_mailbox_notmuch(FILE * fp,struct NmMboxData * mdata,struct ListHead * links)595 static void dot_mailbox_notmuch(FILE *fp, struct NmMboxData *mdata, struct ListHead *links)
596 {
597 dot_object_header(fp, mdata, "NmMboxData", "#60c060");
598 dot_type_number(fp, "db_limit", mdata->db_limit);
599 dot_object_footer(fp);
600 }
601
dot_mailbox_pop(FILE * fp,struct PopAccountData * adata,struct ListHead * links)602 static void dot_mailbox_pop(FILE *fp, struct PopAccountData *adata, struct ListHead *links)
603 {
604 dot_object_header(fp, adata, "PopAccountData", "#60c060");
605 dot_ptr(fp, "conn", adata->conn, "#ff8080");
606 dot_object_footer(fp);
607 }
608 #endif
609
dot_mailbox(FILE * fp,struct Mailbox * m,struct ListHead * links)610 static void dot_mailbox(FILE *fp, struct Mailbox *m, struct ListHead *links)
611 {
612 char buf[64] = { 0 };
613
614 dot_object_header(fp, m, "Mailbox", "#80ff80");
615 dot_mailbox_type(fp, "type", m->type);
616 dot_type_string(fp, "name", m->name, false);
617
618 if ((m->type == MUTT_IMAP) || (m->type == MUTT_POP))
619 {
620 dot_path_imap(buf, sizeof(buf), mutt_buffer_string(&m->pathbuf));
621 dot_type_string(fp, "pathbuf", buf, true);
622 dot_path_imap(buf, sizeof(buf), m->realpath);
623 dot_type_string(fp, "realpath", buf, true);
624 }
625 else
626 {
627 dot_path_fs(buf, sizeof(buf), mutt_buffer_string(&m->pathbuf));
628 dot_type_string(fp, "pathbuf", buf, true);
629 dot_path_fs(buf, sizeof(buf), m->realpath);
630 dot_type_string(fp, "realpath", buf, true);
631 }
632
633 #ifdef GV_HIDE_MDATA
634 dot_ptr(fp, "mdata", m->mdata, NULL);
635 #endif
636 dot_ptr(fp, "account", m->account, "#80ffff");
637 dot_type_number(fp, "opened", m->opened);
638
639 dot_type_number(fp, "msg_count", m->msg_count);
640 // dot_type_number(fp, "msg_unread", m->msg_unread);
641 // dot_type_number(fp, "msg_flagged", m->msg_flagged);
642 // dot_type_number(fp, "msg_new", m->msg_new);
643 // dot_type_number(fp, "msg_deleted", m->msg_deleted);
644 // dot_type_number(fp, "msg_tagged", m->msg_tagged);
645
646 dot_ptr(fp, "emails", m->emails, NULL);
647 dot_type_number(fp, "email_max", m->email_max);
648 dot_ptr(fp, "v2r", m->v2r, NULL);
649 dot_type_number(fp, "vcount", m->vcount);
650
651 dot_object_footer(fp);
652
653 // dot_add_link(links, m, m->mdata, false, NULL);
654
655 #ifndef GV_HIDE_MDATA
656 if (m->mdata)
657 {
658 if (m->type == MUTT_MAILDIR)
659 dot_mailbox_maildir(fp, m->mdata, links);
660 else if (m->type == MUTT_IMAP)
661 dot_mailbox_imap(fp, m->mdata, links);
662 else if (m->type == MUTT_POP)
663 dot_mailbox_pop(fp, m->mdata, links);
664 else if (m->type == MUTT_MBOX)
665 dot_mailbox_mbox(fp, m->mdata, links);
666 else if (m->type == MUTT_NNTP)
667 dot_mailbox_nntp(fp, m->mdata, links);
668 else if (m->type == MUTT_NOTMUCH)
669 dot_mailbox_notmuch(fp, m->mdata, links);
670
671 dot_add_link(links, m, m->mdata, "Mailbox->mdata", false, NULL);
672 }
673 #endif
674
675 if (m->compress_info)
676 {
677 dot_comp(fp, m->compress_info, links);
678 dot_add_link(links, m, m->compress_info, "Mailbox->compress_info", false, NULL);
679 }
680
681 #ifndef GV_HIDE_CONFIG
682 if (m->name)
683 {
684 dot_config(fp, m->name, DT_INHERIT_MBOX, m->sub, links);
685 dot_add_link(links, m, m->name, "Mailbox Config", false, NULL);
686 }
687 #endif
688 }
689
dot_mailbox_node(FILE * fp,struct MailboxNode * mn,struct ListHead * links)690 static void dot_mailbox_node(FILE *fp, struct MailboxNode *mn, struct ListHead *links)
691 {
692 dot_node(fp, mn, "MN", "#80ff80");
693
694 dot_mailbox(fp, mn->mailbox, links);
695
696 dot_add_link(links, mn, mn->mailbox, "MailboxNode->mailbox", false, NULL);
697
698 struct Buffer buf;
699 mutt_buffer_init(&buf);
700
701 char name[256] = { 0 };
702 mutt_buffer_addstr(&buf, "{ rank=same ");
703
704 dot_ptr_name(name, sizeof(name), mn);
705 mutt_buffer_add_printf(&buf, "%s ", name);
706
707 dot_ptr_name(name, sizeof(name), mn->mailbox);
708 mutt_buffer_add_printf(&buf, "%s ", name);
709
710 #ifndef GV_HIDE_MDATA
711 if (mn->mailbox->mdata)
712 {
713 dot_ptr_name(name, sizeof(name), mn->mailbox->mdata);
714 mutt_buffer_add_printf(&buf, "%s ", name);
715 }
716 #endif
717
718 #ifndef GV_HIDE_CONFIG
719 if (mn->mailbox->name)
720 {
721 dot_ptr_name(name, sizeof(name), mn->mailbox->name);
722 mutt_buffer_add_printf(&buf, "%s ", name);
723 }
724 #endif
725
726 mutt_buffer_addstr(&buf, "}");
727
728 mutt_list_insert_tail(links, mutt_str_dup(buf.data));
729 mutt_buffer_dealloc(&buf);
730 }
731
dot_mailbox_list(FILE * fp,struct MailboxList * ml,struct ListHead * links,bool abbr)732 static void dot_mailbox_list(FILE *fp, struct MailboxList *ml, struct ListHead *links, bool abbr)
733 {
734 struct MailboxNode *prev = NULL;
735 struct MailboxNode *np = NULL;
736 STAILQ_FOREACH(np, ml, entries)
737 {
738 if (abbr)
739 dot_node_link(fp, np, "MN", np->mailbox, "#80ff80");
740 else
741 dot_mailbox_node(fp, np, links);
742 if (prev)
743 dot_add_link(links, prev, np, "MailboxNode->next", false, NULL);
744 prev = np;
745 }
746 }
747
748 #ifndef GV_HIDE_ADATA
dot_connection(FILE * fp,struct Connection * c,struct ListHead * links)749 static void dot_connection(FILE *fp, struct Connection *c, struct ListHead *links)
750 {
751 dot_object_header(fp, c, "Connection", "#ff8080");
752 // dot_ptr(fp, "sockdata", c->sockdata, "#60c0c0");
753 dot_type_number(fp, "fd", c->fd);
754 dot_object_footer(fp);
755
756 dot_object_header(fp, c->inbuf, "ConnAccount", "#ff8080");
757 dot_type_string(fp, "user", c->account.user, true);
758 dot_type_string(fp, "host", c->account.host, true);
759 dot_type_number(fp, "port", c->account.port);
760 dot_object_footer(fp);
761
762 dot_add_link(links, c, c->inbuf, "Connection.ConnAccount", false, NULL);
763 }
764
dot_account_imap(FILE * fp,struct ImapAccountData * adata,struct ListHead * links)765 static void dot_account_imap(FILE *fp, struct ImapAccountData *adata, struct ListHead *links)
766 {
767 dot_object_header(fp, adata, "ImapAccountData", "#60c0c0");
768 // dot_type_string(fp, "mbox_name", adata->mbox_name, true);
769 // dot_type_string(fp, "login", adata->conn->account.login, true);
770 dot_type_string(fp, "user", adata->conn->account.user, true);
771 dot_type_string(fp, "pass", adata->conn->account.pass[0] ? "***" : "", true);
772 dot_type_number(fp, "port", adata->conn->account.port);
773 // dot_ptr(fp, "conn", adata->conn, "#ff8080");
774 dot_type_bool(fp, "unicode", adata->unicode);
775 dot_type_bool(fp, "qresync", adata->qresync);
776 dot_type_char(fp, "seqid", adata->seqid);
777 dot_ptr(fp, "mailbox", adata->mailbox, "#80ff80");
778 dot_object_footer(fp);
779
780 if (adata->conn)
781 {
782 dot_connection(fp, adata->conn, links);
783 dot_add_link(links, adata, adata->conn, "ImapAccountData->conn", false, NULL);
784 }
785 }
786
dot_account_mbox(FILE * fp,struct MboxAccountData * adata,struct ListHead * links)787 static void dot_account_mbox(FILE *fp, struct MboxAccountData *adata, struct ListHead *links)
788 {
789 char buf[64] = { 0 };
790
791 dot_object_header(fp, adata, "MboxAccountData", "#60c0c0");
792 dot_ptr(fp, "fp", adata->fp, NULL);
793
794 dot_type_date(buf, sizeof(buf), adata->atime.tv_sec);
795 dot_type_string(fp, "atime", buf, true);
796 dot_type_bool(fp, "locked", adata->locked);
797 dot_type_bool(fp, "append", adata->append);
798
799 dot_object_footer(fp);
800 }
801
dot_account_nntp(FILE * fp,struct NntpAccountData * adata,struct ListHead * links)802 static void dot_account_nntp(FILE *fp, struct NntpAccountData *adata, struct ListHead *links)
803 {
804 dot_object_header(fp, adata, "NntpAccountData", "#60c0c0");
805 dot_type_number(fp, "groups_num", adata->groups_num);
806
807 dot_type_bool(fp, "hasCAPABILITIES", adata->hasCAPABILITIES);
808 dot_type_bool(fp, "hasSTARTTLS", adata->hasSTARTTLS);
809 dot_type_bool(fp, "hasDATE", adata->hasDATE);
810 dot_type_bool(fp, "hasLIST_NEWSGROUPS", adata->hasLIST_NEWSGROUPS);
811 dot_type_bool(fp, "hasXGTITLE", adata->hasXGTITLE);
812 dot_type_bool(fp, "hasLISTGROUP", adata->hasLISTGROUP);
813 dot_type_bool(fp, "hasLISTGROUPrange", adata->hasLISTGROUPrange);
814 dot_type_bool(fp, "hasOVER", adata->hasOVER);
815 dot_type_bool(fp, "hasXOVER", adata->hasXOVER);
816 dot_type_bool(fp, "cacheable", adata->cacheable);
817 dot_type_bool(fp, "newsrc_modified", adata->newsrc_modified);
818
819 dot_type_string(fp, "authenticators", adata->authenticators, true);
820 dot_type_string(fp, "overview_fmt", adata->overview_fmt, true);
821 dot_type_string(fp, "newsrc_file", adata->newsrc_file, true);
822 dot_type_file(fp, "newsrc_fp", adata->fp_newsrc);
823
824 dot_type_number(fp, "groups_num", adata->groups_num);
825 dot_type_number(fp, "groups_max", adata->groups_max);
826
827 char buf[128];
828 dot_type_date(buf, sizeof(buf), adata->mtime);
829 dot_type_string(fp, "mtime", buf, true);
830 dot_type_date(buf, sizeof(buf), adata->newgroups_time);
831 dot_type_string(fp, "newgroups_time", buf, true);
832 dot_type_date(buf, sizeof(buf), adata->check_time);
833 dot_type_string(fp, "check_time", buf, true);
834
835 dot_object_footer(fp);
836
837 if (adata->conn)
838 {
839 dot_connection(fp, adata->conn, links);
840 dot_add_link(links, adata, adata->conn, "NntpAccountData->conn", false, NULL);
841 }
842 }
843
dot_account_notmuch(FILE * fp,struct NmAccountData * adata,struct ListHead * links)844 static void dot_account_notmuch(FILE *fp, struct NmAccountData *adata, struct ListHead *links)
845 {
846 dot_object_header(fp, adata, "NmAccountData", "#60c0c0");
847 dot_ptr(fp, "db", adata->db, NULL);
848 dot_object_footer(fp);
849 }
850
dot_account_pop(FILE * fp,struct PopAccountData * adata,struct ListHead * links)851 static void dot_account_pop(FILE *fp, struct PopAccountData *adata, struct ListHead *links)
852 {
853 char buf[64] = { 0 };
854
855 dot_object_header(fp, adata, "PopAccountData", "#60c0c0");
856
857 dot_type_date(buf, sizeof(buf), adata->check_time);
858 dot_type_string(fp, "check_time", buf, true);
859
860 dot_type_string(fp, "login", adata->conn->account.login, true);
861 dot_type_string(fp, "user", adata->conn->account.user, true);
862 dot_type_string(fp, "pass", adata->conn->account.pass[0] ? "***" : "", true);
863 dot_type_number(fp, "port", adata->conn->account.port);
864 // dot_ptr(fp, "conn", adata->conn, "#ff8080");
865 dot_object_footer(fp);
866
867 if (adata->conn)
868 {
869 dot_connection(fp, adata->conn, links);
870 dot_add_link(links, adata, adata->conn, "PopAccountData->conn", false, NULL);
871 }
872 }
873 #endif
874
dot_account(FILE * fp,struct Account * a,struct ListHead * links)875 static void dot_account(FILE *fp, struct Account *a, struct ListHead *links)
876 {
877 dot_object_header(fp, a, "Account", "#80ffff");
878 dot_mailbox_type(fp, "type", a->type);
879 dot_type_string(fp, "name", a->name, true);
880 // dot_ptr(fp, "adata", a->adata, "#60c0c0");
881 dot_object_footer(fp);
882
883 #ifndef GV_HIDE_ADATA
884 if (a->adata)
885 {
886 if (a->type == MUTT_IMAP)
887 dot_account_imap(fp, a->adata, links);
888 else if (a->type == MUTT_POP)
889 dot_account_pop(fp, a->adata, links);
890 else if (a->type == MUTT_MBOX)
891 dot_account_mbox(fp, a->adata, links);
892 else if (a->type == MUTT_NNTP)
893 dot_account_nntp(fp, a->adata, links);
894 else if (a->type == MUTT_NOTMUCH)
895 dot_account_notmuch(fp, a->adata, links);
896
897 dot_add_link(links, a, a->adata, "Account->adata", false, NULL);
898 }
899 #endif
900
901 #ifndef GV_HIDE_CONFIG
902 if (a->name)
903 {
904 dot_config(fp, a->name, DT_INHERIT_ACC, a->sub, links);
905 dot_add_link(links, a, a->name, "Config", false, NULL);
906
907 char name[256] = { 0 };
908 struct Buffer buf;
909 mutt_buffer_init(&buf);
910
911 mutt_buffer_addstr(&buf, "{ rank=same ");
912
913 dot_ptr_name(name, sizeof(name), a);
914 mutt_buffer_add_printf(&buf, "%s ", name);
915
916 dot_ptr_name(name, sizeof(name), a->name);
917 mutt_buffer_add_printf(&buf, "%s ", name);
918
919 mutt_buffer_addstr(&buf, "}");
920 mutt_list_insert_tail(links, mutt_str_dup(buf.data));
921 mutt_buffer_dealloc(&buf);
922 }
923 #endif
924
925 struct MailboxNode *first = STAILQ_FIRST(&a->mailboxes);
926 dot_add_link(links, a, first, "Account->mailboxes", false, NULL);
927 dot_mailbox_list(fp, &a->mailboxes, links, false);
928 }
929
dot_account_list(FILE * fp,struct AccountList * al,struct ListHead * links)930 static void dot_account_list(FILE *fp, struct AccountList *al, struct ListHead *links)
931 {
932 struct Account *prev = NULL;
933 struct Account *np = NULL;
934 TAILQ_FOREACH(np, al, entries)
935 {
936 #ifdef GV_HIDE_MBOX
937 if (np->type == MUTT_MBOX)
938 continue;
939 #endif
940 dot_account(fp, np, links);
941 if (prev)
942 dot_add_link(links, prev, np, "Account->next", false, NULL);
943
944 prev = np;
945 }
946 }
947
948 #ifndef GV_HIDE_CONTEXT
dot_context(FILE * fp,struct Context * ctx,struct ListHead * links)949 static void dot_context(FILE *fp, struct Context *ctx, struct ListHead *links)
950 {
951 dot_object_header(fp, ctx, "Context", "#ff80ff");
952 dot_ptr(fp, "mailbox", ctx->mailbox, "#80ff80");
953 #ifdef GV_HIDE_CONTEXT_CONTENTS
954 dot_type_number(fp, "vsize", ctx->vsize);
955 dot_type_string(fp, "pattern", ctx->pattern, true);
956 dot_type_bool(fp, "collapsed", ctx->collapsed);
957 #endif
958 dot_object_footer(fp);
959 }
960 #endif
961
dump_graphviz(const char * title,struct Context * ctx)962 void dump_graphviz(const char *title, struct Context *ctx)
963 {
964 char name[256] = { 0 };
965 struct ListHead links = STAILQ_HEAD_INITIALIZER(links);
966
967 time_t now = time(NULL);
968 if (title)
969 {
970 char date[128];
971 mutt_date_localtime_format(date, sizeof(date), "%R", now);
972 snprintf(name, sizeof(name), "%s-%s.gv", date, title);
973 }
974 else
975 {
976 mutt_date_localtime_format(name, sizeof(name), "%R.gv", now);
977 }
978
979 umask(022);
980 FILE *fp = fopen(name, "w");
981 if (!fp)
982 return;
983
984 dot_graph_header(fp);
985
986 #ifndef GV_HIDE_NEOMUTT
987 dot_node(fp, NeoMutt, "NeoMutt", "#ffa500");
988 dot_add_link(&links, NeoMutt, TAILQ_FIRST(&NeoMutt->accounts),
989 "NeoMutt->accounts", false, NULL);
990 #ifndef GV_HIDE_CONFIG
991 dot_config(fp, (const char *) NeoMutt->sub, 0, NeoMutt->sub, &links);
992 dot_add_link(&links, NeoMutt, NeoMutt->sub, "NeoMutt Config", false, NULL);
993 struct Buffer buf = mutt_buffer_make(256);
994 char obj1[16] = { 0 };
995 char obj2[16] = { 0 };
996 dot_ptr_name(obj1, sizeof(obj1), NeoMutt);
997 dot_ptr_name(obj2, sizeof(obj2), NeoMutt->sub);
998 mutt_buffer_printf(&buf, "{ rank=same %s %s }", obj1, obj2);
999 mutt_list_insert_tail(&links, mutt_str_dup(mutt_buffer_string(&buf)));
1000 mutt_buffer_dealloc(&buf);
1001 #endif
1002 #endif
1003
1004 dot_account_list(fp, &NeoMutt->accounts, &links);
1005
1006 #ifndef GV_HIDE_CONTEXT
1007 if (ctx)
1008 dot_context(fp, ctx, &links);
1009
1010 #ifndef GV_HIDE_NEOMUTT
1011 /* Globals */
1012 fprintf(fp, "\t{ rank=same ");
1013 if (ctx)
1014 {
1015 dot_ptr_name(name, sizeof(name), ctx);
1016 fprintf(fp, "%s ", name);
1017 }
1018 dot_ptr_name(name, sizeof(name), NeoMutt);
1019 fprintf(fp, "%s ", name);
1020 fprintf(fp, "}\n");
1021 #endif
1022 #endif
1023
1024 fprintf(fp, "\t{ rank=same ");
1025 struct Account *np = NULL;
1026 TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
1027 {
1028 #ifdef GV_HIDE_MBOX
1029 if (np->type == MUTT_MBOX)
1030 continue;
1031 #endif
1032 dot_ptr_name(name, sizeof(name), np);
1033 fprintf(fp, "%s ", name);
1034 }
1035 fprintf(fp, "}\n");
1036
1037 dot_graph_footer(fp, &links);
1038 fclose(fp);
1039 mutt_list_free(&links);
1040 }
1041
dot_parameter_list(FILE * fp,const char * name,const struct ParameterList * pl)1042 static void dot_parameter_list(FILE *fp, const char *name, const struct ParameterList *pl)
1043 {
1044 if (!pl)
1045 return;
1046 if (TAILQ_EMPTY(pl))
1047 return;
1048
1049 dot_object_header(fp, pl, "ParameterList", "#00ff00");
1050
1051 struct Parameter *np = NULL;
1052 TAILQ_FOREACH(np, pl, entries)
1053 {
1054 dot_type_string(fp, np->attribute, np->value, false);
1055 }
1056
1057 dot_object_footer(fp);
1058 }
1059
dot_content(FILE * fp,struct Content * cont,struct ListHead * links)1060 static void dot_content(FILE *fp, struct Content *cont, struct ListHead *links)
1061 {
1062 struct Buffer buf = mutt_buffer_make(256);
1063
1064 dot_object_header(fp, cont, "Content", "#800080");
1065
1066 dot_type_number(fp, "hibin", cont->hibin);
1067 dot_type_number(fp, "lobin", cont->lobin);
1068 dot_type_number(fp, "nulbin", cont->nulbin);
1069 dot_type_number(fp, "crlf", cont->crlf);
1070 dot_type_number(fp, "ascii", cont->ascii);
1071 dot_type_number(fp, "linemax", cont->linemax);
1072
1073 #define ADD_BOOL(F) add_flag(&buf, cont->F, #F)
1074 ADD_BOOL(space);
1075 ADD_BOOL(binary);
1076 ADD_BOOL(from);
1077 ADD_BOOL(dot);
1078 ADD_BOOL(cr);
1079 #undef ADD_BOOL
1080
1081 dot_object_footer(fp);
1082
1083 mutt_buffer_dealloc(&buf);
1084 }
1085
dot_attach_ptr(FILE * fp,struct AttachPtr * aptr,struct ListHead * links)1086 void dot_attach_ptr(FILE *fp, struct AttachPtr *aptr, struct ListHead *links)
1087 {
1088 if (!aptr)
1089 return;
1090
1091 struct Buffer buf = mutt_buffer_make(256);
1092
1093 dot_object_header(fp, aptr, "AttachPtr", "#ff0000");
1094
1095 dot_type_file(fp, "fp", aptr->fp);
1096
1097 dot_type_string(fp, "parent_type", get_content_type(aptr->parent_type), false);
1098
1099 dot_type_number(fp, "level", aptr->level);
1100 dot_type_number(fp, "num", aptr->num);
1101
1102 dot_type_bool(fp, "unowned", aptr->unowned);
1103 dot_type_bool(fp, "decrypted", aptr->decrypted);
1104
1105 dot_object_footer(fp);
1106
1107 dot_add_link(links, aptr->body, aptr, "AttachPtr->body", true, NULL);
1108
1109 mutt_buffer_dealloc(&buf);
1110 }
1111
dot_body(FILE * fp,struct Body * b,struct ListHead * links,bool link_next)1112 static void dot_body(FILE *fp, struct Body *b, struct ListHead *links, bool link_next)
1113 {
1114 struct Buffer buf = mutt_buffer_make(256);
1115
1116 dot_object_header(fp, b, "Body", "#2020ff");
1117
1118 char file[256];
1119 dot_path_fs(file, sizeof(file), b->filename);
1120 dot_type_string(fp, "file", file, false);
1121
1122 dot_type_string(fp, "charset", b->charset, false);
1123 dot_type_string(fp, "description", b->description, false);
1124 dot_type_string(fp, "d_filename", b->d_filename, false);
1125 dot_type_string(fp, "form_name", b->form_name, false);
1126 dot_type_string(fp, "language", b->language, false);
1127 dot_type_string(fp, "subtype", b->subtype, false);
1128 dot_type_string(fp, "xtype", b->xtype, false);
1129
1130 dot_type_string(fp, "type", get_content_type(b->type), true);
1131 dot_type_string(fp, "encoding", get_content_encoding(b->encoding), true);
1132 dot_type_string(fp, "disposition", get_content_disposition(b->disposition), true);
1133
1134 if (b->stamp != 0)
1135 {
1136 char arr[64];
1137 dot_type_date(arr, sizeof(arr), b->stamp);
1138 dot_type_string(fp, "stamp", arr, true);
1139 }
1140
1141 #define ADD_BOOL(F) add_flag(&buf, b->F, #F)
1142 ADD_BOOL(attach_qualifies);
1143 ADD_BOOL(badsig);
1144 ADD_BOOL(collapsed);
1145 ADD_BOOL(deleted);
1146 ADD_BOOL(force_charset);
1147 ADD_BOOL(goodsig);
1148 #ifdef USE_AUTOCRYPT
1149 ADD_BOOL(is_autocrypt);
1150 #endif
1151 ADD_BOOL(noconv);
1152 ADD_BOOL(tagged);
1153 ADD_BOOL(unlink);
1154 ADD_BOOL(use_disp);
1155 ADD_BOOL(warnsig);
1156 #undef ADD_BOOL
1157 dot_type_string(fp, "bools",
1158 mutt_buffer_is_empty(&buf) ? "[NONE]" : mutt_buffer_string(&buf), true);
1159
1160 dot_type_number(fp, "attach_count", b->attach_count);
1161 dot_type_number(fp, "hdr_offset", b->hdr_offset);
1162 dot_type_number(fp, "length", b->length);
1163 dot_type_number(fp, "offset", b->offset);
1164
1165 dot_ptr(fp, "aptr", b->aptr, "#3bcbc4");
1166 dot_object_footer(fp);
1167
1168 if (!TAILQ_EMPTY(&b->parameter))
1169 {
1170 dot_parameter_list(fp, "parameter", &b->parameter);
1171 dot_add_link(links, b, &b->parameter, "Body->mime_headers", false, NULL);
1172 }
1173
1174 if (b->mime_headers)
1175 {
1176 dot_envelope(fp, b->mime_headers, links);
1177 dot_add_link(links, b, b->mime_headers, "Body->mime_headers", false, NULL);
1178 }
1179
1180 if (b->email)
1181 {
1182 dot_email(fp, b->email, links);
1183 dot_add_link(links, b, b->email, "Body->email", false, NULL);
1184 }
1185
1186 if (b->parts)
1187 {
1188 if (!b->email)
1189 dot_body(fp, b->parts, links, true);
1190 dot_add_link(links, b, b->parts, "Body->parts", false, "#ff0000");
1191 }
1192
1193 if (b->next && link_next)
1194 {
1195 char name[256] = { 0 };
1196 mutt_buffer_reset(&buf);
1197
1198 mutt_buffer_addstr(&buf, "{ rank=same ");
1199
1200 dot_ptr_name(name, sizeof(name), b);
1201 mutt_buffer_add_printf(&buf, "%s ", name);
1202
1203 for (; b->next; b = b->next)
1204 {
1205 dot_body(fp, b->next, links, false);
1206 dot_add_link(links, b, b->next, "Body->next", false, "#008000");
1207
1208 dot_ptr_name(name, sizeof(name), b->next);
1209 mutt_buffer_add_printf(&buf, "%s ", name);
1210 }
1211
1212 mutt_buffer_addstr(&buf, "}");
1213 mutt_list_insert_tail(links, mutt_str_dup(buf.data));
1214 }
1215 else
1216 {
1217 if (b->content)
1218 {
1219 dot_content(fp, b->content, links);
1220 dot_add_link(links, b, b->content, "Body->content", false, NULL);
1221 }
1222
1223 // if (b->aptr)
1224 // {
1225 // dot_attach_ptr(fp, b->aptr, links);
1226 // dot_add_link(links, b, b->aptr, "Body->aptr", false, NULL);
1227 // }
1228 }
1229
1230 mutt_buffer_dealloc(&buf);
1231 }
1232
dot_list_head(FILE * fp,const char * name,const struct ListHead * list)1233 static void dot_list_head(FILE *fp, const char *name, const struct ListHead *list)
1234 {
1235 if (!list || !name)
1236 return;
1237 if (STAILQ_EMPTY(list))
1238 return;
1239
1240 struct Buffer buf = mutt_buffer_make(256);
1241
1242 struct ListNode *np = NULL;
1243 STAILQ_FOREACH(np, list, entries)
1244 {
1245 if (!mutt_buffer_is_empty(&buf))
1246 mutt_buffer_addch(&buf, ',');
1247 mutt_buffer_addstr(&buf, np->data);
1248 }
1249
1250 dot_type_string(fp, name, mutt_buffer_string(&buf), false);
1251 }
1252
dot_addr_list(FILE * fp,const char * name,const struct AddressList * al,struct ListHead * links)1253 static void dot_addr_list(FILE *fp, const char *name,
1254 const struct AddressList *al, struct ListHead *links)
1255 {
1256 if (!al)
1257 return;
1258 if (TAILQ_EMPTY(al))
1259 return;
1260
1261 char buf[1024] = { 0 };
1262
1263 mutt_addrlist_write(al, buf, sizeof(buf), true);
1264 dot_type_string(fp, name, buf, false);
1265 }
1266
dot_envelope(FILE * fp,struct Envelope * env,struct ListHead * links)1267 static void dot_envelope(FILE *fp, struct Envelope *env, struct ListHead *links)
1268 {
1269 struct Buffer buf = mutt_buffer_make(256);
1270
1271 dot_object_header(fp, env, "Envelope", "#ffff00");
1272
1273 #define ADD_FLAG(F) add_flag(&buf, (env->changed & F), #F)
1274 ADD_FLAG(MUTT_ENV_CHANGED_IRT);
1275 ADD_FLAG(MUTT_ENV_CHANGED_REFS);
1276 ADD_FLAG(MUTT_ENV_CHANGED_XLABEL);
1277 ADD_FLAG(MUTT_ENV_CHANGED_SUBJECT);
1278 #undef ADD_BOOL
1279 dot_type_string(fp, "changed",
1280 mutt_buffer_is_empty(&buf) ? "[NONE]" : mutt_buffer_string(&buf), true);
1281
1282 #define ADDR_LIST(AL) dot_addr_list(fp, #AL, &env->AL, links)
1283 ADDR_LIST(return_path);
1284 ADDR_LIST(from);
1285 ADDR_LIST(to);
1286 ADDR_LIST(cc);
1287 ADDR_LIST(bcc);
1288 ADDR_LIST(sender);
1289 ADDR_LIST(reply_to);
1290 ADDR_LIST(mail_followup_to);
1291 ADDR_LIST(x_original_to);
1292 #undef ADDR_LIST
1293
1294 dot_type_string(fp, "date", env->date, false);
1295 dot_type_string(fp, "disp_subj", env->disp_subj, false);
1296 dot_type_string(fp, "followup_to", env->followup_to, false);
1297 dot_type_string(fp, "list_post", env->list_post, false);
1298 dot_type_string(fp, "list_subscribe", env->list_subscribe, false);
1299 dot_type_string(fp, "list_unsubscribe", env->list_unsubscribe, false);
1300 dot_type_string(fp, "message_id", env->message_id, false);
1301 dot_type_string(fp, "newsgroups", env->newsgroups, false);
1302 dot_type_string(fp, "organization", env->organization, false);
1303 dot_type_string(fp, "real_subj", env->real_subj, false);
1304 dot_type_string(fp, "spam", mutt_buffer_string(&env->spam), false);
1305 dot_type_string(fp, "subject", env->subject, false);
1306 dot_type_string(fp, "supersedes", env->supersedes, false);
1307 dot_type_string(fp, "xref", env->xref, false);
1308 dot_type_string(fp, "x_comment_to", env->x_comment_to, false);
1309 dot_type_string(fp, "x_label", env->x_label, false);
1310
1311 if (0)
1312 {
1313 dot_list_head(fp, "references", &env->references);
1314 dot_list_head(fp, "in_reply_to", &env->in_reply_to);
1315 dot_list_head(fp, "userhdrs", &env->userhdrs);
1316 }
1317
1318 #ifdef USE_AUTOCRYPT
1319 dot_ptr(fp, "autocrypt", env->autocrypt, NULL);
1320 dot_ptr(fp, "autocrypt_gossip", env->autocrypt_gossip, NULL);
1321 #endif
1322
1323 dot_object_footer(fp);
1324
1325 mutt_buffer_dealloc(&buf);
1326 }
1327
dot_email(FILE * fp,struct Email * e,struct ListHead * links)1328 static void dot_email(FILE *fp, struct Email *e, struct ListHead *links)
1329 {
1330 struct Buffer buf = mutt_buffer_make(256);
1331 char arr[256];
1332
1333 dot_object_header(fp, e, "Email", "#ff80ff");
1334
1335 dot_type_string(fp, "path", e->path, true);
1336
1337 #define ADD_BOOL(F) add_flag(&buf, e->F, #F)
1338 ADD_BOOL(active);
1339 ADD_BOOL(attach_del);
1340 ADD_BOOL(attach_valid);
1341 ADD_BOOL(changed);
1342 ADD_BOOL(collapsed);
1343 ADD_BOOL(deleted);
1344 ADD_BOOL(display_subject);
1345 ADD_BOOL(expired);
1346 ADD_BOOL(flagged);
1347 ADD_BOOL(matched);
1348 ADD_BOOL(mime);
1349 ADD_BOOL(old);
1350 ADD_BOOL(purge);
1351 ADD_BOOL(quasi_deleted);
1352 ADD_BOOL(read);
1353 ADD_BOOL(recip_valid);
1354 ADD_BOOL(replied);
1355 ADD_BOOL(searched);
1356 ADD_BOOL(subject_changed);
1357 ADD_BOOL(superseded);
1358 ADD_BOOL(tagged);
1359 ADD_BOOL(threaded);
1360 ADD_BOOL(trash);
1361 ADD_BOOL(visible);
1362 #undef ADD_BOOL
1363 dot_type_string(fp, "bools",
1364 mutt_buffer_is_empty(&buf) ? "[NONE]" : mutt_buffer_string(&buf), true);
1365
1366 mutt_buffer_reset(&buf);
1367 #define ADD_BOOL(F) add_flag(&buf, (e->security & F), #F)
1368 ADD_BOOL(SEC_ENCRYPT);
1369 ADD_BOOL(SEC_SIGN);
1370 ADD_BOOL(SEC_GOODSIGN);
1371 ADD_BOOL(SEC_BADSIGN);
1372 ADD_BOOL(SEC_PARTSIGN);
1373 ADD_BOOL(SEC_SIGNOPAQUE);
1374 ADD_BOOL(SEC_KEYBLOCK);
1375 ADD_BOOL(SEC_INLINE);
1376 ADD_BOOL(SEC_OPPENCRYPT);
1377 ADD_BOOL(SEC_AUTOCRYPT);
1378 ADD_BOOL(SEC_AUTOCRYPT_OVERRIDE);
1379 ADD_BOOL(APPLICATION_PGP);
1380 ADD_BOOL(APPLICATION_SMIME);
1381 ADD_BOOL(PGP_TRADITIONAL_CHECKED);
1382 #undef ADD_BOOL
1383 dot_type_string(fp, "security",
1384 mutt_buffer_is_empty(&buf) ? "[NONE]" : mutt_buffer_string(&buf), true);
1385
1386 dot_type_number(fp, "num_hidden", e->num_hidden);
1387 dot_type_number(fp, "offset", e->offset);
1388 dot_type_number(fp, "lines", e->lines);
1389 dot_type_number(fp, "index", e->index);
1390 dot_type_number(fp, "msgno", e->msgno);
1391 dot_type_number(fp, "vnum", e->vnum);
1392 dot_type_number(fp, "score", e->score);
1393 dot_type_number(fp, "attach_total", e->attach_total);
1394
1395 // struct MaildirEmailData *edata = maildir_edata_get(e);
1396 // if (edata)
1397 // dot_type_string(fp, "maildir_flags", edata->maildir_flags, false);
1398
1399 if (e->date_sent != 0)
1400 {
1401 char zone[32];
1402 dot_type_date(arr, sizeof(arr), e->date_sent);
1403 snprintf(zone, sizeof(zone), " (%c%02u%02u)", e->zoccident ? '-' : '+',
1404 e->zhours, e->zminutes);
1405 mutt_str_cat(arr, sizeof(arr), zone);
1406 dot_type_string(fp, "date_sent", arr, false);
1407 }
1408
1409 if (e->received != 0)
1410 {
1411 dot_type_date(arr, sizeof(arr), e->received);
1412 dot_type_string(fp, "received", arr, false);
1413 }
1414
1415 dot_object_footer(fp);
1416
1417 if (e->body)
1418 {
1419 dot_body(fp, e->body, links, true);
1420 dot_add_link(links, e, e->body, "Email->body", false, NULL);
1421 }
1422
1423 if (e->env)
1424 {
1425 dot_envelope(fp, e->env, links);
1426 dot_add_link(links, e, e->env, "Email->env", false, NULL);
1427
1428 mutt_buffer_reset(&buf);
1429 mutt_buffer_addstr(&buf, "{ rank=same ");
1430
1431 dot_ptr_name(arr, sizeof(arr), e);
1432 mutt_buffer_add_printf(&buf, "%s ", arr);
1433
1434 dot_ptr_name(arr, sizeof(arr), e->env);
1435 mutt_buffer_add_printf(&buf, "%s ", arr);
1436
1437 mutt_buffer_addstr(&buf, "}");
1438
1439 mutt_list_insert_tail(links, mutt_str_dup(buf.data));
1440 }
1441
1442 // struct TagList tags;
1443
1444 mutt_buffer_dealloc(&buf);
1445 }
1446
dump_graphviz_email(struct Email * e)1447 void dump_graphviz_email(struct Email *e)
1448 {
1449 char name[256] = { 0 };
1450 struct ListHead links = STAILQ_HEAD_INITIALIZER(links);
1451
1452 time_t now = time(NULL);
1453 mutt_date_localtime_format(name, sizeof(name), "%R-email.gv", now);
1454
1455 umask(022);
1456 FILE *fp = fopen(name, "w");
1457 if (!fp)
1458 return;
1459
1460 dot_graph_header(fp);
1461
1462 dot_email(fp, e, &links);
1463
1464 dot_graph_footer(fp, &links);
1465 fclose(fp);
1466 mutt_list_free(&links);
1467 }
1468
dot_attach_ptr2(FILE * fp,struct AttachPtr * aptr,struct ListHead * links)1469 static void dot_attach_ptr2(FILE *fp, struct AttachPtr *aptr, struct ListHead *links)
1470 {
1471 if (!aptr)
1472 return;
1473
1474 struct Buffer buf = mutt_buffer_make(256);
1475
1476 dot_object_header(fp, aptr, "AttachPtr", "#3bcbc4");
1477
1478 dot_ptr(fp, "body", aptr->body, "#2020ff");
1479 dot_type_file(fp, "fp", aptr->fp);
1480
1481 dot_type_string(fp, "parent_type", get_content_type(aptr->parent_type), false);
1482 dot_type_number(fp, "level", aptr->level);
1483 dot_type_number(fp, "num", aptr->num);
1484 dot_type_bool(fp, "unowned", aptr->unowned);
1485 dot_type_bool(fp, "decrypted", aptr->decrypted);
1486
1487 // dot_type_string(fp, "tree", aptr->tree, false);
1488
1489 dot_object_footer(fp);
1490
1491 mutt_buffer_dealloc(&buf);
1492 }
1493
dot_array_actx_idx(FILE * fp,struct AttachPtr ** idx,short idxlen,short idxmax,struct ListHead * links)1494 static void dot_array_actx_idx(FILE *fp, struct AttachPtr **idx, short idxlen,
1495 short idxmax, struct ListHead *links)
1496 {
1497 dot_object_header(fp, idx, "AttachCtx->idx", "#9347de");
1498
1499 dot_type_number(fp, "idxlen", idxlen);
1500 dot_type_number(fp, "idxmax", idxmax);
1501
1502 char arr[32];
1503 for (size_t i = 0; i < idxmax; i++)
1504 {
1505 snprintf(arr, sizeof(arr), "idx[%ld]", i);
1506 dot_ptr(fp, arr, idx[i], "#3bcbc4");
1507 }
1508
1509 dot_object_footer(fp);
1510
1511 for (size_t i = 0; i < idxlen; i++)
1512 {
1513 dot_attach_ptr2(fp, idx[i], links);
1514 dot_add_link(links, idx, idx[i], "AttachCtx->idx", false, NULL);
1515 }
1516 }
1517
dot_array_actx_v2r(FILE * fp,short * v2r,short vcount,struct ListHead * links)1518 static void dot_array_actx_v2r(FILE *fp, short *v2r, short vcount, struct ListHead *links)
1519 {
1520 dot_object_header(fp, v2r, "AttachCtx->v2r", "#9347de");
1521
1522 dot_type_number(fp, "vcount", vcount);
1523
1524 char arr[32];
1525 for (size_t i = 0; i < vcount; i++)
1526 {
1527 snprintf(arr, sizeof(arr), "v2r[%ld]", i);
1528 dot_type_number(fp, arr, v2r[i]);
1529 }
1530
1531 dot_object_footer(fp);
1532 }
1533
dot_array_actx_fp_idx(FILE * fp,FILE ** fp_idx,short fp_len,short fp_max,struct ListHead * links)1534 static void dot_array_actx_fp_idx(FILE *fp, FILE **fp_idx, short fp_len,
1535 short fp_max, struct ListHead *links)
1536 {
1537 dot_object_header(fp, fp_idx, "AttachCtx->fp_idx", "#f86e28");
1538
1539 dot_type_number(fp, "fp_len", fp_len);
1540 dot_type_number(fp, "fp_max", fp_max);
1541
1542 char arr[32];
1543 for (size_t i = 0; i < fp_max; i++)
1544 {
1545 snprintf(arr, sizeof(arr), "fp_idx[%ld]", i);
1546 dot_type_file(fp, arr, fp_idx[i]);
1547 }
1548
1549 dot_object_footer(fp);
1550 }
1551
dot_array_actx_body_idx(FILE * fp,struct Body ** body_idx,short body_len,short body_max,struct ListHead * links)1552 static void dot_array_actx_body_idx(FILE *fp, struct Body **body_idx, short body_len,
1553 short body_max, struct ListHead *links)
1554 {
1555 dot_object_header(fp, body_idx, "AttachCtx->body_idx", "#4ff270");
1556
1557 dot_type_number(fp, "body_len", body_len);
1558 dot_type_number(fp, "body_max", body_max);
1559
1560 char arr[32];
1561 for (size_t i = 0; i < body_max; i++)
1562 {
1563 snprintf(arr, sizeof(arr), "body_idx[%ld]", i);
1564 dot_ptr(fp, arr, body_idx[i], "#2020ff");
1565 }
1566
1567 dot_object_footer(fp);
1568 }
1569
dot_attach_ctx(FILE * fp,struct AttachCtx * actx,struct ListHead * links)1570 static void dot_attach_ctx(FILE *fp, struct AttachCtx *actx, struct ListHead *links)
1571 {
1572 struct Buffer buf = mutt_buffer_make(256);
1573 // char arr[256];
1574
1575 dot_object_header(fp, actx, "AttachCtx", "#9347de");
1576
1577 dot_ptr(fp, "email", actx->email, "#ff80ff");
1578 dot_type_file(fp, "fp_root", actx->fp_root);
1579
1580 dot_object_footer(fp);
1581
1582 if (actx->idx)
1583 {
1584 dot_array_actx_idx(fp, actx->idx, actx->idxlen, actx->idxmax, links);
1585 dot_add_link(links, actx, actx->idx, "AttachCtx->idx", false, NULL);
1586 }
1587
1588 if (actx->v2r)
1589 {
1590 dot_array_actx_v2r(fp, actx->v2r, actx->vcount, links);
1591 dot_add_link(links, actx, actx->v2r, "AttachCtx->v2r", false, NULL);
1592 }
1593
1594 if (actx->fp_idx)
1595 {
1596 dot_array_actx_fp_idx(fp, actx->fp_idx, actx->fp_len, actx->fp_max, links);
1597 dot_add_link(links, actx, actx->fp_idx, "AttachCtx->fp_idx", false, NULL);
1598 }
1599
1600 if (actx->body_idx)
1601 {
1602 dot_array_actx_body_idx(fp, actx->body_idx, actx->body_len, actx->body_max, links);
1603 dot_add_link(links, actx, actx->body_idx, "AttachCtx->body_idx", false, NULL);
1604 }
1605
1606 mutt_buffer_dealloc(&buf);
1607 }
1608
dump_graphviz_attach_ctx(struct AttachCtx * actx)1609 void dump_graphviz_attach_ctx(struct AttachCtx *actx)
1610 {
1611 char name[256] = { 0 };
1612 struct ListHead links = STAILQ_HEAD_INITIALIZER(links);
1613
1614 time_t now = time(NULL);
1615 mutt_date_localtime_format(name, sizeof(name), "%R-actx.gv", now);
1616
1617 umask(022);
1618 FILE *fp = fopen(name, "w");
1619 if (!fp)
1620 return;
1621
1622 dot_graph_header(fp);
1623
1624 dot_attach_ctx(fp, actx, &links);
1625
1626 dot_graph_footer(fp, &links);
1627 fclose(fp);
1628 mutt_list_free(&links);
1629 }
1630