masqmail

annotate src/header.c @ 323:29de6a1c4538

Fixed an important bug with folded headers! g_strconcat() returns a *copy* of the string, but hdr->value still pointed to the old header (which probably was a memory leak, too). If the folded part had been quite small it was likely that the new string was at the same position as the old one, thus making everything go well. But if pretty long headers were folded several times it was likely that the new string was allocated somewhere else in memory, thus breaking things. In result mails to lots of recipients (folded header) were frequently only sent to the ones in the first line. Sorry for the inconvenience.
author meillo@marmaro.de
date Fri, 03 Jun 2011 09:47:27 +0200
parents 3e3c280ca5b2
children 41958685480d
rev   line source
meillo@0 1 /* MasqMail
meillo@0 2 Copyright (C) 2000 Oliver Kurth
meillo@0 3
meillo@0 4 This program is free software; you can redistribute it and/or modify
meillo@0 5 it under the terms of the GNU General Public License as published by
meillo@0 6 the Free Software Foundation; either version 2 of the License, or
meillo@0 7 (at your option) any later version.
meillo@0 8
meillo@0 9 This program is distributed in the hope that it will be useful,
meillo@0 10 but WITHOUT ANY WARRANTY; without even the implied warranty of
meillo@0 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
meillo@0 12 GNU General Public License for more details.
meillo@0 13
meillo@0 14 You should have received a copy of the GNU General Public License
meillo@0 15 along with this program; if not, write to the Free Software
meillo@0 16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
meillo@0 17 */
meillo@0 18 #include "masqmail.h"
meillo@0 19
meillo@10 20 header_name header_names[] = {
meillo@15 21 {"From", HEAD_FROM,},
meillo@15 22 {"Sender", HEAD_SENDER,},
meillo@15 23 {"To", HEAD_TO,},
meillo@15 24 {"Cc", HEAD_CC,},
meillo@15 25 {"Bcc", HEAD_BCC,},
meillo@15 26 {"Date", HEAD_DATE,},
meillo@15 27 {"Message-Id", HEAD_MESSAGE_ID,},
meillo@15 28 {"Reply-To", HEAD_REPLY_TO,},
meillo@15 29 {"Subject", HEAD_SUBJECT,},
meillo@15 30 {"Return-Path", HEAD_RETURN_PATH,},
meillo@15 31 {"Envelope-To", HEAD_ENVELOPE_TO,},
meillo@15 32 {"Received", HEAD_RECEIVED},
meillo@0 33 };
meillo@0 34
meillo@0 35 /* this was borrowed from exim and slightly changed */
meillo@10 36 gchar*
meillo@10 37 rec_timestamp()
meillo@0 38 {
meillo@10 39 static gchar buf[64];
meillo@10 40 int len;
meillo@0 41
meillo@10 42 time_t now = time(NULL);
meillo@10 43 struct tm *t = localtime(&now);
meillo@0 44
meillo@10 45 int diff_hour, diff_min;
meillo@10 46 struct tm local;
meillo@10 47 struct tm *gmt;
meillo@0 48
meillo@10 49 memcpy(&local, t, sizeof(struct tm));
meillo@10 50 gmt = gmtime(&now);
meillo@10 51 diff_min = 60 * (local.tm_hour - gmt->tm_hour) + local.tm_min - gmt->tm_min;
meillo@301 52 if (local.tm_year != gmt->tm_year) {
meillo@10 53 diff_min += (local.tm_year > gmt->tm_year) ? 1440 : -1440;
meillo@301 54 } else if (local.tm_yday != gmt->tm_yday) {
meillo@10 55 diff_min += (local.tm_yday > gmt->tm_yday) ? 1440 : -1440;
meillo@301 56 }
meillo@10 57 diff_hour = diff_min / 60;
meillo@10 58 diff_min = abs(diff_min - diff_hour * 60);
meillo@0 59
meillo@10 60 len = strftime(buf, sizeof(buf), "%a, ", &local);
meillo@10 61 g_snprintf(buf + len, sizeof(buf) - len, "%02d ", local.tm_mday);
meillo@10 62 len += strlen(buf + len);
meillo@10 63 len += strftime(buf + len, sizeof(buf) - len, "%b %Y %H:%M:%S", &local);
meillo@10 64 g_snprintf(buf + len, sizeof(buf) - len, " %+03d%02d", diff_hour, diff_min);
meillo@10 65
meillo@10 66 return buf;
meillo@0 67 }
meillo@0 68
meillo@0 69 /* finds list of headers matching id
meillo@0 70 if id == HEAD_UNKNOWN and header == NULL finds all unknown headers
meillo@0 71 else finds all headers matching header
meillo@0 72 */
meillo@10 73 GList*
meillo@10 74 find_header(GList * hdr_list, header_id id, gchar * hdr_str)
meillo@0 75 {
meillo@10 76 GList *found_list = NULL;
meillo@10 77 GList *node;
meillo@0 78
meillo@301 79 if ((id != HEAD_UNKNOWN) || !hdr_str) {
meillo@10 80 foreach(hdr_list, node) {
meillo@10 81 header *hdr = (header *) (node->data);
meillo@301 82 if (hdr->id == id) {
meillo@10 83 found_list = g_list_append(found_list, hdr);
meillo@301 84 }
meillo@10 85 }
meillo@301 86 return found_list;
meillo@301 87 }
meillo@10 88
meillo@301 89 foreach(hdr_list, node) {
meillo@301 90 header *hdr = (header *) (node->data);
meillo@301 91 gchar buf[64], *q = buf, *p = hdr->header;
meillo@10 92
meillo@301 93 while (*p != ':' && q < buf+sizeof(buf)-1 && *p) {
meillo@301 94 *(q++) = *(p++);
meillo@301 95 }
meillo@301 96 *q = '\0';
meillo@301 97
meillo@301 98 if (strcasecmp(buf, hdr_str) == 0) {
meillo@301 99 found_list = g_list_append(found_list, hdr);
meillo@10 100 }
meillo@10 101 }
meillo@10 102 return found_list;
meillo@0 103 }
meillo@0 104
meillo@10 105 void
meillo@10 106 header_unfold(header * hdr)
meillo@0 107 {
meillo@302 108 char *src = hdr->header;
meillo@302 109 char *dest = src;
meillo@302 110 char *p;
meillo@0 111
meillo@302 112 p = strchr(src, '\n');
meillo@302 113 if (!p || !p[1]) {
meillo@302 114 /* no folded header */
meillo@302 115 return;
meillo@302 116 }
meillo@0 117
meillo@302 118 while (*src) {
meillo@302 119 if (*src == '\n') {
meillo@302 120 /* ignore */
meillo@302 121 src++;
meillo@302 122 } else {
meillo@302 123 /* copy */
meillo@302 124 *(dest++) = *(src++);
meillo@302 125 }
meillo@302 126 }
meillo@302 127 *(dest++) = '\n';
meillo@302 128 *(dest++) = '\0';
meillo@0 129 }
meillo@0 130
meillo@303 131 /*
meillo@303 132 fold the header at maxlen chars (newline excluded)
meillo@303 133 (We exclude the newline because the RFCs deal with it this way)
meillo@303 134 */
meillo@10 135 void
meillo@303 136 header_fold(header* hdr, unsigned int maxlen)
meillo@0 137 {
meillo@303 138 int len = strlen(hdr->header);
meillo@303 139 char* src = hdr->header;
meillo@303 140 char* dest;
meillo@303 141 char* tmp;
meillo@303 142 char* p;
meillo@301 143 int valueoffset;
meillo@301 144
meillo@303 145 if (len <= maxlen) {
meillo@301 146 /* we don't need to do anything */
meillo@301 147 return;
meillo@301 148 }
meillo@301 149
meillo@303 150 /* strip trailing whitespace */
meillo@303 151 for (p=src+len-1; *p==' '||*p=='\t'||*p=='\n'; p--) {
meillo@303 152 *p = '\0';
meillo@303 153 len--;
meillo@303 154 printf(" trailing whitespace\n");
meillo@303 155 }
meillo@303 156 printf("stripped len: %d\n", len);
meillo@303 157
meillo@303 158 /* FIXME: would be nice to have a better size calculation */
meillo@303 159 /* (the current size + what we insert as break, twice as often as
meillo@303 160 we have breaks in the optimal case) */
meillo@303 161 tmp = malloc(len + 2 * (len/maxlen) * strlen("\n\t"));
meillo@303 162 dest = tmp;
meillo@303 163
meillo@303 164 /* the position in hdr->header where the value part start */
meillo@301 165 valueoffset = hdr->value - hdr->header;
meillo@301 166
meillo@303 167 while (strlen(src) > maxlen) {
meillo@303 168 int i, l;
meillo@303 169 char *pp;
meillo@0 170
meillo@303 171 for (pp=src+maxlen; pp>src; pp--) {
meillo@303 172 if (*pp==' ' || *pp=='\t' || *p=='\n') {
meillo@303 173 break;
meillo@303 174 }
meillo@303 175 }
meillo@303 176
meillo@303 177 if (src == pp) {
meillo@303 178 /* no potential break point was found within
meillo@303 179 maxlen so advance further until the next */
meillo@303 180 for (pp=src+maxlen; *pp; pp++) {
meillo@303 181 if (*pp==' ' || *pp=='\t' || *p=='\n') {
meillo@303 182 break;
meillo@303 183 }
meillo@303 184 }
meillo@303 185 }
meillo@303 186 if (!*pp) {
meillo@303 187 break;
meillo@303 188 }
meillo@0 189
meillo@303 190 memcpy(dest, src, pp-src);
meillo@303 191 dest += pp-src;
meillo@303 192 *(dest++) = '\n';
meillo@303 193 *(dest++) = '\t';
meillo@303 194 while (*pp == ' ' || *pp == '\t' || *p=='\n') {
meillo@303 195 pp++;
meillo@303 196 }
meillo@303 197 src = pp;
meillo@303 198 }
meillo@303 199 memcpy(dest, src, strlen(src));
meillo@303 200 dest += strlen(src);
meillo@303 201
meillo@303 202 if (*(dest-1) != '\n') {
meillo@303 203 *dest = '\n';
meillo@303 204 *(dest+1) = '\0';
meillo@301 205 }
meillo@0 206
meillo@303 207 hdr->header = tmp;
meillo@301 208 hdr->value = hdr->header + valueoffset;
meillo@0 209 }
meillo@0 210
meillo@10 211 header*
meillo@10 212 create_header(header_id id, gchar * fmt, ...)
meillo@0 213 {
meillo@10 214 gchar *p;
meillo@10 215 header *hdr;
meillo@10 216 va_list args;
meillo@10 217 va_start(args, fmt);
meillo@0 218
meillo@301 219 /* g_malloc() calls exit on failure */
meillo@301 220 hdr = g_malloc(sizeof(header));
meillo@0 221
meillo@301 222 hdr->id = id;
meillo@301 223 hdr->header = g_strdup_vprintf(fmt, args);
meillo@301 224 hdr->value = NULL;
meillo@0 225
meillo@301 226 /* value shall point to the first non-whitespace char in the
meillo@301 227 value part of the header line (i.e. after the first colon) */
meillo@301 228 p = strchr(hdr->header, ':');
meillo@301 229 if (p) {
meillo@301 230 p++;
meillo@301 231 while (*p == ' ' || *p == '\t' || *p == '\n') {
meillo@10 232 p++;
meillo@301 233 }
meillo@301 234 hdr->value = (*p) ? p : NULL;
meillo@10 235 }
meillo@0 236
meillo@301 237 DEBUG(3) debugf("create_header(): hdr: `%s'\n", hdr->header);
meillo@301 238 DEBUG(3) debugf("create_header(): val: `%s'\n", hdr->value);
meillo@301 239
meillo@10 240 va_end(args);
meillo@10 241 return hdr;
meillo@0 242 }
meillo@0 243
meillo@10 244 void
meillo@10 245 destroy_header(header * hdr)
meillo@0 246 {
meillo@10 247 if (hdr) {
meillo@301 248 if (hdr->header) {
meillo@10 249 g_free(hdr->header);
meillo@301 250 }
meillo@10 251 g_free(hdr);
meillo@10 252 }
meillo@0 253 }
meillo@0 254
meillo@10 255 header*
meillo@10 256 copy_header(header * hdr)
meillo@0 257 {
meillo@10 258 header *new_hdr = NULL;
meillo@0 259
meillo@10 260 if (hdr) {
meillo@301 261 new_hdr = g_malloc(sizeof(header));
meillo@301 262 new_hdr->id = hdr->id;
meillo@301 263 new_hdr->header = g_strdup(hdr->header);
meillo@301 264 new_hdr->value = new_hdr->header + (hdr->value - hdr->header);
meillo@10 265 }
meillo@10 266 return new_hdr;
meillo@0 267 }
meillo@0 268
meillo@10 269 header*
meillo@10 270 get_header(gchar * line)
meillo@0 271 {
meillo@10 272 gchar *p = line;
meillo@10 273 gchar buf[64], *q = buf;
meillo@10 274 gint i;
meillo@10 275 header *hdr;
meillo@0 276
meillo@301 277 while (*p && (*p != ':') && (q < buf+sizeof(buf)-1)) {
meillo@10 278 *(q++) = *(p++);
meillo@301 279 }
meillo@15 280 *q = '\0';
meillo@0 281
meillo@301 282 if (*p != ':') {
meillo@10 283 return NULL;
meillo@301 284 }
meillo@0 285
meillo@10 286 hdr = g_malloc(sizeof(header));
meillo@0 287
meillo@10 288 hdr->value = NULL;
meillo@10 289 p++;
meillo@0 290
meillo@301 291 while (*p && (*p == ' ' || *p == '\t')) {
meillo@10 292 p++;
meillo@301 293 }
meillo@10 294 hdr->value = p;
meillo@301 295 /* Note: an empty value can also mean that it's only the first part
meillo@301 296 of a folded header line */
meillo@0 297
meillo@10 298 for (i = 0; i < HEAD_NUM_IDS; i++) {
meillo@301 299 if (strcasecmp(header_names[i].header, buf) == 0) {
meillo@10 300 break;
meillo@301 301 }
meillo@10 302 }
meillo@10 303 hdr->id = (header_id) i;
meillo@10 304 hdr->header = g_strdup(line);
meillo@10 305 hdr->value = hdr->header + (hdr->value - line);
meillo@10 306
meillo@322 307 DEBUG(4) debugf("header: %d = %s[...]", hdr->id, hdr->header);
meillo@301 308 /* Note: This only outputs the first line if the header is folded */
meillo@10 309
meillo@10 310 return hdr;
meillo@0 311 }