/[mailutils]/mailutils/mailbox/rfc2047.c
ViewVC logotype

Diff of /mailutils/mailbox/rfc2047.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.4 by gray, Mon Feb 3 11:31:06 2003 UTC revision 1.5 by gobry, Fri Feb 7 14:13:28 2003 UTC
# Line 157  rfc2047_decode (const char *tocode, cons Line 157  rfc2047_decode (const char *tocode, cons
157    return status;    return status;
158  }  }
159    
160    
161    
162    /* --------------------------------------------------
163       RFC 2047 Encoder
164       -------------------------------------------------- */
165    
166    #define MAX_QUOTE 75
167    
168    /* Be more conservative in what we quote. This is never a problem for
169       the recipient, except for the extra overhead in the message size */
170    static int
171    must_quote (char c)
172    {
173      if (((c >  32) && (c <= 57))  ||
174          ((c >= 64) && (c <= 126)))
175        return 0;
176    
177      return 1;
178    }
179    
180    typedef struct _encoder rfc2047_encoder;
181    
182    struct _encoder {
183      char   encoding;
184      const char * charset;
185    
186      int must_open;
187    
188      const unsigned char * src;
189      char * dst, * result;
190    
191      int todo, done, quotesize;
192    
193      int  (* count) (rfc2047_encoder * enc);
194      void (* next)  (rfc2047_encoder * enc);
195      void (* flush) (rfc2047_encoder * enc);
196    
197      unsigned char buffer [4];
198    
199      int  state;
200    
201    };
202    
203    
204    static int
205    _open_quote (const char * charset,
206                char encoding,
207                char ** dst, int * done)
208    {
209      int len = strlen (charset) + 5;
210    
211      (* done) += len;
212      
213      if (* dst)
214        {
215          sprintf (* dst, "=?%s?%c?", charset, encoding);
216          (* dst) += len;
217        }
218    
219      /* in the initial length of the quote we already count the final ?= */
220      return len + 2;
221    }
222    
223    static void
224    _close_quote (char ** dst, int * done)
225    {
226      * done += 2;
227      
228      if (* dst)
229        {
230          strcpy (* dst, "?=");
231          (* dst) += 2;
232        }
233    }
234    
235    static void
236    init_quoted (rfc2047_encoder * enc)
237    {
238      enc->must_open = 1;
239    }
240    
241    
242    static void
243    insert_quoted (rfc2047_encoder * enc)
244    {
245      int size;
246    
247      if (enc->must_open)
248        {
249          enc->must_open = 0;
250          enc->quotesize = _open_quote (enc->charset, enc->encoding,
251                                        & enc->dst, & enc->done);
252        }
253      else
254        {
255          size = enc->count (enc);
256      
257          if (enc->quotesize + size > MAX_QUOTE)
258            {
259              _close_quote (& enc->dst, & enc->done);
260    
261              if (enc->dst) * (enc->dst ++) = ' ';
262              enc->done ++;
263    
264              enc->quotesize = _open_quote (enc->charset, enc->encoding,
265                                            & enc->dst, & enc->done);
266            }
267        }
268      
269      enc->next (enc);
270    }
271    
272    static void
273    flush_quoted (rfc2047_encoder * enc)
274    {
275      if (enc->must_open) return;
276    
277      enc->flush (enc);
278      _close_quote (& enc->dst, & enc->done);
279    }
280    
281    
282    
283    static void
284    insert_unquoted (rfc2047_encoder * enc)
285    {
286      if (enc->dst) * (enc->dst ++) = * (enc->src);
287      enc->src ++;
288      enc->todo --;
289      enc->done ++;
290    }
291    
292    
293    static int
294    is_next_quoted (const char * src)
295    {
296      while (isspace (* src)) src ++;
297    
298      while (* src)
299        {
300          if (isspace (* src)) return 0;
301          if (must_quote (* src)) return 1;
302    
303          src ++;
304        }
305    
306      return 0;
307    }
308    
309    
310    /* Quoted-printable encoder */
311    
312    static void
313    qp_init (rfc2047_encoder * enc)
314    {
315      return;
316    }
317    
318    static int
319    qp_count (rfc2047_encoder * enc)
320    {
321      return must_quote (* enc->src) ? 3 : 1;
322    }
323    
324    static const char _hexdigit[16] = "0123456789ABCDEF";
325    
326    static void
327    qp_next (rfc2047_encoder * enc)
328    {
329      if (must_quote (* enc->src))
330        {
331          /* special encoding of space as a '_' to increase readability */
332          if (* enc->src == ' ')
333            {
334              if (enc->dst)
335                {
336                  * (enc->dst ++) = '_';
337                  enc->src ++;
338                }
339            
340            enc->done ++;
341            enc->quotesize ++;
342            }
343          else {
344            /* default encoding */
345            if (enc->dst)
346              {
347                * (enc->dst ++) = '=';
348                * (enc->dst ++) = _hexdigit [* (enc->src) >> 4];
349                * (enc->dst ++) = _hexdigit [* (enc->src) & 0xF];
350                
351                enc->src ++;
352              }
353            
354            enc->done += 3;
355            enc->quotesize += 3;
356          }
357        }
358      else
359        {
360          if (enc->dst)
361            {
362              * (enc->dst ++) = * (enc->src ++);
363            }
364    
365          enc->done ++;
366          enc->quotesize ++;
367        }
368    
369      enc->todo --;
370    }
371    
372    static void
373    qp_flush (rfc2047_encoder * enc)
374    {
375      return;
376    }
377    
378    
379    /* Base64 encoder */
380    
381    const char *b64 =
382    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
383    
384    static void
385    base64_init (rfc2047_encoder * enc)
386    {
387      enc->state = 0;
388      return;
389    }
390    
391    static int
392    base64_count (rfc2047_encoder * enc)
393    {
394      /* Count the size of the encoded block only once, at the first byte
395         transmitted. */
396      if (enc->state == 0) return 4;
397      return 0;
398    }
399    
400    static void
401    base64_next (rfc2047_encoder * enc)
402    {
403      enc->buffer [enc->state ++] = * (enc->src ++);
404    
405      enc->todo --;
406    
407      /* We have a full quantum */
408      if (enc->state >= 3)
409        {
410          if (enc->dst)
411            {
412              * (enc->dst ++) = b64 [(enc->src[0] >> 2)];
413              * (enc->dst ++) = b64 [((enc->src[0] & 0x3) << 4) | (enc->src[1] >> 4)];
414              * (enc->dst ++) = b64 [((enc->src[1] & 0xF) << 2) | (enc->src[2] >> 6)];
415              * (enc->dst ++) = b64 [(enc->src[2] & 0x3F)];
416    
417              enc->src += 3;
418            }
419    
420          enc->done += 4;
421          enc->quotesize += 4;
422    
423          enc->state = 0;
424        }
425      return;
426    }
427    
428    static void
429    base64_flush (rfc2047_encoder * enc)
430    {
431      if (enc->state == 0) return;
432    
433      if (enc->dst)
434        {
435          switch (enc->state)
436            {
437            case 1:
438              * (enc->dst ++) = b64 [(enc->src[0] >> 2)];
439              * (enc->dst ++) = b64 [((enc->src[0] & 0x3) << 4)];
440              * (enc->dst ++) = '=';
441              * (enc->dst ++) = '=';
442              break;
443            
444            case 2:
445              * (enc->dst ++) = b64 [(enc->src[0] >> 2)];
446              * (enc->dst ++) = b64 [((enc->src[0] & 0x3) << 4) | (enc->src[1] >> 4)];
447              * (enc->dst ++) = b64 [((enc->src[1] & 0xF) << 2)];
448              * (enc->dst ++) = '=';
449              break;
450            }
451        }
452    
453      enc->done += 4;
454      enc->quotesize += 4;
455      enc->state = 0;
456      return;
457    }
458    
459    
460    /* States of the RFC2047 encoder */
461    enum {
462      ST_SPACE,   /* waiting for non-quoted whitespace */
463      ST_WORD,    /* waiting for non-quoted word */
464      ST_QUOTED,  /* waiting for quoted word */
465      ST_QUOTED_SPACE, /* waiting for quoted whitespace */
466    };
467    
468    
469    
470    /**
471       Encode a header according to RFC 2047
472      
473       @param charset
474         Charset of the text to encode
475       @param encoding
476         Requested encoding (must be "base64" or "quoted-printable")
477       @param text
478         Actual text to encode
479       @param result [OUT]
480         Encoded string
481    */
482    int
483    rfc2047_encode (const char *charset, const char *encoding,
484                    const char *text, char ** result)
485    {
486      rfc2047_encoder enc;
487    
488      int is_compose;
489      int state;
490    
491      if (! charset  ||
492          ! encoding ||
493          ! text     ||
494          ! result) return EINVAL;
495    
496      do
497        {
498          if (strcmp (encoding, "base64") == 0)
499            {
500              base64_init (& enc);
501              enc.encoding = 'B';
502              enc.next  = base64_next;
503              enc.count = base64_count;
504              enc.flush = base64_flush;
505              break;
506            }
507          
508          if (strcmp (encoding, "quoted-printable") == 0)
509            {
510              qp_init (& enc);
511              enc.encoding = 'Q';
512              enc.next  = qp_next;
513              enc.count = qp_count;
514              enc.flush = qp_flush;
515              break;
516            }
517          
518          return ENOENT;
519        }
520      while (0);
521    
522      enc.dst = NULL;
523      enc.charset = charset;
524    
525      /* proceed in two passes: count, then fill */
526      for (is_compose = 0 ; is_compose <= 1 ; is_compose ++)
527        {
528          state = ST_SPACE;
529    
530          enc.src  = text;
531          enc.todo = strlen (text);
532          enc.done = 0;
533    
534          while (enc.todo)
535            {
536              
537              switch (state)
538                {
539                case ST_SPACE:
540                  if (isspace (* enc.src))
541                    {
542                      insert_unquoted (& enc);
543                      break;
544                    }
545    
546                  if (is_next_quoted (enc.src))
547                    {
548                      init_quoted (& enc);
549                      state = ST_QUOTED;
550                    }
551                  else
552                    {
553                      state = ST_WORD;
554                    }
555                  break;
556    
557                case ST_WORD:
558                  if (isspace (* enc.src))
559                    {
560                      state = ST_SPACE;
561                      break;
562                    }
563    
564                  insert_unquoted (& enc);
565                  break;
566                  
567                case ST_QUOTED:
568                  if (isspace (* enc.src))
569                    {
570                      if (is_next_quoted (enc.src))
571                        {
572                          state = ST_QUOTED_SPACE;
573                        }
574                      else
575                        {
576                          flush_quoted (& enc);
577                          state = ST_SPACE;
578                        }
579                      break;
580                    }
581    
582                  insert_quoted (& enc);
583                  break;
584                  
585                case ST_QUOTED_SPACE:
586                  if (! isspace (* enc.src))
587                    {
588                      state = ST_QUOTED;
589                      break;
590                    }
591    
592                  insert_quoted (& enc);
593                  break;
594                }
595            }
596    
597          if (state == ST_QUOTED ||
598              state == ST_QUOTED_SPACE)
599            {
600              flush_quoted (& enc);
601            }
602    
603          if (enc.dst == NULL)
604            {
605              enc.dst = malloc (enc.done + 1);
606              if (enc.dst == NULL) return -ENOMEM;
607              enc.result = enc.dst;
608            }
609        }
610    
611      * (enc.dst) = '\0';
612      * result = enc.result;
613    
614      return 0;
615    }

Legend:
Removed from v.1.4  
changed lines
  Added in v.1.5

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26