]> diplodocus.org Git - nmh/blob - sbr/message_id.c
Various IMAP protocol improvements
[nmh] / sbr / message_id.c
1 /* message_id.c -- construct the body of a Message-ID or Content-ID
2 * header field
3 *
4 * This code is Copyright (c) 2012, by the authors of nmh. See the
5 * COPYRIGHT file in the root directory of the nmh distribution for
6 * complete copyright information.
7 */
8
9 #include <h/mh.h>
10 #include "h/mts.h"
11 #include "m_rand.h"
12 #include "message_id.h"
13 #include <sys/time.h> /* for gettimeofday() */
14 #include "base64.h"
15
16
17 static enum {
18 NMH_MESSAGE_ID_LOCALNAME,
19 NMH_MESSAGE_ID_RANDOM
20 } message_id_style = NMH_MESSAGE_ID_LOCALNAME;
21 static char message_id_[BUFSIZ];
22
23
24 /* Convert name of message id style to integer value and store it. */
25 int
26 save_message_id_style (const char *value)
27 {
28 if (! strcasecmp (value, "localname")) {
29 message_id_style = NMH_MESSAGE_ID_LOCALNAME;
30 return 0;
31 }
32 if (! strcasecmp (value, "random")) {
33 message_id_style = NMH_MESSAGE_ID_RANDOM;
34 return 0;
35 }
36 return 1;
37 }
38
39
40 char *
41 message_id (time_t tclock, int content_id)
42 {
43 switch (message_id_style) {
44 case NMH_MESSAGE_ID_LOCALNAME: {
45 #define P(fmt) \
46 snprintf(message_id_, sizeof message_id_, \
47 (fmt), (int)getpid(), (long)tclock, LocalName(1))
48
49 if (content_id)
50 P("<%d.%ld.%%d@%s>");
51 else
52 P("<%d.%ld@%s>");
53 #undef P
54 break;
55 }
56
57 case NMH_MESSAGE_ID_RANDOM: {
58 /* Use a sequence of digits divisible by 3 because that will
59 expand to base64 without any waste. Must be shorter than 58,
60 see below. */
61 unsigned char rnd[9];
62 /* The part after the '@' is divided into thirds. The base64
63 encoded string will be 4/3 the size of rnd. */
64 size_t one_third = sizeof rnd * 4/3/3;
65
66 if (m_rand (rnd, sizeof rnd) == 0) {
67 struct timeval now;
68 /* All we really need is 4 * [sizeof rnd/3] + 2, as long as
69 the base64 encoding stays shorter than 76 bytes so embedded
70 newlines aren't necessary. But use double the sizeof rnd
71 just to be safe. */
72 unsigned char rnd_base64[2 * sizeof rnd];
73 unsigned char *cp;
74 int i;
75
76 writeBase64 (rnd, sizeof rnd, rnd_base64);
77
78 for (i = strlen ((const char *) rnd_base64) - 1;
79 i > 0 && iscntrl (rnd_base64[i]);
80 --i) {
81 /* Remove trailing newline. rnd_base64 had better be
82 shorter than 76 characters, so don't bother to look for
83 embedded newlines. */
84 rnd_base64[i] = '\0';
85 }
86
87 /* Try to make the base64 string look a little more like a
88 hostname by replacing + with - and / with _. */
89 for (cp = rnd_base64; *cp; ++cp) {
90 if (*cp == '+') {
91 *cp = '-';
92 } else if (*cp == '/') {
93 *cp = '_';
94 }
95 }
96
97 /* gettimeofday() and getpid() shouldn't fail on POSIX platforms. */
98 gettimeofday (&now, 0);
99
100 /* The format string inserts a couple of dots, for the benefit
101 of spam filters that want to see a message id with a final
102 part that resembles a hostname. */
103 #define P(fmt) \
104 snprintf(message_id_, sizeof message_id_, (fmt), \
105 getpid(), (long)now.tv_sec, (long)now.tv_usec, \
106 (int)one_third, rnd_base64, \
107 (int)one_third, &rnd_base64[one_third], \
108 (int)one_third, &rnd_base64[2*one_third])
109
110 if (content_id)
111 P("<%d-%ld.%06ld%%d@%.*s.%.*s.%.*s>");
112 else
113 P("<%d-%ld.%06ld@%.*s.%.*s.%.*s>");
114 #undef P
115 }
116
117 break;
118 }
119 }
120
121 return message_id_;
122 }