Commit b2c1b1ff b2c1b1ff400a604fa41c168351630bac3238177e by Sergey Poznyakoff

Improve compatibility with RFC 2231 and RFC 2047.

* mailbox/mimehdr.c: New file.
* mailbox/Makefile.am (libmailutils_la_SOURCES): Add
mailbox/mimehdr.c.
* mailbox/attachment.c (_header_get_param)
(_get_attachment_name, mu_message_aget_attachment_name)
(mu_message_get_attachment_name: Move to mailbox/mimehdr.c (with
edits).
(mu_message_save_attachment): Add a FIXME comment.
* include/mailutils/message.h (MU_MIMEHDR_MULTILINE)
(MU_MIMEHDR_CSINFO): New defines.
(mu_mimehdr_get_disp,mu_mimehdr_aget_disp)
(mu_mimehdr_get_param,mu_mimehdr_aget_param)
(mu_mimehdr_decode_param)
(mu_mimehdr_aget_decoded_param): New prototypes.
(mu_message_aget_attachment_name): Change signature.
(mu_message_aget_decoded_attachment_name): New prototype.

* mailbox/mutil.c (mu_hex2ul): Fix a silly bug (have
anybody ever tried to use that function?!?)
* mailbox/testsuite/Urls: Update.

* examples/mimetest.c (message_display_parts): Add a FIXME comment.
* libmu_cpp/message.cc (Message::get_attachment_name): Likewise.
* mh/mhn.c (store_handler): Likewise.
* python/libmu_py/message.c (api_message_get_attachment_name): Likewise.
1 parent d5ae81e5
...@@ -242,7 +242,8 @@ message_display_parts (mu_message_t msg, int indent) ...@@ -242,7 +242,8 @@ message_display_parts (mu_message_t msg, int indent)
242 { 242 {
243 /* Save the attachements. */ 243 /* Save the attachements. */
244 char *fname = NULL; 244 char *fname = NULL;
245 mu_message_aget_attachment_name (part, &fname); 245 /* FIXME: CS/Lang info is ignored */
246 mu_message_aget_attachment_name (part, &fname, NULL);
246 if (fname == NULL) 247 if (fname == NULL)
247 fname = mu_tempname (NULL); 248 fname = mu_tempname (NULL);
248 249
......
...@@ -119,9 +119,36 @@ extern int mu_message_encapsulate (mu_message_t msg, mu_message_t *newmsg, ...@@ -119,9 +119,36 @@ extern int mu_message_encapsulate (mu_message_t msg, mu_message_t *newmsg,
119 extern int mu_message_unencapsulate (mu_message_t msg, mu_message_t *newmsg, 119 extern int mu_message_unencapsulate (mu_message_t msg, mu_message_t *newmsg,
120 void **data); 120 void **data);
121 121
122 /* Bit values for *pflags in functions below */
123 #define MU_MIMEHDR_MULTILINE 0x01 /* Parameter was multiline */
124 #define MU_MIMEHDR_CSINFO 0x02 /* Parameter contains charset/language
125 info */
126
127 extern int mu_mimehdr_get_disp (const char *str, const char *param,
128 char *buf, size_t bufsz, size_t *retsz);
129 extern int mu_mimehdr_aget_disp (const char *str, const char *param,
130 char **pvalue);
131 extern int mu_mimehdr_get_param (const char *str, const char *param,
132 char *buf, size_t bufsz, size_t *retsz,
133 int *pflags);
134 extern int mu_mimehdr_aget_param (const char *str, const char *param,
135 char **pval, int *pflags);
136 extern int mu_mimehdr_decode_param (const char *value, int csinfo,
137 const char *charset,
138 char **pval, char **plang);
139 extern int mu_mimehdr_aget_decoded_param (const char *str, const char *param,
140 const char *charset,
141 char **pval, char **plang);
142
122 extern int mu_message_get_attachment_name (mu_message_t, char *name, 143 extern int mu_message_get_attachment_name (mu_message_t, char *name,
123 size_t bufsz, size_t* sz); 144 size_t bufsz, size_t* sz,
124 extern int mu_message_aget_attachment_name (mu_message_t, char **name); 145 int *pflags);
146 extern int mu_message_aget_attachment_name (mu_message_t, char **name,
147 int *pflags);
148 extern int mu_message_aget_decoded_attachment_name (mu_message_t msg,
149 const char *charset,
150 char **name,
151 char **plang);
125 152
126 extern int mu_message_save_to_mailbox (mu_message_t msg, 153 extern int mu_message_save_to_mailbox (mu_message_t msg,
127 mu_debug_t debug, const char *toname, 154 mu_debug_t debug, const char *toname,
......
...@@ -224,7 +224,8 @@ Message :: get_attachment_name () ...@@ -224,7 +224,8 @@ Message :: get_attachment_name ()
224 char *c_name; 224 char *c_name;
225 std::string name; 225 std::string name;
226 226
227 int status = mu_message_aget_attachment_name (msg, &c_name); 227 /* FIXME: CS/Lang info is ignored */
228 int status = mu_message_aget_attachment_name (msg, &c_name, NULL);
228 if (status) 229 if (status)
229 throw Exception ("Message::get_attachment_name", status); 230 throw Exception ("Message::get_attachment_name", status);
230 if (c_name) { 231 if (c_name) {
......
...@@ -101,6 +101,7 @@ libmailutils_la_SOURCES = \ ...@@ -101,6 +101,7 @@ libmailutils_la_SOURCES = \
101 memory_stream.c\ 101 memory_stream.c\
102 message_stream.c\ 102 message_stream.c\
103 mime.c\ 103 mime.c\
104 mimehdr.c\
104 mkfilename.c\ 105 mkfilename.c\
105 monitor.c\ 106 monitor.c\
106 msrv.c\ 107 msrv.c\
......
...@@ -185,297 +185,6 @@ _attachment_free (struct _msg_info *info, int free_message) ...@@ -185,297 +185,6 @@ _attachment_free (struct _msg_info *info, int free_message)
185 free (info); 185 free (info);
186 } 186 }
187 187
188 /* See RFC 2045, 5.1. Syntax of the Content-Type Header Field */
189 #define _ISSPECIAL(c) !!strchr ("()<>@,;:\\\"/[]?=", c)
190
191 /* _header_get_param - an auxiliary function to extract values from
192 Content-Type, Content-Disposition and similar headers.
193
194 Arguments:
195
196 FIELD_BODY Header value, complying to RFCs 2045, 2183, 2231.3;
197 DISP Disposition. Unless it is NULL, the disposition part
198 of FIELD_BODY is compared with it. If they differ,
199 the function returns MU_ERR_NOENT.
200 PARAM Name of the parameter to extract from FIELD_BODY;
201 BUF Where to extract the value to;
202 BUFSZ Size of BUF;
203 PRET Pointer to the memory location for the return buffer (see
204 below).
205 PLEN Pointer to the return size.
206
207 The function parses FIELD_BODY and extracts the value of the parameter
208 PARAM.
209
210 If BUF is not NULL and BUFSZ is not 0, the extracted value is stored into
211 BUF. At most BUFSZ-1 bytes are copied.
212
213 Otherwise, if PRET is not NULL, the function allocates enough memory to
214 hold the extracted value, copies there the result, and stores the
215 pointer to the allocated memory into the location pointed to by PRET.
216
217 If PLEN is not NULL, the size of the extracted value (without terminating
218 NUL character) is stored there.
219
220 If BUF==NULL *and* PRET==NULL, no memory is allocated, but PLEN is
221 honored anyway, i.e. unless it is NULL it receives size of the result.
222 This can be used to estimate the needed buffer size.
223
224 Return values:
225 0 on success.
226 MU_ERR_NOENT, requested parameter not found, or disposition does
227 not match DISP.
228 MU_ERR_PARSE, if FIELD_BODY does not comply to any of the abovemntioned
229 RFCs.
230 ENOMEM , if unable to allocate memory.
231 */
232
233 int
234 _header_get_param (char *field_body,
235 const char *disp,
236 const char *param,
237 char *buf, size_t bufsz,
238 char **pret, size_t *plen)
239 {
240 int res = MU_ERR_NOENT; /* Return value, pessimistic default */
241 size_t param_len = strlen (param);
242 char *p;
243 char *mem = NULL; /* Allocated memory storage */
244 size_t retlen = 0; /* Total number of bytes copied */
245 unsigned long cind = 0; /* Expected continued parameter index.
246 See RFC 2231, Section 3,
247 "Parameter Value Continuations" */
248
249 if (field_body == NULL)
250 return EINVAL;
251
252 if (bufsz == 0) /* Make sure buf value is meaningful */
253 buf = NULL;
254
255 p = strchr (field_body, ';');
256 if (!p)
257 return MU_ERR_NOENT;
258 if (disp && mu_c_strncasecmp (field_body, disp, p - field_body))
259 return MU_ERR_NOENT;
260
261 while (p && *p)
262 {
263 char *v, *e;
264 size_t len, escaped_chars = 0;
265
266 if (*p != ';')
267 {
268 res = MU_ERR_PARSE;
269 break;
270 }
271
272 /* walk upto start of param */
273 p = mu_str_skip_class (p + 1, MU_CTYPE_SPACE);
274 if ((v = strchr (p, '=')) == NULL)
275 break;
276 v++;
277 /* Find end of the parameter */
278 if (*v == '"')
279 {
280 /* Quoted string */
281 for (e = ++v; *e != '"'; e++)
282 {
283 if (*e == 0) /* Malformed header */
284 {
285 res = MU_ERR_PARSE;
286 break;
287 }
288 if (*e == '\\')
289 {
290 if (*++e == 0)
291 {
292 res = MU_ERR_PARSE;
293 break;
294 }
295 escaped_chars++;
296 }
297 }
298 if (res == MU_ERR_PARSE)
299 break;
300 len = e - v;
301 e++;
302 }
303 else
304 {
305 for (e = v + 1; !(_ISSPECIAL (*e) || mu_isspace (*e)); e++)
306 ;
307 len = e - v;
308 }
309
310 /* Is it our parameter? */
311 if (mu_c_strncasecmp (p, param, param_len))
312 { /* nope, jump to next */
313 p = strchr (e, ';');
314 continue;
315 }
316
317 res = 0; /* Indicate success */
318
319 if (p[param_len] == '*')
320 {
321 /* Parameter value continuation (RFC 2231, Section 3).
322 See if the index is OK */
323 char *end;
324 unsigned long n = strtoul (p + param_len + 1, &end, 10);
325 if (*end != '=' || n != cind)
326 {
327 res = MU_ERR_PARSE;
328 break;
329 }
330 /* Everything OK, increase the estimation */
331 cind++;
332 }
333
334 /* Prepare P for the next iteration */
335 p = e;
336
337 /* Escape characters that appear in quoted-pairs are
338 semantically "invisible" (RFC 2822, Section 3.2.2,
339 "Quoted characters") */
340 len -= escaped_chars;
341
342 /* Adjust len if nearing end of the buffer */
343 if (bufsz && len >= bufsz)
344 len = bufsz - 1;
345
346 if (pret)
347 {
348 /* The caller wants us to allocate the memory */
349 if (!buf && !mem)
350 {
351 mem = malloc (len + 1);
352 if (!mem)
353 {
354 res = ENOMEM;
355 break;
356 }
357 buf = mem;
358 }
359 else if (mem)
360 {
361 /* If we got here, it means we are iterating over
362 a parameter value continuation, and cind=0 has
363 already been passed. Reallocate the memory to
364 accomodate next chunk of data. */
365 char *newmem = realloc (mem, retlen + len + 1);
366 if (!newmem)
367 {
368 res = ENOMEM;
369 break;
370 }
371 mem = newmem;
372 }
373 }
374
375 if (buf)
376 {
377 /* Actually copy the data. Buf is not NULL either because
378 the user passed it as an argument, or because we allocated
379 memory for it. */
380 if (escaped_chars)
381 {
382 int i;
383 for (i = 0; i < len; i++)
384 {
385 if (*v == '\\')
386 ++v;
387 buf[retlen + i] = *v++;
388 }
389 }
390 else
391 memcpy (buf + retlen, v, len);
392 }
393 /* Adjust total result size ... */
394 retlen += len;
395 /* ... and remaining buffer size, if necessary */
396 if (bufsz)
397 {
398 bufsz -= len;
399 if (bufsz == 0)
400 break;
401 }
402 }
403
404 if (res == 0)
405 {
406 /* Everything OK, prepare the returned data. */
407 if (buf)
408 buf[retlen] = 0;
409 if (plen)
410 *plen = retlen;
411 if (pret)
412 *pret = mem;
413 }
414 else if (mem)
415 free (mem);
416 return res;
417 }
418
419 /* Get the attachment name from MSG. See _header_get_param, for a
420 description of the rest of arguments. */
421 static int
422 _get_attachment_name (mu_message_t msg, char *buf, size_t bufsz,
423 char **pbuf, size_t *sz)
424 {
425 int ret = EINVAL;
426 mu_header_t hdr;
427 char *value = NULL;
428
429 if (!msg)
430 return ret;
431
432 if ((ret = mu_message_get_header (msg, &hdr)) != 0)
433 return ret;
434
435 ret = mu_header_aget_value (hdr, "Content-Disposition", &value);
436
437 /* If the header wasn't there, we'll fall back to Content-Type, but
438 other errors are fatal. */
439 if (ret != 0 && ret != MU_ERR_NOENT)
440 return ret;
441
442 if (ret == 0 && value != NULL)
443 {
444 ret = _header_get_param (value, "attachment",
445 "filename", buf, bufsz, pbuf, sz);
446 free (value);
447 value = NULL;
448 if (ret == 0 || ret != MU_ERR_NOENT)
449 return ret;
450 }
451
452 /* If we didn't get the name, we fall back on the Content-Type name
453 parameter. */
454
455 free (value);
456 ret = mu_header_aget_value (hdr, "Content-Type", &value);
457 if (ret == 0)
458 ret = _header_get_param (value, NULL, "name", buf, bufsz, pbuf, sz);
459 free (value);
460
461 return ret;
462 }
463
464 int
465 mu_message_aget_attachment_name (mu_message_t msg, char **name)
466 {
467 if (name == NULL)
468 return MU_ERR_OUT_PTR_NULL;
469 return _get_attachment_name (msg, NULL, 0, name, NULL);
470 }
471
472 int
473 mu_message_get_attachment_name (mu_message_t msg, char *buf, size_t bufsz,
474 size_t *sz)
475 {
476 return _get_attachment_name (msg, buf, bufsz, NULL, sz);
477 }
478
479 int 188 int
480 mu_message_save_attachment (mu_message_t msg, const char *filename, 189 mu_message_save_attachment (mu_message_t msg, const char *filename,
481 void **data) 190 void **data)
...@@ -499,7 +208,8 @@ mu_message_save_attachment (mu_message_t msg, const char *filename, ...@@ -499,7 +208,8 @@ mu_message_save_attachment (mu_message_t msg, const char *filename,
499 { 208 {
500 if (filename == NULL) 209 if (filename == NULL)
501 { 210 {
502 ret = mu_message_aget_attachment_name (msg, &partname); 211 /* FIXME: Charset info is ignored */
212 ret = mu_message_aget_attachment_name (msg, &partname, NULL);
503 if (partname) 213 if (partname)
504 fname = partname; 214 fname = partname;
505 } 215 }
......
...@@ -75,10 +75,10 @@ mu_hex2ul (char hex) ...@@ -75,10 +75,10 @@ mu_hex2ul (char hex)
75 return hex - '0'; 75 return hex - '0';
76 76
77 if (hex >= 'a' && hex <= 'z') 77 if (hex >= 'a' && hex <= 'z')
78 return hex - 'a'; 78 return hex - 'a' + 10;
79 79
80 if (hex >= 'A' && hex <= 'Z') 80 if (hex >= 'A' && hex <= 'Z')
81 return hex - 'A'; 81 return hex - 'A' + 10;
82 82
83 return -1; 83 return -1;
84 } 84 }
......
...@@ -58,7 +58,7 @@ scheme://%75%73%65%72:%70%61%73%73@%68%6f%73%74 => SUCCESS ...@@ -58,7 +58,7 @@ scheme://%75%73%65%72:%70%61%73%73@%68%6f%73%74 => SUCCESS
58 user <user> 58 user <user>
59 passwd <pass> 59 passwd <pass>
60 auth <> 60 auth <>
61 host <hest> 61 host <host>
62 port 0 62 port 0
63 path <> 63 path <>
64 64
......
...@@ -1590,7 +1590,9 @@ store_handler (mu_message_t msg, msg_part_t part, char *type, char *encoding, ...@@ -1590,7 +1590,9 @@ store_handler (mu_message_t msg, msg_part_t part, char *type, char *encoding,
1590 { 1590 {
1591 char *val; 1591 char *val;
1592 1592
1593 if (mu_message_aget_attachment_name (msg, &val) == 0) 1593 /* FIXME: Take into account CS/Lang info and recode the value
1594 if necessary */
1595 if (mu_message_aget_attachment_name (msg, &val, NULL) == 0)
1594 { 1596 {
1595 name = normalize_path (dir, val); 1597 name = normalize_path (dir, val);
1596 free (val); 1598 free (val);
......
...@@ -287,7 +287,8 @@ api_message_get_attachment_name (PyObject *self, PyObject *args) ...@@ -287,7 +287,8 @@ api_message_get_attachment_name (PyObject *self, PyObject *args)
287 if (!PyArg_ParseTuple (args, "O!", &PyMessageType, &py_msg)) 287 if (!PyArg_ParseTuple (args, "O!", &PyMessageType, &py_msg))
288 return NULL; 288 return NULL;
289 289
290 status = mu_message_aget_attachment_name (py_msg->msg, &name); 290 /* FIXME: CS/Lang info is ignored */
291 status = mu_message_aget_attachment_name (py_msg->msg, &name, NULL);
291 return status_object (status, PyString_FromString (name ? name : "")); 292 return status_object (status, PyString_FromString (name ? name : ""));
292 } 293 }
293 294
......