Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Another stupid program, this one parsing the headers of an |
| 3 | * email to figure out authorship and subject |
| 4 | */ |
Junio C Hamano | f1f909e | 2005-11-27 16:29:38 -0800 | [diff] [blame] | 5 | #include "cache.h" |
Lukas Sandström | 34488e3 | 2006-06-13 22:21:50 +0200 | [diff] [blame] | 6 | #include "builtin.h" |
Junio C Hamano | b45974a | 2006-12-23 23:36:55 -0800 | [diff] [blame] | 7 | #include "utf8.h" |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 8 | |
Lukas Sandström | 34488e3 | 2006-06-13 22:21:50 +0200 | [diff] [blame] | 9 | static FILE *cmitmsg, *patchfile, *fin, *fout; |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 10 | |
David Rientjes | 96f1e58 | 2006-08-15 10:23:48 -0700 | [diff] [blame] | 11 | static int keep_subject; |
| 12 | static const char *metainfo_charset; |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 13 | static char line[1000]; |
| 14 | static char name[1000]; |
| 15 | static char email[1000]; |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 16 | |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 17 | static enum { |
| 18 | TE_DONTCARE, TE_QP, TE_BASE64, |
| 19 | } transfer_encoding; |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 20 | static enum { |
| 21 | TYPE_TEXT, TYPE_OTHER, |
| 22 | } message_type; |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 23 | |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 24 | static char charset[256]; |
David Rientjes | 96f1e58 | 2006-08-15 10:23:48 -0700 | [diff] [blame] | 25 | static int patch_lines; |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 26 | static char **p_hdr_data, **s_hdr_data; |
| 27 | |
| 28 | #define MAX_HDR_PARSED 10 |
| 29 | #define MAX_BOUNDARIES 5 |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 30 | |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 31 | static char *sanity_check(char *name, char *email) |
| 32 | { |
| 33 | int len = strlen(name); |
| 34 | if (len < 3 || len > 60) |
| 35 | return email; |
| 36 | if (strchr(name, '@') || strchr(name, '<') || strchr(name, '>')) |
| 37 | return email; |
| 38 | return name; |
| 39 | } |
| 40 | |
Junio C Hamano | e0e3ba2 | 2005-12-14 16:31:06 -0800 | [diff] [blame] | 41 | static int bogus_from(char *line) |
| 42 | { |
| 43 | /* John Doe <johndoe> */ |
| 44 | char *bra, *ket, *dst, *cp; |
| 45 | |
| 46 | /* This is fallback, so do not bother if we already have an |
| 47 | * e-mail address. |
Lukas Sandström | 34488e3 | 2006-06-13 22:21:50 +0200 | [diff] [blame] | 48 | */ |
Junio C Hamano | e0e3ba2 | 2005-12-14 16:31:06 -0800 | [diff] [blame] | 49 | if (*email) |
| 50 | return 0; |
| 51 | |
| 52 | bra = strchr(line, '<'); |
| 53 | if (!bra) |
| 54 | return 0; |
| 55 | ket = strchr(bra, '>'); |
| 56 | if (!ket) |
| 57 | return 0; |
| 58 | |
| 59 | for (dst = email, cp = bra+1; cp < ket; ) |
| 60 | *dst++ = *cp++; |
| 61 | *dst = 0; |
| 62 | for (cp = line; isspace(*cp); cp++) |
| 63 | ; |
| 64 | for (bra--; isspace(*bra); bra--) |
| 65 | *bra = 0; |
| 66 | cp = sanity_check(cp, email); |
| 67 | strcpy(name, cp); |
| 68 | return 1; |
| 69 | } |
| 70 | |
Eric W. Biederman | 2dec02b | 2006-05-23 13:58:36 -0600 | [diff] [blame] | 71 | static int handle_from(char *in_line) |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 72 | { |
Eric W. Biederman | 2dec02b | 2006-05-23 13:58:36 -0600 | [diff] [blame] | 73 | char line[1000]; |
| 74 | char *at; |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 75 | char *dst; |
| 76 | |
Eric W. Biederman | 2dec02b | 2006-05-23 13:58:36 -0600 | [diff] [blame] | 77 | strcpy(line, in_line); |
| 78 | at = strchr(line, '@'); |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 79 | if (!at) |
Junio C Hamano | e0e3ba2 | 2005-12-14 16:31:06 -0800 | [diff] [blame] | 80 | return bogus_from(line); |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 81 | |
| 82 | /* |
| 83 | * If we already have one email, don't take any confusing lines |
| 84 | */ |
| 85 | if (*email && strchr(at+1, '@')) |
| 86 | return 0; |
| 87 | |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 88 | /* Pick up the string around '@', possibly delimited with <> |
| 89 | * pair; that is the email part. White them out while copying. |
| 90 | */ |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 91 | while (at > line) { |
| 92 | char c = at[-1]; |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 93 | if (isspace(c)) |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 94 | break; |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 95 | if (c == '<') { |
| 96 | at[-1] = ' '; |
| 97 | break; |
| 98 | } |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 99 | at--; |
| 100 | } |
| 101 | dst = email; |
| 102 | for (;;) { |
| 103 | unsigned char c = *at; |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 104 | if (!c || c == '>' || isspace(c)) { |
| 105 | if (c == '>') |
| 106 | *at = ' '; |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 107 | break; |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 108 | } |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 109 | *at++ = ' '; |
| 110 | *dst++ = c; |
| 111 | } |
| 112 | *dst++ = 0; |
| 113 | |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 114 | /* The remainder is name. It could be "John Doe <john.doe@xz>" |
| 115 | * or "john.doe@xz (John Doe)", but we have whited out the |
| 116 | * email part, so trim from both ends, possibly removing |
| 117 | * the () pair at the end. |
| 118 | */ |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 119 | at = line + strlen(line); |
| 120 | while (at > line) { |
| 121 | unsigned char c = *--at; |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 122 | if (!isspace(c)) { |
| 123 | at[(c == ')') ? 0 : 1] = 0; |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 124 | break; |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 125 | } |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 126 | } |
| 127 | |
| 128 | at = line; |
| 129 | for (;;) { |
| 130 | unsigned char c = *at; |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 131 | if (!c || !isspace(c)) { |
| 132 | if (c == '(') |
| 133 | at++; |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 134 | break; |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 135 | } |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 136 | at++; |
| 137 | } |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 138 | at = sanity_check(at, email); |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 139 | strcpy(name, at); |
| 140 | return 1; |
| 141 | } |
| 142 | |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 143 | static int handle_header(char *line, char *data, int ofs) |
Linus Torvalds | 62c1f6b | 2005-05-01 21:42:53 -0700 | [diff] [blame] | 144 | { |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 145 | if (!line || !data) |
| 146 | return 1; |
Linus Torvalds | 62c1f6b | 2005-05-01 21:42:53 -0700 | [diff] [blame] | 147 | |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 148 | strcpy(data, line+ofs); |
| 149 | |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 150 | return 0; |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 151 | } |
| 152 | |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 153 | /* NOTE NOTE NOTE. We do not claim we do full MIME. We just attempt |
| 154 | * to have enough heuristics to grok MIME encoded patches often found |
| 155 | * on our mailing lists. For example, we do not even treat header lines |
| 156 | * case insensitively. |
| 157 | */ |
| 158 | |
| 159 | static int slurp_attr(const char *line, const char *name, char *attr) |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 160 | { |
Timo Hirvonen | 554fe20 | 2006-06-28 12:04:39 +0300 | [diff] [blame] | 161 | const char *ends, *ap = strcasestr(line, name); |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 162 | size_t sz; |
| 163 | |
| 164 | if (!ap) { |
| 165 | *attr = 0; |
| 166 | return 0; |
| 167 | } |
| 168 | ap += strlen(name); |
| 169 | if (*ap == '"') { |
| 170 | ap++; |
| 171 | ends = "\""; |
| 172 | } |
| 173 | else |
| 174 | ends = "; \t"; |
| 175 | sz = strcspn(ap, ends); |
| 176 | memcpy(attr, ap, sz); |
| 177 | attr[sz] = 0; |
| 178 | return 1; |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 179 | } |
| 180 | |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 181 | struct content_type { |
| 182 | char *boundary; |
| 183 | int boundary_len; |
| 184 | }; |
| 185 | |
| 186 | static struct content_type content[MAX_BOUNDARIES]; |
| 187 | |
| 188 | static struct content_type *content_top = content; |
| 189 | |
| 190 | static int handle_content_type(char *line) |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 191 | { |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 192 | char boundary[256]; |
| 193 | |
| 194 | if (strcasestr(line, "text/") == NULL) |
| 195 | message_type = TYPE_OTHER; |
| 196 | if (slurp_attr(line, "boundary=", boundary + 2)) { |
| 197 | memcpy(boundary, "--", 2); |
| 198 | if (content_top++ >= &content[MAX_BOUNDARIES]) { |
| 199 | fprintf(stderr, "Too many boundaries to handle\n"); |
| 200 | exit(1); |
| 201 | } |
| 202 | content_top->boundary_len = strlen(boundary); |
| 203 | content_top->boundary = xmalloc(content_top->boundary_len+1); |
| 204 | strcpy(content_top->boundary, boundary); |
Junio C Hamano | b893f09 | 2005-09-06 16:46:34 -0700 | [diff] [blame] | 205 | } |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 206 | if (slurp_attr(line, "charset=", charset)) { |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 207 | int i, c; |
| 208 | for (i = 0; (c = charset[i]) != 0; i++) |
| 209 | charset[i] = tolower(c); |
| 210 | } |
| 211 | return 0; |
| 212 | } |
| 213 | |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 214 | static int handle_content_transfer_encoding(char *line) |
| 215 | { |
| 216 | if (strcasestr(line, "base64")) |
| 217 | transfer_encoding = TE_BASE64; |
| 218 | else if (strcasestr(line, "quoted-printable")) |
| 219 | transfer_encoding = TE_QP; |
| 220 | else |
| 221 | transfer_encoding = TE_DONTCARE; |
| 222 | return 0; |
| 223 | } |
| 224 | |
| 225 | static int is_multipart_boundary(const char *line) |
| 226 | { |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 227 | return (!memcmp(line, content_top->boundary, content_top->boundary_len)); |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 228 | } |
| 229 | |
| 230 | static int eatspace(char *line) |
| 231 | { |
| 232 | int len = strlen(line); |
| 233 | while (len > 0 && isspace(line[len-1])) |
| 234 | line[--len] = 0; |
| 235 | return len; |
| 236 | } |
| 237 | |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 238 | static char *cleanup_subject(char *subject) |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 239 | { |
Junio C Hamano | 6bff6a6 | 2005-08-16 22:18:27 -0700 | [diff] [blame] | 240 | if (keep_subject) |
| 241 | return subject; |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 242 | for (;;) { |
| 243 | char *p; |
| 244 | int len, remove; |
| 245 | switch (*subject) { |
| 246 | case 'r': case 'R': |
| 247 | if (!memcmp("e:", subject+1, 2)) { |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 248 | subject += 3; |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 249 | continue; |
| 250 | } |
| 251 | break; |
| 252 | case ' ': case '\t': case ':': |
| 253 | subject++; |
| 254 | continue; |
| 255 | |
| 256 | case '[': |
| 257 | p = strchr(subject, ']'); |
| 258 | if (!p) { |
| 259 | subject++; |
| 260 | continue; |
| 261 | } |
| 262 | len = strlen(p); |
| 263 | remove = p - subject; |
| 264 | if (remove <= len *2) { |
| 265 | subject = p+1; |
| 266 | continue; |
Lukas Sandström | 34488e3 | 2006-06-13 22:21:50 +0200 | [diff] [blame] | 267 | } |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 268 | break; |
| 269 | } |
Junio C Hamano | ae448e3 | 2006-06-17 16:58:51 -0700 | [diff] [blame] | 270 | eatspace(subject); |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 271 | return subject; |
| 272 | } |
Lukas Sandström | 34488e3 | 2006-06-13 22:21:50 +0200 | [diff] [blame] | 273 | } |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 274 | |
| 275 | static void cleanup_space(char *buf) |
| 276 | { |
| 277 | unsigned char c; |
| 278 | while ((c = *buf) != 0) { |
| 279 | buf++; |
| 280 | if (isspace(c)) { |
| 281 | buf[-1] = ' '; |
| 282 | c = *buf; |
| 283 | while (isspace(c)) { |
| 284 | int len = strlen(buf); |
| 285 | memmove(buf, buf+1, len); |
| 286 | c = *buf; |
| 287 | } |
| 288 | } |
| 289 | } |
| 290 | } |
| 291 | |
Junio C Hamano | b75bf2c | 2006-07-05 14:17:49 -0700 | [diff] [blame] | 292 | static void decode_header(char *it); |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 293 | static char *header[MAX_HDR_PARSED] = { |
| 294 | "From","Subject","Date", |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 295 | }; |
| 296 | |
Don Zickus | 86747c1 | 2007-03-30 12:18:45 -0400 | [diff] [blame] | 297 | static int check_header(char *line, char **hdr_data, int overwrite) |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 298 | { |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 299 | int i; |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 300 | |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 301 | /* search for the interesting parts */ |
| 302 | for (i = 0; header[i]; i++) { |
| 303 | int len = strlen(header[i]); |
Don Zickus | 86747c1 | 2007-03-30 12:18:45 -0400 | [diff] [blame] | 304 | if ((!hdr_data[i] || overwrite) && |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 305 | !strncasecmp(line, header[i], len) && |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 306 | line[len] == ':' && isspace(line[len + 1])) { |
Eric W. Biederman | 3350453 | 2006-05-23 13:45:37 -0600 | [diff] [blame] | 307 | /* Unwrap inline B and Q encoding, and optionally |
| 308 | * normalize the meta information to utf8. |
| 309 | */ |
Junio C Hamano | b75bf2c | 2006-07-05 14:17:49 -0700 | [diff] [blame] | 310 | decode_header(line + len + 2); |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 311 | hdr_data[i] = xmalloc(1000 * sizeof(char)); |
| 312 | if (! handle_header(line, hdr_data[i], len + 2)) { |
| 313 | return 1; |
| 314 | } |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 315 | } |
| 316 | } |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 317 | |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 318 | /* Content stuff */ |
| 319 | if (!strncasecmp(line, "Content-Type", 12) && |
| 320 | line[12] == ':' && isspace(line[12 + 1])) { |
| 321 | decode_header(line + 12 + 2); |
| 322 | if (! handle_content_type(line)) { |
| 323 | return 1; |
| 324 | } |
| 325 | } |
| 326 | if (!strncasecmp(line, "Content-Transfer-Encoding", 25) && |
| 327 | line[25] == ':' && isspace(line[25 + 1])) { |
| 328 | decode_header(line + 25 + 2); |
| 329 | if (! handle_content_transfer_encoding(line)) { |
| 330 | return 1; |
| 331 | } |
| 332 | } |
| 333 | |
| 334 | /* for inbody stuff */ |
| 335 | if (!memcmp(">From", line, 5) && isspace(line[5])) |
| 336 | return 1; |
| 337 | if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) { |
| 338 | for (i = 0; header[i]; i++) { |
| 339 | if (!memcmp("Subject: ", header[i], 9)) { |
| 340 | if (! handle_header(line, hdr_data[i], 0)) { |
| 341 | return 1; |
| 342 | } |
| 343 | } |
| 344 | } |
| 345 | } |
| 346 | |
| 347 | /* no match */ |
| 348 | return 0; |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 349 | } |
| 350 | |
Junio C Hamano | ef29c11 | 2006-05-26 00:46:58 -0700 | [diff] [blame] | 351 | static int is_rfc2822_header(char *line) |
| 352 | { |
| 353 | /* |
| 354 | * The section that defines the loosest possible |
| 355 | * field name is "3.6.8 Optional fields". |
| 356 | * |
| 357 | * optional-field = field-name ":" unstructured CRLF |
| 358 | * field-name = 1*ftext |
| 359 | * ftext = %d33-57 / %59-126 |
| 360 | */ |
| 361 | int ch; |
| 362 | char *cp = line; |
Linus Torvalds | 34fc5ce | 2007-02-26 11:10:59 -0800 | [diff] [blame] | 363 | |
| 364 | /* Count mbox From headers as headers */ |
| 365 | if (!memcmp(line, "From ", 5) || !memcmp(line, ">From ", 6)) |
| 366 | return 1; |
| 367 | |
Junio C Hamano | ef29c11 | 2006-05-26 00:46:58 -0700 | [diff] [blame] | 368 | while ((ch = *cp++)) { |
| 369 | if (ch == ':') |
| 370 | return cp != line; |
| 371 | if ((33 <= ch && ch <= 57) || |
| 372 | (59 <= ch && ch <= 126)) |
| 373 | continue; |
| 374 | break; |
| 375 | } |
| 376 | return 0; |
| 377 | } |
| 378 | |
Linus Torvalds | 34fc5ce | 2007-02-26 11:10:59 -0800 | [diff] [blame] | 379 | /* |
| 380 | * sz is size of 'line' buffer in bytes. Must be reasonably |
| 381 | * long enough to hold one physical real-world e-mail line. |
| 382 | */ |
Junio C Hamano | 1d8fa41 | 2005-07-23 02:10:31 -0700 | [diff] [blame] | 383 | static int read_one_header_line(char *line, int sz, FILE *in) |
| 384 | { |
Linus Torvalds | 34fc5ce | 2007-02-26 11:10:59 -0800 | [diff] [blame] | 385 | int len; |
| 386 | |
| 387 | /* |
| 388 | * We will read at most (sz-1) bytes and then potentially |
| 389 | * re-add NUL after it. Accessing line[sz] after this is safe |
| 390 | * and we can allow len to grow up to and including sz. |
| 391 | */ |
| 392 | sz--; |
| 393 | |
| 394 | /* Get the first part of the line. */ |
| 395 | if (!fgets(line, sz, in)) |
| 396 | return 0; |
| 397 | |
| 398 | /* |
| 399 | * Is it an empty line or not a valid rfc2822 header? |
| 400 | * If so, stop here, and return false ("not a header") |
| 401 | */ |
| 402 | len = eatspace(line); |
| 403 | if (!len || !is_rfc2822_header(line)) { |
| 404 | /* Re-add the newline */ |
| 405 | line[len] = '\n'; |
| 406 | line[len + 1] = '\0'; |
| 407 | return 0; |
| 408 | } |
| 409 | |
| 410 | /* |
| 411 | * Now we need to eat all the continuation lines.. |
| 412 | * Yuck, 2822 header "folding" |
| 413 | */ |
| 414 | for (;;) { |
| 415 | int peek, addlen; |
| 416 | static char continuation[1000]; |
| 417 | |
Eric W. Biederman | f30b202 | 2006-05-23 13:53:20 -0600 | [diff] [blame] | 418 | peek = fgetc(in); ungetc(peek, in); |
| 419 | if (peek != ' ' && peek != '\t') |
| 420 | break; |
Linus Torvalds | 34fc5ce | 2007-02-26 11:10:59 -0800 | [diff] [blame] | 421 | if (!fgets(continuation, sizeof(continuation), in)) |
| 422 | break; |
| 423 | addlen = eatspace(continuation); |
| 424 | if (len < sz - 1) { |
| 425 | if (addlen >= sz - len) |
| 426 | addlen = sz - len - 1; |
| 427 | memcpy(line + len, continuation, addlen); |
| 428 | len += addlen; |
| 429 | } |
Junio C Hamano | 1d8fa41 | 2005-07-23 02:10:31 -0700 | [diff] [blame] | 430 | } |
Linus Torvalds | 34fc5ce | 2007-02-26 11:10:59 -0800 | [diff] [blame] | 431 | line[len] = 0; |
| 432 | |
| 433 | return 1; |
Junio C Hamano | 1d8fa41 | 2005-07-23 02:10:31 -0700 | [diff] [blame] | 434 | } |
| 435 | |
Junio C Hamano | 7573193 | 2006-04-21 00:06:58 -0700 | [diff] [blame] | 436 | static int decode_q_segment(char *in, char *ot, char *ep, int rfc2047) |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 437 | { |
| 438 | int c; |
| 439 | while ((c = *in++) != 0 && (in <= ep)) { |
| 440 | if (c == '=') { |
| 441 | int d = *in++; |
| 442 | if (d == '\n' || !d) |
| 443 | break; /* drop trailing newline */ |
| 444 | *ot++ = ((hexval(d) << 4) | hexval(*in++)); |
Junio C Hamano | 7573193 | 2006-04-21 00:06:58 -0700 | [diff] [blame] | 445 | continue; |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 446 | } |
Junio C Hamano | 7573193 | 2006-04-21 00:06:58 -0700 | [diff] [blame] | 447 | if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */ |
| 448 | c = 0x20; |
| 449 | *ot++ = c; |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 450 | } |
| 451 | *ot = 0; |
| 452 | return 0; |
| 453 | } |
| 454 | |
| 455 | static int decode_b_segment(char *in, char *ot, char *ep) |
| 456 | { |
| 457 | /* Decode in..ep, possibly in-place to ot */ |
| 458 | int c, pos = 0, acc = 0; |
| 459 | |
| 460 | while ((c = *in++) != 0 && (in <= ep)) { |
| 461 | if (c == '+') |
| 462 | c = 62; |
| 463 | else if (c == '/') |
| 464 | c = 63; |
| 465 | else if ('A' <= c && c <= 'Z') |
| 466 | c -= 'A'; |
| 467 | else if ('a' <= c && c <= 'z') |
| 468 | c -= 'a' - 26; |
| 469 | else if ('0' <= c && c <= '9') |
| 470 | c -= '0' - 52; |
| 471 | else if (c == '=') { |
| 472 | /* padding is almost like (c == 0), except we do |
| 473 | * not output NUL resulting only from it; |
| 474 | * for now we just trust the data. |
| 475 | */ |
| 476 | c = 0; |
| 477 | } |
| 478 | else |
| 479 | continue; /* garbage */ |
| 480 | switch (pos++) { |
| 481 | case 0: |
| 482 | acc = (c << 2); |
| 483 | break; |
| 484 | case 1: |
| 485 | *ot++ = (acc | (c >> 4)); |
| 486 | acc = (c & 15) << 4; |
| 487 | break; |
| 488 | case 2: |
| 489 | *ot++ = (acc | (c >> 2)); |
| 490 | acc = (c & 3) << 6; |
| 491 | break; |
| 492 | case 3: |
| 493 | *ot++ = (acc | c); |
| 494 | acc = pos = 0; |
| 495 | break; |
| 496 | } |
| 497 | } |
| 498 | *ot = 0; |
| 499 | return 0; |
| 500 | } |
| 501 | |
Shawn O. Pearce | 3a55602 | 2007-03-06 20:44:17 -0500 | [diff] [blame] | 502 | static void convert_to_utf8(char *line, const char *charset) |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 503 | { |
Shawn O. Pearce | 3a55602 | 2007-03-06 20:44:17 -0500 | [diff] [blame] | 504 | static const char latin_one[] = "latin1"; |
| 505 | const char *input_charset = *charset ? charset : latin_one; |
Junio C Hamano | b45974a | 2006-12-23 23:36:55 -0800 | [diff] [blame] | 506 | char *out = reencode_string(line, metainfo_charset, input_charset); |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 507 | |
Junio C Hamano | bb1091a | 2007-01-09 21:31:36 -0800 | [diff] [blame] | 508 | if (!out) |
| 509 | die("cannot convert from %s to %s\n", |
| 510 | input_charset, metainfo_charset); |
Junio C Hamano | b45974a | 2006-12-23 23:36:55 -0800 | [diff] [blame] | 511 | strcpy(line, out); |
| 512 | free(out); |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 513 | } |
| 514 | |
Junio C Hamano | b75bf2c | 2006-07-05 14:17:49 -0700 | [diff] [blame] | 515 | static int decode_header_bq(char *it) |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 516 | { |
| 517 | char *in, *out, *ep, *cp, *sp; |
| 518 | char outbuf[1000]; |
Junio C Hamano | b75bf2c | 2006-07-05 14:17:49 -0700 | [diff] [blame] | 519 | int rfc2047 = 0; |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 520 | |
| 521 | in = it; |
| 522 | out = outbuf; |
| 523 | while ((ep = strstr(in, "=?")) != NULL) { |
| 524 | int sz, encoding; |
| 525 | char charset_q[256], piecebuf[256]; |
Junio C Hamano | b75bf2c | 2006-07-05 14:17:49 -0700 | [diff] [blame] | 526 | rfc2047 = 1; |
| 527 | |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 528 | if (in != ep) { |
| 529 | sz = ep - in; |
| 530 | memcpy(out, in, sz); |
| 531 | out += sz; |
| 532 | in += sz; |
| 533 | } |
| 534 | /* E.g. |
| 535 | * ep : "=?iso-2022-jp?B?GyR...?= foo" |
| 536 | * ep : "=?ISO-8859-1?Q?Foo=FCbar?= baz" |
| 537 | */ |
| 538 | ep += 2; |
| 539 | cp = strchr(ep, '?'); |
| 540 | if (!cp) |
Junio C Hamano | b75bf2c | 2006-07-05 14:17:49 -0700 | [diff] [blame] | 541 | return rfc2047; /* no munging */ |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 542 | for (sp = ep; sp < cp; sp++) |
| 543 | charset_q[sp - ep] = tolower(*sp); |
| 544 | charset_q[cp - ep] = 0; |
| 545 | encoding = cp[1]; |
| 546 | if (!encoding || cp[2] != '?') |
Junio C Hamano | b75bf2c | 2006-07-05 14:17:49 -0700 | [diff] [blame] | 547 | return rfc2047; /* no munging */ |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 548 | ep = strstr(cp + 3, "?="); |
| 549 | if (!ep) |
Junio C Hamano | b75bf2c | 2006-07-05 14:17:49 -0700 | [diff] [blame] | 550 | return rfc2047; /* no munging */ |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 551 | switch (tolower(encoding)) { |
| 552 | default: |
Junio C Hamano | b75bf2c | 2006-07-05 14:17:49 -0700 | [diff] [blame] | 553 | return rfc2047; /* no munging */ |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 554 | case 'b': |
| 555 | sz = decode_b_segment(cp + 3, piecebuf, ep); |
| 556 | break; |
| 557 | case 'q': |
Junio C Hamano | 7573193 | 2006-04-21 00:06:58 -0700 | [diff] [blame] | 558 | sz = decode_q_segment(cp + 3, piecebuf, ep, 1); |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 559 | break; |
| 560 | } |
| 561 | if (sz < 0) |
Junio C Hamano | b75bf2c | 2006-07-05 14:17:49 -0700 | [diff] [blame] | 562 | return rfc2047; |
Junio C Hamano | 650e4be | 2005-11-27 16:22:16 -0800 | [diff] [blame] | 563 | if (metainfo_charset) |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 564 | convert_to_utf8(piecebuf, charset_q); |
| 565 | strcpy(out, piecebuf); |
| 566 | out += strlen(out); |
| 567 | in = ep + 2; |
| 568 | } |
| 569 | strcpy(out, in); |
| 570 | strcpy(it, outbuf); |
Junio C Hamano | b75bf2c | 2006-07-05 14:17:49 -0700 | [diff] [blame] | 571 | return rfc2047; |
| 572 | } |
| 573 | |
| 574 | static void decode_header(char *it) |
| 575 | { |
| 576 | |
| 577 | if (decode_header_bq(it)) |
| 578 | return; |
| 579 | /* otherwise "it" is a straight copy of the input. |
| 580 | * This can be binary guck but there is no charset specified. |
| 581 | */ |
| 582 | if (metainfo_charset) |
| 583 | convert_to_utf8(it, ""); |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 584 | } |
| 585 | |
| 586 | static void decode_transfer_encoding(char *line) |
| 587 | { |
| 588 | char *ep; |
| 589 | |
| 590 | switch (transfer_encoding) { |
| 591 | case TE_QP: |
| 592 | ep = line + strlen(line); |
Junio C Hamano | 7573193 | 2006-04-21 00:06:58 -0700 | [diff] [blame] | 593 | decode_q_segment(line, line, ep, 0); |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 594 | break; |
| 595 | case TE_BASE64: |
| 596 | ep = line + strlen(line); |
| 597 | decode_b_segment(line, line, ep); |
| 598 | break; |
| 599 | case TE_DONTCARE: |
| 600 | break; |
| 601 | } |
| 602 | } |
| 603 | |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 604 | static int handle_filter(char *line); |
| 605 | |
| 606 | static int find_boundary(void) |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 607 | { |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 608 | while(fgets(line, sizeof(line), fin) != NULL) { |
| 609 | if (is_multipart_boundary(line)) |
| 610 | return 1; |
| 611 | } |
| 612 | return 0; |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 613 | } |
| 614 | |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 615 | static int handle_boundary(void) |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 616 | { |
Don Zickus | 86747c1 | 2007-03-30 12:18:45 -0400 | [diff] [blame] | 617 | char newline[]="\n"; |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 618 | again: |
| 619 | if (!memcmp(line+content_top->boundary_len, "--", 2)) { |
| 620 | /* we hit an end boundary */ |
| 621 | /* pop the current boundary off the stack */ |
| 622 | free(content_top->boundary); |
| 623 | |
| 624 | /* technically won't happen as is_multipart_boundary() |
| 625 | will fail first. But just in case.. |
| 626 | */ |
| 627 | if (content_top-- < content) { |
| 628 | fprintf(stderr, "Detected mismatched boundaries, " |
| 629 | "can't recover\n"); |
| 630 | exit(1); |
| 631 | } |
Don Zickus | 86747c1 | 2007-03-30 12:18:45 -0400 | [diff] [blame] | 632 | handle_filter(newline); |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 633 | |
| 634 | /* skip to the next boundary */ |
| 635 | if (!find_boundary()) |
| 636 | return 0; |
| 637 | goto again; |
| 638 | } |
| 639 | |
| 640 | /* set some defaults */ |
| 641 | transfer_encoding = TE_DONTCARE; |
| 642 | charset[0] = 0; |
| 643 | message_type = TYPE_TEXT; |
| 644 | |
| 645 | /* slurp in this section's info */ |
| 646 | while (read_one_header_line(line, sizeof(line), fin)) |
Don Zickus | 86747c1 | 2007-03-30 12:18:45 -0400 | [diff] [blame] | 647 | check_header(line, p_hdr_data, 0); |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 648 | |
| 649 | /* eat the blank line after section info */ |
| 650 | return (fgets(line, sizeof(line), fin) != NULL); |
| 651 | } |
| 652 | |
Don Zickus | f0658cf | 2007-03-12 15:52:06 -0400 | [diff] [blame] | 653 | static inline int patchbreak(const char *line) |
| 654 | { |
| 655 | /* Beginning of a "diff -" header? */ |
| 656 | if (!memcmp("diff -", line, 6)) |
| 657 | return 1; |
| 658 | |
| 659 | /* CVS "Index: " line? */ |
| 660 | if (!memcmp("Index: ", line, 7)) |
| 661 | return 1; |
| 662 | |
| 663 | /* |
| 664 | * "--- <filename>" starts patches without headers |
| 665 | * "---<sp>*" is a manual separator |
| 666 | */ |
| 667 | if (!memcmp("---", line, 3)) { |
| 668 | line += 3; |
| 669 | /* space followed by a filename? */ |
| 670 | if (line[0] == ' ' && !isspace(line[1])) |
| 671 | return 1; |
| 672 | /* Just whitespace? */ |
| 673 | for (;;) { |
| 674 | unsigned char c = *line++; |
| 675 | if (c == '\n') |
| 676 | return 1; |
| 677 | if (!isspace(c)) |
| 678 | break; |
| 679 | } |
| 680 | return 0; |
| 681 | } |
| 682 | return 0; |
| 683 | } |
| 684 | |
| 685 | |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 686 | static int handle_commit_msg(char *line) |
| 687 | { |
| 688 | static int still_looking = 1; |
| 689 | |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 690 | if (!cmitmsg) |
| 691 | return 0; |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 692 | |
| 693 | if (still_looking) { |
| 694 | char *cp = line; |
| 695 | if (isspace(*line)) { |
| 696 | for (cp = line + 1; *cp; cp++) { |
| 697 | if (!isspace(*cp)) |
| 698 | break; |
| 699 | } |
| 700 | if (!*cp) |
| 701 | return 0; |
| 702 | } |
Don Zickus | 86747c1 | 2007-03-30 12:18:45 -0400 | [diff] [blame] | 703 | if ((still_looking = check_header(cp, s_hdr_data, 0)) != 0) |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 704 | return 0; |
| 705 | } |
| 706 | |
Don Zickus | 86747c1 | 2007-03-30 12:18:45 -0400 | [diff] [blame] | 707 | /* normalize the log message to UTF-8. */ |
| 708 | if (metainfo_charset) |
| 709 | convert_to_utf8(line, charset); |
| 710 | |
Don Zickus | f0658cf | 2007-03-12 15:52:06 -0400 | [diff] [blame] | 711 | if (patchbreak(line)) { |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 712 | fclose(cmitmsg); |
| 713 | cmitmsg = NULL; |
| 714 | return 1; |
| 715 | } |
| 716 | |
| 717 | fputs(line, cmitmsg); |
| 718 | return 0; |
| 719 | } |
| 720 | |
| 721 | static int handle_patch(char *line) |
| 722 | { |
| 723 | fputs(line, patchfile); |
| 724 | patch_lines++; |
| 725 | return 0; |
| 726 | } |
| 727 | |
| 728 | static int handle_filter(char *line) |
| 729 | { |
| 730 | static int filter = 0; |
| 731 | |
| 732 | /* filter tells us which part we left off on |
| 733 | * a non-zero return indicates we hit a filter point |
| 734 | */ |
| 735 | switch (filter) { |
| 736 | case 0: |
| 737 | if (!handle_commit_msg(line)) |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 738 | break; |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 739 | filter++; |
| 740 | case 1: |
| 741 | if (!handle_patch(line)) |
| 742 | break; |
| 743 | filter++; |
| 744 | default: |
| 745 | return 1; |
| 746 | } |
| 747 | |
| 748 | return 0; |
| 749 | } |
| 750 | |
| 751 | static void handle_body(void) |
| 752 | { |
| 753 | int rc = 0; |
| 754 | static char newline[2000]; |
| 755 | static char *np = newline; |
| 756 | |
| 757 | /* Skip up to the first boundary */ |
| 758 | if (content_top->boundary) { |
| 759 | if (!find_boundary()) |
| 760 | return; |
| 761 | } |
| 762 | |
| 763 | do { |
| 764 | /* process any boundary lines */ |
| 765 | if (content_top->boundary && is_multipart_boundary(line)) { |
| 766 | /* flush any leftover */ |
| 767 | if ((transfer_encoding == TE_BASE64) && |
| 768 | (np != newline)) { |
| 769 | handle_filter(newline); |
| 770 | } |
| 771 | if (!handle_boundary()) |
| 772 | return; |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 773 | } |
| 774 | |
Don Zickus | 86747c1 | 2007-03-30 12:18:45 -0400 | [diff] [blame] | 775 | /* Unwrap transfer encoding */ |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 776 | decode_transfer_encoding(line); |
Eric W. Biederman | 8b4525f | 2006-05-23 13:47:28 -0600 | [diff] [blame] | 777 | |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 778 | switch (transfer_encoding) { |
| 779 | case TE_BASE64: |
| 780 | { |
| 781 | char *op = line; |
| 782 | |
| 783 | /* binary data most likely doesn't have newlines */ |
| 784 | if (message_type != TYPE_TEXT) { |
| 785 | rc = handle_filter(line); |
| 786 | break; |
| 787 | } |
| 788 | |
| 789 | /* this is a decoded line that may contain |
| 790 | * multiple new lines. Pass only one chunk |
| 791 | * at a time to handle_filter() |
| 792 | */ |
| 793 | |
| 794 | do { |
| 795 | while (*op != '\n' && *op != 0) |
| 796 | *np++ = *op++; |
| 797 | *np = *op; |
| 798 | if (*np != 0) { |
| 799 | /* should be sitting on a new line */ |
| 800 | *(++np) = 0; |
| 801 | op++; |
| 802 | rc = handle_filter(newline); |
| 803 | np = newline; |
| 804 | } |
| 805 | } while (*op != 0); |
| 806 | /* the partial chunk is saved in newline and |
| 807 | * will be appended by the next iteration of fgets |
| 808 | */ |
| 809 | break; |
| 810 | } |
| 811 | default: |
| 812 | rc = handle_filter(line); |
| 813 | } |
| 814 | if (rc) |
| 815 | /* nothing left to filter */ |
| 816 | break; |
| 817 | } while (fgets(line, sizeof(line), fin)); |
| 818 | |
| 819 | return; |
| 820 | } |
| 821 | |
| 822 | static void handle_info(void) |
| 823 | { |
| 824 | char *sub; |
| 825 | char *hdr; |
| 826 | int i; |
| 827 | |
| 828 | for (i = 0; header[i]; i++) { |
| 829 | |
| 830 | /* only print inbody headers if we output a patch file */ |
| 831 | if (patch_lines && s_hdr_data[i]) |
| 832 | hdr = s_hdr_data[i]; |
| 833 | else if (p_hdr_data[i]) |
| 834 | hdr = p_hdr_data[i]; |
| 835 | else |
Eric W. Biederman | 8b4525f | 2006-05-23 13:47:28 -0600 | [diff] [blame] | 836 | continue; |
| 837 | |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 838 | if (!memcmp(header[i], "Subject", 7)) { |
| 839 | sub = cleanup_subject(hdr); |
| 840 | cleanup_space(sub); |
| 841 | fprintf(fout, "Subject: %s\n", sub); |
| 842 | } else if (!memcmp(header[i], "From", 4)) { |
| 843 | handle_from(hdr); |
| 844 | fprintf(fout, "Author: %s\n", name); |
| 845 | fprintf(fout, "Email: %s\n", email); |
| 846 | } else { |
| 847 | cleanup_space(hdr); |
| 848 | fprintf(fout, "%s: %s\n", header[i], hdr); |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 849 | } |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 850 | } |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 851 | fprintf(fout, "\n"); |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 852 | } |
| 853 | |
Junio C Hamano | fcd056a | 2007-06-08 02:22:56 -0700 | [diff] [blame] | 854 | static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, |
| 855 | const char *msg, const char *patch) |
Lukas Sandström | 34488e3 | 2006-06-13 22:21:50 +0200 | [diff] [blame] | 856 | { |
| 857 | keep_subject = ks; |
| 858 | metainfo_charset = encoding; |
| 859 | fin = in; |
| 860 | fout = out; |
| 861 | |
| 862 | cmitmsg = fopen(msg, "w"); |
| 863 | if (!cmitmsg) { |
| 864 | perror(msg); |
| 865 | return -1; |
| 866 | } |
| 867 | patchfile = fopen(patch, "w"); |
| 868 | if (!patchfile) { |
| 869 | perror(patch); |
| 870 | fclose(cmitmsg); |
| 871 | return -1; |
| 872 | } |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 873 | |
| 874 | p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *)); |
| 875 | s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *)); |
| 876 | |
| 877 | /* process the email header */ |
| 878 | while (read_one_header_line(line, sizeof(line), fin)) |
Don Zickus | 86747c1 | 2007-03-30 12:18:45 -0400 | [diff] [blame] | 879 | check_header(line, p_hdr_data, 1); |
Don Zickus | 87ab799 | 2007-03-12 15:52:04 -0400 | [diff] [blame] | 880 | |
| 881 | handle_body(); |
| 882 | handle_info(); |
Lukas Sandström | 34488e3 | 2006-06-13 22:21:50 +0200 | [diff] [blame] | 883 | |
| 884 | return 0; |
| 885 | } |
| 886 | |
Junio C Hamano | 6bff6a6 | 2005-08-16 22:18:27 -0700 | [diff] [blame] | 887 | static const char mailinfo_usage[] = |
Junio C Hamano | 9f63892 | 2005-11-28 01:29:52 -0800 | [diff] [blame] | 888 | "git-mailinfo [-k] [-u | --encoding=<encoding>] msg patch <mail >info"; |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 889 | |
Linus Torvalds | a633fca | 2006-07-28 22:44:25 -0700 | [diff] [blame] | 890 | int cmd_mailinfo(int argc, const char **argv, const char *prefix) |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 891 | { |
Junio C Hamano | bb1091a | 2007-01-09 21:31:36 -0800 | [diff] [blame] | 892 | const char *def_charset; |
| 893 | |
Junio C Hamano | f1f909e | 2005-11-27 16:29:38 -0800 | [diff] [blame] | 894 | /* NEEDSWORK: might want to do the optional .git/ directory |
| 895 | * discovery |
| 896 | */ |
| 897 | git_config(git_default_config); |
| 898 | |
Junio C Hamano | bb1091a | 2007-01-09 21:31:36 -0800 | [diff] [blame] | 899 | def_charset = (git_commit_encoding ? git_commit_encoding : "utf-8"); |
| 900 | metainfo_charset = def_charset; |
| 901 | |
Junio C Hamano | 6bff6a6 | 2005-08-16 22:18:27 -0700 | [diff] [blame] | 902 | while (1 < argc && argv[1][0] == '-') { |
| 903 | if (!strcmp(argv[1], "-k")) |
| 904 | keep_subject = 1; |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 905 | else if (!strcmp(argv[1], "-u")) |
Junio C Hamano | bb1091a | 2007-01-09 21:31:36 -0800 | [diff] [blame] | 906 | metainfo_charset = def_charset; |
| 907 | else if (!strcmp(argv[1], "-n")) |
| 908 | metainfo_charset = NULL; |
Junio C Hamano | cc44c76 | 2007-02-20 01:53:29 -0800 | [diff] [blame] | 909 | else if (!prefixcmp(argv[1], "--encoding=")) |
Junio C Hamano | 9f63892 | 2005-11-28 01:29:52 -0800 | [diff] [blame] | 910 | metainfo_charset = argv[1] + 11; |
Junio C Hamano | d4a9ce7 | 2005-08-28 12:33:16 -0700 | [diff] [blame] | 911 | else |
Junio C Hamano | f1f909e | 2005-11-27 16:29:38 -0800 | [diff] [blame] | 912 | usage(mailinfo_usage); |
Junio C Hamano | 6bff6a6 | 2005-08-16 22:18:27 -0700 | [diff] [blame] | 913 | argc--; argv++; |
| 914 | } |
| 915 | |
Linus Torvalds | a196d8d | 2005-06-23 09:40:23 -0700 | [diff] [blame] | 916 | if (argc != 3) |
Junio C Hamano | f1f909e | 2005-11-27 16:29:38 -0800 | [diff] [blame] | 917 | usage(mailinfo_usage); |
Lukas Sandström | 34488e3 | 2006-06-13 22:21:50 +0200 | [diff] [blame] | 918 | |
| 919 | return !!mailinfo(stdin, stdout, keep_subject, metainfo_charset, argv[1], argv[2]); |
Linus Torvalds | 2744b23 | 2005-04-11 23:46:50 -0700 | [diff] [blame] | 920 | } |