Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

mime.c

Go to the documentation of this file.
00001 /* ==================================================================== 
00002  * The Kannel Software License, Version 1.0 
00003  * 
00004  * Copyright (c) 2001-2008 Kannel Group  
00005  * Copyright (c) 1998-2001 WapIT Ltd.   
00006  * All rights reserved. 
00007  * 
00008  * Redistribution and use in source and binary forms, with or without 
00009  * modification, are permitted provided that the following conditions 
00010  * are met: 
00011  * 
00012  * 1. Redistributions of source code must retain the above copyright 
00013  *    notice, this list of conditions and the following disclaimer. 
00014  * 
00015  * 2. Redistributions in binary form must reproduce the above copyright 
00016  *    notice, this list of conditions and the following disclaimer in 
00017  *    the documentation and/or other materials provided with the 
00018  *    distribution. 
00019  * 
00020  * 3. The end-user documentation included with the redistribution, 
00021  *    if any, must include the following acknowledgment: 
00022  *       "This product includes software developed by the 
00023  *        Kannel Group (http://www.kannel.org/)." 
00024  *    Alternately, this acknowledgment may appear in the software itself, 
00025  *    if and wherever such third-party acknowledgments normally appear. 
00026  * 
00027  * 4. The names "Kannel" and "Kannel Group" must not be used to 
00028  *    endorse or promote products derived from this software without 
00029  *    prior written permission. For written permission, please  
00030  *    contact org@kannel.org. 
00031  * 
00032  * 5. Products derived from this software may not be called "Kannel", 
00033  *    nor may "Kannel" appear in their name, without prior written 
00034  *    permission of the Kannel Group. 
00035  * 
00036  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 
00037  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
00038  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
00039  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS 
00040  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,  
00041  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
00042  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
00043  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
00044  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
00045  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  
00046  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
00047  * ==================================================================== 
00048  * 
00049  * This software consists of voluntary contributions made by many 
00050  * individuals on behalf of the Kannel Group.  For more information on  
00051  * the Kannel Group, please see <http://www.kannel.org/>. 
00052  * 
00053  * Portions of this software are based upon software originally written at  
00054  * WapIT Ltd., Helsinki, Finland for the Kannel project.  
00055  */ 
00056 
00057 /*
00058  * mime.c - Implement MIME multipart/related handling 
00059  * 
00060  * References:
00061  *   RFC 2387 (The MIME Multipart/Related Content-type)
00062  *   RFC 2025 (Multipurpose Internet Mail Extensions [MIME])
00063  *
00064  * See gwlib/mime.h for more details on the implementation.
00065  *
00066  * Stipe Tolj <stolj@kannel.org>
00067  */
00068 
00069 #include <string.h>
00070 #include <limits.h>
00071 #include <ctype.h>
00072 
00073 #include "gwlib/gwlib.h"
00074 #include "gwlib/mime.h"
00075 
00076 struct MIMEEntity {
00077     List *headers;
00078     List *multiparts;
00079     Octstr *body;
00080     struct MIMEEntity *start;   /* in case multipart/related */
00081 };
00082 
00083 /********************************************************************
00084  * Creation and destruction routines.
00085  */
00086 
00087 MIMEEntity *mime_entity_create(void) 
00088 {
00089     MIMEEntity *e;
00090 
00091     e = gw_malloc(sizeof(MIMEEntity));
00092     e->headers = http_create_empty_headers();
00093     e->multiparts = gwlist_create();
00094     e->body = NULL;
00095     e->start = NULL;
00096 
00097     return e;
00098 }
00099 
00100 void static mime_entity_destroy_item(void *e)
00101 {
00102     mime_entity_destroy(e);
00103 }
00104 
00105 void mime_entity_destroy(MIMEEntity *e) 
00106 {
00107     gw_assert(e != NULL);
00108 
00109     if (e->headers != NULL)
00110         gwlist_destroy(e->headers, octstr_destroy_item);
00111     if (e->multiparts != NULL)
00112         gwlist_destroy(e->multiparts, mime_entity_destroy_item);
00113     octstr_destroy(e->body);
00114     e->start = NULL; /* will be destroyed on it's own via gwlist_destroy */
00115   
00116     gw_free(e);
00117 }    
00118 
00119 
00120 
00121 /********************************************************************
00122  * Helper routines. Some are derived from gwlib/http.[ch]
00123  */
00124 
00125 /*
00126  * Read some headers, i.e., until the first empty line (read and discard
00127  * the empty line as well). Return -1 for error, 0 for all headers read.
00128  */
00129 static int read_mime_headers(ParseContext *context, List *headers)
00130 {
00131     Octstr *line, *prev;
00132 
00133     if (gwlist_len(headers) == 0)
00134         prev = NULL;
00135     else
00136         prev = gwlist_get(headers, gwlist_len(headers) - 1);
00137 
00138     for (;;) {
00139         line = parse_get_line(context);
00140         if (line == NULL) {
00141             return -1;
00142         }
00143         if (octstr_len(line) == 0) {
00144             octstr_destroy(line);
00145             break;
00146         }
00147         if (isspace(octstr_get_char(line, 0)) && prev != NULL) {
00148             octstr_append(prev, line);
00149             octstr_destroy(line);
00150         } else {
00151             gwlist_append(headers, line);
00152             prev = line;
00153         }
00154     }
00155 
00156     return 0;
00157 }
00158 
00159 /* This function checks that there is a boundary parameter in the headers
00160  * for a multipart MIME object. If not, it is inserted and passed back to caller
00161  * in the variable boundary_elem.
00162  */
00163 static void fix_boundary_element(List *headers, Octstr **boundary_elem)
00164 {
00165     Octstr *value, *boundary;
00166     long len;
00167     
00168      /* 
00169       * Check if we have an boundary parameter already in the 
00170       * Content-Type header. If no, add one, otherwise parse which one 
00171       * we should use.
00172       * XXX this can be abstracted as function in gwlib/http.[ch].
00173       */
00174      value = http_header_value(headers, octstr_imm("Content-Type"));
00175      boundary = value ? http_get_header_parameter(value, octstr_imm("boundary")) : NULL;
00176 
00177      if (value == NULL) {
00178         /* we got here because it is multi-part, so... */
00179         value = octstr_create("multipart/mixed");
00180         http_header_add(headers, "Content-Type", "multipart/mixed");
00181      }
00182      if (boundary == NULL) {
00183         Octstr *v;
00184         boundary = octstr_format("_boundary_%d_%ld_%c_%c_bd%d", 
00185                                  random(), (long) time(NULL), 'A' + (random() % 26), 
00186                                  'a' + (random() % 26), random());
00187            
00188         octstr_format_append(value, "; boundary=%S", boundary);
00189       
00190         http_header_remove_all(headers, "Content-Type");
00191         http_header_add(headers, "Content-Type", octstr_get_cstr(value));
00192         if ((v = http_header_value(headers, octstr_imm("MIME-Version"))) == NULL)
00193             http_header_add(headers, "MIME-Version", "1.0");
00194         else
00195             octstr_destroy(v);
00196     } 
00197     else if ((len = octstr_len(boundary)) > 0 &&
00198              octstr_get_char(boundary, 0) == '"' && 
00199              octstr_get_char(boundary, len - 1) == '"') {
00200         octstr_delete(boundary, 0, 1);
00201         octstr_delete(boundary, len - 2, 1);      
00202     }
00203 
00204     octstr_destroy(value);
00205     if (boundary_elem)
00206         *boundary_elem = boundary;
00207     else
00208         octstr_destroy(boundary);
00209 }
00210 
00211 
00212 /********************************************************************
00213  * Mapping function from other data types, mainly Octstr and HTTP.
00214  */
00215 
00216 Octstr *mime_entity_to_octstr(MIMEEntity *m)
00217 {
00218     Octstr *mime, *boundary = NULL;
00219     List *headers;
00220     long i;
00221 
00222     gw_assert(m != NULL && m->headers != NULL);
00223 
00224     mime = octstr_create("");
00225 
00226     /* 
00227      * First of all check if we have further MIME entity dependencies,
00228      * which means we have further MIMEEntities in our m->multiparts
00229      * list. If no, then add headers and body and return. This is the
00230      * easy case. Otherwise we have to loop inside our entities.
00231      */
00232     if (gwlist_len(m->multiparts) == 0) {
00233         for (i = 0; i < gwlist_len(m->headers); i++) {
00234             octstr_append(mime, gwlist_get(m->headers, i));
00235             octstr_append(mime, octstr_imm("\r\n"));
00236         }
00237         octstr_append(mime, octstr_imm("\r\n"));
00238         if (m->body != NULL)
00239             octstr_append(mime, m->body);
00240         goto finished;
00241     }
00242 
00243     /* This call ensures boundary exists, and returns it */
00244     fix_boundary_element(m->headers, &boundary);
00245     headers = http_header_duplicate(m->headers);
00246 
00247     /* headers */
00248     for (i = 0; i < gwlist_len(headers); i++) {
00249         octstr_append(mime, gwlist_get(headers, i));
00250         octstr_append(mime, octstr_imm("\r\n"));
00251     }
00252     http_destroy_headers(headers);
00253     octstr_append(mime, octstr_imm("\r\n")); /* Mark end of headers. */
00254 
00255     /* loop through all MIME multipart entities of this entity */
00256     for (i = 0; i < gwlist_len(m->multiparts); i++) {
00257         MIMEEntity *e = gwlist_get(m->multiparts, i);
00258         Octstr *body;
00259 
00260         octstr_append(mime, octstr_imm("\r\n--"));
00261         octstr_append(mime, boundary);
00262         octstr_append(mime, octstr_imm("\r\n"));
00263 
00264         /* call ourself to produce the MIME entity body */
00265         body = mime_entity_to_octstr(e);
00266         octstr_append(mime, body);
00267 
00268         octstr_destroy(body);
00269     }
00270 
00271     octstr_append(mime, octstr_imm("\r\n--"));
00272     octstr_append(mime, boundary);
00273     octstr_append(mime, octstr_imm("--\r\n"));
00274 
00275     octstr_destroy(boundary);
00276 
00277 finished:
00278 
00279     return mime;
00280 }
00281 
00282 static Octstr *get_start_param(Octstr *content_type)
00283 {
00284      Octstr *start;
00285      int len;
00286 
00287      if (!content_type)
00288       return NULL;
00289 
00290      start = http_get_header_parameter(content_type, octstr_imm("start"));
00291      if (start && (len = octstr_len(start)) > 0 &&
00292      octstr_get_char(start, 0) == '"' && octstr_get_char(start, len-1) == '"') {
00293       octstr_delete(start, 0, 1);
00294       octstr_delete(start, len-2, 1);
00295      }
00296      
00297      return start;  
00298 }
00299 
00300 static int cid_matches(List *headers, Octstr *start)
00301 {
00302      Octstr *cid = http_header_value(headers, octstr_imm("Content-ID"));
00303      char *cid_str;
00304      int cid_len;
00305      int ret;
00306 
00307      if (cid == NULL) 
00308          return 0;
00309      
00310      /* First, strip the <> if any. XXX some mime coders produce such messiness! */
00311      cid_str = octstr_get_cstr(cid);
00312      cid_len = octstr_len(cid);
00313      if (cid_str[0] == '<') {
00314          cid_str+=1;
00315      cid_len-=2;
00316      }
00317      if (start != NULL && cid != NULL  && 
00318      (octstr_compare(start, cid) == 0 || octstr_str_ncompare(start, cid_str, cid_len) == 0)) 
00319       ret = 1;
00320      else 
00321       ret = 0;
00322 
00323      octstr_destroy(cid);
00324      return ret;
00325 }
00326 
00327 /*
00328  * This routine is used for mime_[http|octstr]_to_entity() in order to
00329  * reduce code duplication. Basically the only difference is how the headers
00330  * are parsed or passed to the resulting MIMEEntity representation.
00331  */
00332 static MIMEEntity *mime_something_to_entity(Octstr *mime, List *headers)
00333 {
00334     MIMEEntity *e;
00335     ParseContext *context;
00336     Octstr *value, *boundary, *start;
00337     int len = 0;
00338 
00339     gw_assert(mime != NULL);
00340 
00341     value = boundary = start = NULL;
00342     context = parse_context_create(mime);
00343     e = mime_entity_create();
00344     
00345     /* parse the headers up to the body. If we have headers already passed 
00346      * from our caller, then duplicate them and continue */
00347     if (headers != NULL) {
00348         /* we have some headers to duplicate, first ensure we destroy
00349          * the list from the previous creation inside mime_entity_create() */
00350         http_destroy_headers(e->headers);
00351         e->headers = http_header_duplicate(headers);
00352     } else {
00353         /* parse the headers out of the mime block */
00354         if ((read_mime_headers(context, e->headers) != 0) || e->headers == NULL) {
00355             debug("mime.parse",0,"Failed to read MIME headers in Octstr block:");
00356             octstr_dump(mime, 0);
00357             mime_entity_destroy(e);
00358             parse_context_destroy(context);
00359             return NULL;
00360         }
00361     }
00362 
00363     /* 
00364      * Now check if the body is a multipart. This is indicated by an 'boundary'
00365      * parameter in the 'Content-Type' value. If yes, call ourself for the 
00366      * multipart entities after parsing them.
00367      */
00368     value = http_header_value(e->headers, octstr_imm("Content-Type"));
00369     boundary = http_get_header_parameter(value, octstr_imm("boundary"));
00370     start = get_start_param(value);
00371 
00372     /* Beware that we need *unquoted* strings to compare against in the
00373      * following parsing sections. */
00374     if (boundary && (len = octstr_len(boundary)) > 0 &&
00375         octstr_get_char(boundary, 0) == '"' && octstr_get_char(boundary, len-1) == '"') {
00376         octstr_delete(boundary, 0, 1);
00377         octstr_delete(boundary, len-2, 1);
00378 
00379     }
00380 
00381     if (boundary != NULL) {
00382         /* we have a multipart block as body, parse the boundary blocks */
00383         Octstr *entity, *seperator, *os;
00384         
00385         /* loop by all boundary blocks we have in the body */
00386         seperator = octstr_create("--");
00387         octstr_append(seperator, boundary);
00388         while ((entity = parse_get_seperated_block(context, seperator)) != NULL) {
00389             MIMEEntity *m;
00390             int del2 = 0;
00391         
00392             /* we have still linefeeds at the beginning and end that we 
00393              * need to remove, these are from the separator. 
00394              * We check if it is LF only or CRLF! */
00395             del2 = (octstr_get_char(entity, 0) == '\r');
00396             if (del2) 
00397                 octstr_delete(entity, 0, 2);          
00398             else
00399                 octstr_delete(entity, 0, 1);
00400 
00401             /* we assume the same mechanism applies to beginning and end -- 
00402              * seems reasonable! */
00403             if (del2)
00404                 octstr_delete(entity, octstr_len(entity) - 2, 2);
00405             else
00406                 octstr_delete(entity, octstr_len(entity) - 1, 1);
00407 
00408             debug("mime.parse",0,"MIME multipart: Parsing entity:");
00409             octstr_dump(entity, 0);
00410 
00411             /* call ourself for this MIME entity and inject to list */
00412             if ((m = mime_octstr_to_entity(entity))) {
00413                 gwlist_append(e->multiparts, m);
00414          
00415                 /* check if this entity is our start entity (in terms of related)
00416                  * and set our start pointer to it */
00417                 if (cid_matches(m->headers, start)) {
00418                     /* set only if none has been set before */
00419                     e->start = (e->start == NULL) ? m : e->start;
00420                 }
00421             }
00422 
00423             octstr_destroy(entity);
00424         }
00425         /* ok, we parsed all blocks, we expect to see now the end boundary */
00426         octstr_append_cstr(seperator, "--");
00427         os = parse_get_line(context);
00428         if (os != NULL && octstr_compare(os, seperator) != 0) {
00429             debug("mime.parse",0,"Failed to see end boundary, parsed line is '%s'.",
00430                   octstr_get_cstr(os));
00431         }
00432 
00433         octstr_destroy(seperator);
00434         octstr_destroy(os);
00435     }
00436     else {
00437 
00438         /* we don't have boundaries, so this is no multipart block, 
00439          * pass the body to the MIME entity. */
00440         e->body = parse_get_rest(context);
00441 
00442     }
00443 
00444     parse_context_destroy(context);
00445     octstr_destroy(value);
00446     octstr_destroy(boundary);
00447     octstr_destroy(start);
00448 
00449     return e;
00450 }
00451 
00452 
00453 MIMEEntity *mime_octstr_to_entity(Octstr *mime)
00454 {
00455     gw_assert(mime != NULL);
00456 
00457     return mime_something_to_entity(mime, NULL);
00458 }
00459 
00460 
00461 MIMEEntity *mime_http_to_entity(List *headers, Octstr *body)
00462 {
00463     gw_assert(headers != NULL && body != NULL);
00464 
00465     return mime_something_to_entity(body, headers);
00466 }
00467 
00468 
00469 List *mime_entity_headers(MIMEEntity *m)
00470 {
00471     List *headers;
00472 
00473     gw_assert(m != NULL && m->headers != NULL);
00474 
00475     /* Need a fixup before hand over. */
00476     if (mime_entity_num_parts(m) > 0)
00477        fix_boundary_element(m->headers, NULL);
00478 
00479     headers = http_header_duplicate(m->headers);
00480 
00481     return headers;
00482 }
00483 
00484 
00485 Octstr *mime_entity_body(MIMEEntity *m)
00486 {
00487     Octstr *os, *body;
00488     ParseContext *context;
00489     MIMEEntity *e;
00490 
00491     gw_assert(m != NULL && m->headers != NULL);
00492 
00493     /* For non-multipart, return body directly. */
00494     if (mime_entity_num_parts(m) == 0)
00495      return octstr_duplicate(m->body);
00496 
00497     os = mime_entity_to_octstr(m);
00498     context = parse_context_create(os);
00499     e = mime_entity_create();
00500 
00501     /* parse the headers up to the body */
00502     if ((read_mime_headers(context, e->headers) != 0) || e->headers == NULL) {
00503         debug("mime.parse",0,"Failed to read MIME headers in Octstr block:");
00504         octstr_dump(os, 0);
00505         mime_entity_destroy(e);
00506         parse_context_destroy(context);
00507         return NULL;
00508     }
00509 
00510     /* the rest is the body */
00511     body = parse_get_rest(context);
00512 
00513     octstr_destroy(os);
00514     mime_entity_destroy(e);
00515     parse_context_destroy(context);
00516 
00517     return body;
00518 }
00519 
00520 /* Make a copy of a mime object. recursively. */
00521 MIMEEntity *mime_entity_duplicate(MIMEEntity *e)
00522 {
00523      MIMEEntity *copy = mime_entity_create();
00524      int i, n;
00525      
00526      mime_replace_headers(copy, e->headers);
00527      copy->body = e->body ? octstr_duplicate(e->body) : NULL;
00528      
00529      for (i = 0, n = gwlist_len(e->multiparts); i < n; i++)
00530       gwlist_append(copy->multiparts, 
00531             mime_entity_duplicate(gwlist_get(e->multiparts, i)));
00532      return copy;
00533 }
00534 
00535 /* Replace top-level MIME headers: Old ones removed completetly */
00536 void mime_replace_headers(MIMEEntity *e, List *headers)
00537 {
00538      gw_assert(e != NULL);
00539      gw_assert(headers != NULL);
00540 
00541      http_destroy_headers(e->headers);
00542      e->headers = http_header_duplicate(headers);
00543      e->start = NULL; /* clear it, since header change means it could have changed.*/
00544 }
00545 
00546 
00547 /* Get number of body parts. Returns 0 if this is not
00548  * a multipart object.
00549  */
00550 int mime_entity_num_parts(MIMEEntity *e)
00551 {
00552      gw_assert(e != NULL);
00553      return e->multiparts ? gwlist_len(e->multiparts) : 0;
00554 }
00555 
00556 
00557 /* Append  a new part to list of body parts. Copy is made
00558  * Note that if it was not multipart, this action makes it so!
00559  */ 
00560 void mime_entity_add_part(MIMEEntity *e, MIMEEntity *part)
00561 {
00562      gw_assert(e != NULL);
00563      gw_assert(part != NULL);
00564      
00565      gwlist_append(e->multiparts, mime_entity_duplicate(part));
00566 }
00567 
00568 
00569 /* Get part i in list of body parts. Copy is made*/ 
00570 MIMEEntity *mime_entity_get_part(MIMEEntity *e, int i)
00571 {
00572      MIMEEntity *m;
00573      gw_assert(e != NULL);
00574      gw_assert(i >= 0);
00575      gw_assert(i < gwlist_len(e->multiparts));
00576 
00577      m = gwlist_get(e->multiparts, i);
00578      gw_assert(m);
00579      return mime_entity_duplicate(m);
00580 }
00581 
00582 
00583 /* Remove part i in list of body parts. */ 
00584 void mime_entity_remove_part(MIMEEntity *e, int i)
00585 {
00586      MIMEEntity *m;
00587 
00588      gw_assert(e != NULL);
00589      gw_assert(i >= 0);
00590      gw_assert(i < gwlist_len(e->multiparts));
00591      
00592      
00593      m = gwlist_get(e->multiparts, i);
00594      gwlist_delete(e->multiparts, i, 1);
00595      if (m == e->start) e->start = NULL;
00596 
00597      mime_entity_destroy(m);
00598 }
00599 
00600 /* Replace part i in list of body parts.  Old one will be deleted */ 
00601 void mime_entity_replace_part(MIMEEntity *e, int i, MIMEEntity *newpart)
00602 {
00603 
00604      MIMEEntity *m;
00605      
00606      gw_assert(e != NULL);
00607      gw_assert(i >= 0);
00608      gw_assert(i < gwlist_len(e->multiparts));
00609      
00610      m = gwlist_get(e->multiparts, i);
00611      gwlist_delete(e->multiparts, i, 1);
00612      gwlist_insert(e->multiparts, i, mime_entity_duplicate(newpart));
00613      if (m == e->start) e->start = NULL;
00614 
00615      mime_entity_destroy(m);
00616 }
00617 
00618 /* Change body element of non-multipart entity.
00619  * We don't check that object is multi part. Result is just that 
00620  * body will be ignored.
00621  */
00622 void mime_entity_set_body(MIMEEntity *e, Octstr *body)
00623 {
00624      gw_assert(e != NULL);
00625      gw_assert(body != NULL);
00626 
00627      if (e->body)
00628       octstr_destroy(e->body);
00629      e->body = octstr_duplicate(body);
00630 }
00631 
00632 /* Returns (copy of) the 'start' element of a multi-part entity. */
00633 MIMEEntity *mime_multipart_start_elem(MIMEEntity *e)
00634 {
00635      gw_assert(e != NULL);
00636      
00637     /* If e->start element is not yet set, set it as follows:
00638      * - if content type is not set, then set it to NULL
00639      * - if the start element is not set but this is a multipart object, set
00640      *   it to first multipart element, else set it to null
00641      * - if the start element of the content type is set, find a matching object
00642      *    and set e->start accordingly.
00643      * Finally, return a copy of it.
00644      */
00645      if (!e->start) {
00646       Octstr *ctype = http_header_value(e->headers, octstr_imm("Content-Type"));
00647       Octstr *start = get_start_param(ctype);
00648       int i;
00649       
00650       if (!ctype)
00651            e->start = NULL;
00652       else if (!start) {
00653            if (gwlist_len(e->multiparts) > 0) 
00654             e->start = gwlist_get(e->multiparts, 0); 
00655            else 
00656             e->start = NULL;
00657       } else 
00658            for (i = 0; i < gwlist_len(e->multiparts); i++) {
00659             MIMEEntity *x = gwlist_get(e->multiparts, i);
00660             if (cid_matches(x->headers, start)) {
00661              e->start = x;
00662              break;
00663             }
00664            }
00665       
00666       if (ctype)
00667            octstr_destroy(ctype);
00668       if (start)
00669            octstr_destroy(start);
00670      }
00671      
00672      return (e->start) ? mime_entity_duplicate(e->start) : NULL;
00673 }
00674 
00675 /********************************************************************
00676  * Routines for debugging purposes.
00677  */
00678 
00679 static void mime_entity_dump_real(MIMEEntity *m, unsigned int level)
00680 {
00681     long i, items;
00682     Octstr *prefix, *type, *charset;
00683     unsigned int j;
00684 
00685     gw_assert(m != NULL && m->headers != NULL);
00686 
00687     prefix = octstr_create("");
00688     for (j = 0; j < level * 2; j++)
00689         octstr_append_cstr(prefix, " ");
00690 
00691     http_header_get_content_type(m->headers, &type, &charset);
00692     debug("mime.dump",0,"%sContent-Type `%s'", octstr_get_cstr(prefix),
00693           octstr_get_cstr(type));
00694     if (m->start != NULL) {
00695         Octstr *cid = http_header_value(m->start->headers, octstr_imm("Content-ID"));
00696         debug("mime.dump",0,"%sRelated to Content-ID <%s> MIMEEntity at address `%p'", 
00697               octstr_get_cstr(prefix), octstr_get_cstr(cid), m->start);
00698         octstr_destroy(cid);
00699     }
00700     items = gwlist_len(m->multiparts);
00701     debug("mime.dump",0,"%sBody contains %ld MIME entities, size %ld", octstr_get_cstr(prefix),
00702           items, (items == 0 && m->body) ? octstr_len(m->body) : -1);
00703 
00704     octstr_destroy(prefix);
00705     octstr_destroy(type);
00706     octstr_destroy(charset);
00707 
00708     for (i = 0; i < items; i++) {
00709         MIMEEntity *e = gwlist_get(m->multiparts, i);
00710 
00711         mime_entity_dump_real(e, level + 1);
00712     }
00713 
00714 }
00715 
00716 
00717 void mime_entity_dump(MIMEEntity *m)
00718 {
00719     gw_assert(m != NULL && m->headers != NULL);
00720 
00721     debug("mms",0,"Dumping MIMEEntity at address %p", m);
00722     mime_entity_dump_real(m, 0);
00723 }
00724 
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.