Annotation of embedaddon/curl/lib/mime.c, revision 1.1

1.1     ! misho       1: /***************************************************************************
        !             2:  *                                  _   _ ____  _
        !             3:  *  Project                     ___| | | |  _ \| |
        !             4:  *                             / __| | | | |_) | |
        !             5:  *                            | (__| |_| |  _ <| |___
        !             6:  *                             \___|\___/|_| \_\_____|
        !             7:  *
        !             8:  * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
        !             9:  *
        !            10:  * This software is licensed as described in the file COPYING, which
        !            11:  * you should have received as part of this distribution. The terms
        !            12:  * are also available at https://curl.haxx.se/docs/copyright.html.
        !            13:  *
        !            14:  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
        !            15:  * copies of the Software, and permit persons to whom the Software is
        !            16:  * furnished to do so, under the terms of the COPYING file.
        !            17:  *
        !            18:  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
        !            19:  * KIND, either express or implied.
        !            20:  *
        !            21:  ***************************************************************************/
        !            22: 
        !            23: #include "curl_setup.h"
        !            24: 
        !            25: #include <curl/curl.h>
        !            26: 
        !            27: #include "mime.h"
        !            28: #include "non-ascii.h"
        !            29: #include "warnless.h"
        !            30: #include "urldata.h"
        !            31: #include "sendf.h"
        !            32: 
        !            33: #if (!defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME)) || \
        !            34:   !defined(CURL_DISABLE_SMTP) || !defined(CURL_DISABLE_IMAP)
        !            35: 
        !            36: #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
        !            37: #include <libgen.h>
        !            38: #endif
        !            39: 
        !            40: #include "rand.h"
        !            41: #include "slist.h"
        !            42: #include "strcase.h"
        !            43: /* The last 3 #include files should be in this order */
        !            44: #include "curl_printf.h"
        !            45: #include "curl_memory.h"
        !            46: #include "memdebug.h"
        !            47: 
        !            48: #ifdef WIN32
        !            49: # ifndef R_OK
        !            50: #  define R_OK 4
        !            51: # endif
        !            52: #endif
        !            53: 
        !            54: 
        !            55: #define READ_ERROR                      ((size_t) -1)
        !            56: #define STOP_FILLING                    ((size_t) -2)
        !            57: 
        !            58: static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems,
        !            59:                                  void *instream, bool *hasread);
        !            60: 
        !            61: /* Encoders. */
        !            62: static size_t encoder_nop_read(char *buffer, size_t size, bool ateof,
        !            63:                                 curl_mimepart *part);
        !            64: static curl_off_t encoder_nop_size(curl_mimepart *part);
        !            65: static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof,
        !            66:                                 curl_mimepart *part);
        !            67: static size_t encoder_base64_read(char *buffer, size_t size, bool ateof,
        !            68:                                 curl_mimepart *part);
        !            69: static curl_off_t encoder_base64_size(curl_mimepart *part);
        !            70: static size_t encoder_qp_read(char *buffer, size_t size, bool ateof,
        !            71:                               curl_mimepart *part);
        !            72: static curl_off_t encoder_qp_size(curl_mimepart *part);
        !            73: 
        !            74: static const mime_encoder encoders[] = {
        !            75:   {"binary", encoder_nop_read, encoder_nop_size},
        !            76:   {"8bit", encoder_nop_read, encoder_nop_size},
        !            77:   {"7bit", encoder_7bit_read, encoder_nop_size},
        !            78:   {"base64", encoder_base64_read, encoder_base64_size},
        !            79:   {"quoted-printable", encoder_qp_read, encoder_qp_size},
        !            80:   {ZERO_NULL, ZERO_NULL, ZERO_NULL}
        !            81: };
        !            82: 
        !            83: /* Base64 encoding table */
        !            84: static const char base64[] =
        !            85:   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        !            86: 
        !            87: /* Quoted-printable character class table.
        !            88:  *
        !            89:  * We cannot rely on ctype functions since quoted-printable input data
        !            90:  * is assumed to be ascii-compatible, even on non-ascii platforms. */
        !            91: #define QP_OK           1       /* Can be represented by itself. */
        !            92: #define QP_SP           2       /* Space or tab. */
        !            93: #define QP_CR           3       /* Carriage return. */
        !            94: #define QP_LF           4       /* Line-feed. */
        !            95: static const unsigned char qp_class[] = {
        !            96:  0,     0,     0,     0,     0,     0,     0,     0,            /* 00 - 07 */
        !            97:  0,     QP_SP, QP_LF, 0,     0,     QP_CR, 0,     0,            /* 08 - 0F */
        !            98:  0,     0,     0,     0,     0,     0,     0,     0,            /* 10 - 17 */
        !            99:  0,     0,     0,     0,     0,     0,     0,     0,            /* 18 - 1F */
        !           100:  QP_SP, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 20 - 27 */
        !           101:  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 28 - 2F */
        !           102:  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 30 - 37 */
        !           103:  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, 0    , QP_OK, QP_OK,        /* 38 - 3F */
        !           104:  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 40 - 47 */
        !           105:  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 48 - 4F */
        !           106:  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 50 - 57 */
        !           107:  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 58 - 5F */
        !           108:  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 60 - 67 */
        !           109:  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 68 - 6F */
        !           110:  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 70 - 77 */
        !           111:  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, 0,            /* 78 - 7F */
        !           112:  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                /* 80 - 8F */
        !           113:  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                /* 90 - 9F */
        !           114:  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                /* A0 - AF */
        !           115:  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                /* B0 - BF */
        !           116:  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                /* C0 - CF */
        !           117:  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                /* D0 - DF */
        !           118:  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                /* E0 - EF */
        !           119:  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0                 /* F0 - FF */
        !           120: };
        !           121: 
        !           122: 
        !           123: /* Binary --> hexadecimal ASCII table. */
        !           124: static const char aschex[] =
        !           125:   "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x41\x42\x43\x44\x45\x46";
        !           126: 
        !           127: 
        !           128: 
        !           129: #ifndef __VMS
        !           130: #define filesize(name, stat_data) (stat_data.st_size)
        !           131: #define fopen_read fopen
        !           132: 
        !           133: #else
        !           134: 
        !           135: #include <fabdef.h>
        !           136: /*
        !           137:  * get_vms_file_size does what it takes to get the real size of the file
        !           138:  *
        !           139:  * For fixed files, find out the size of the EOF block and adjust.
        !           140:  *
        !           141:  * For all others, have to read the entire file in, discarding the contents.
        !           142:  * Most posted text files will be small, and binary files like zlib archives
        !           143:  * and CD/DVD images should be either a STREAM_LF format or a fixed format.
        !           144:  *
        !           145:  */
        !           146: curl_off_t VmsRealFileSize(const char *name,
        !           147:                            const struct_stat *stat_buf)
        !           148: {
        !           149:   char buffer[8192];
        !           150:   curl_off_t count;
        !           151:   int ret_stat;
        !           152:   FILE * file;
        !           153: 
        !           154:   file = fopen(name, FOPEN_READTEXT); /* VMS */
        !           155:   if(file == NULL)
        !           156:     return 0;
        !           157: 
        !           158:   count = 0;
        !           159:   ret_stat = 1;
        !           160:   while(ret_stat > 0) {
        !           161:     ret_stat = fread(buffer, 1, sizeof(buffer), file);
        !           162:     if(ret_stat != 0)
        !           163:       count += ret_stat;
        !           164:   }
        !           165:   fclose(file);
        !           166: 
        !           167:   return count;
        !           168: }
        !           169: 
        !           170: /*
        !           171:  *
        !           172:  *  VmsSpecialSize checks to see if the stat st_size can be trusted and
        !           173:  *  if not to call a routine to get the correct size.
        !           174:  *
        !           175:  */
        !           176: static curl_off_t VmsSpecialSize(const char *name,
        !           177:                                  const struct_stat *stat_buf)
        !           178: {
        !           179:   switch(stat_buf->st_fab_rfm) {
        !           180:   case FAB$C_VAR:
        !           181:   case FAB$C_VFC:
        !           182:     return VmsRealFileSize(name, stat_buf);
        !           183:     break;
        !           184:   default:
        !           185:     return stat_buf->st_size;
        !           186:   }
        !           187: }
        !           188: 
        !           189: #define filesize(name, stat_data) VmsSpecialSize(name, &stat_data)
        !           190: 
        !           191: /*
        !           192:  * vmsfopenread
        !           193:  *
        !           194:  * For upload to work as expected on VMS, different optional
        !           195:  * parameters must be added to the fopen command based on
        !           196:  * record format of the file.
        !           197:  *
        !           198:  */
        !           199: static FILE * vmsfopenread(const char *file, const char *mode)
        !           200: {
        !           201:   struct_stat statbuf;
        !           202:   int result;
        !           203: 
        !           204:   result = stat(file, &statbuf);
        !           205: 
        !           206:   switch(statbuf.st_fab_rfm) {
        !           207:   case FAB$C_VAR:
        !           208:   case FAB$C_VFC:
        !           209:   case FAB$C_STMCR:
        !           210:     return fopen(file, FOPEN_READTEXT); /* VMS */
        !           211:     break;
        !           212:   default:
        !           213:     return fopen(file, FOPEN_READTEXT, "rfm=stmlf", "ctx=stm");
        !           214:   }
        !           215: }
        !           216: 
        !           217: #define fopen_read vmsfopenread
        !           218: #endif
        !           219: 
        !           220: 
        !           221: #ifndef HAVE_BASENAME
        !           222: /*
        !           223:   (Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004
        !           224:   Edition)
        !           225: 
        !           226:   The basename() function shall take the pathname pointed to by path and
        !           227:   return a pointer to the final component of the pathname, deleting any
        !           228:   trailing '/' characters.
        !           229: 
        !           230:   If the string pointed to by path consists entirely of the '/' character,
        !           231:   basename() shall return a pointer to the string "/". If the string pointed
        !           232:   to by path is exactly "//", it is implementation-defined whether '/' or "//"
        !           233:   is returned.
        !           234: 
        !           235:   If path is a null pointer or points to an empty string, basename() shall
        !           236:   return a pointer to the string ".".
        !           237: 
        !           238:   The basename() function may modify the string pointed to by path, and may
        !           239:   return a pointer to static storage that may then be overwritten by a
        !           240:   subsequent call to basename().
        !           241: 
        !           242:   The basename() function need not be reentrant. A function that is not
        !           243:   required to be reentrant is not required to be thread-safe.
        !           244: 
        !           245: */
        !           246: static char *Curl_basename(char *path)
        !           247: {
        !           248:   /* Ignore all the details above for now and make a quick and simple
        !           249:      implementation here */
        !           250:   char *s1;
        !           251:   char *s2;
        !           252: 
        !           253:   s1 = strrchr(path, '/');
        !           254:   s2 = strrchr(path, '\\');
        !           255: 
        !           256:   if(s1 && s2) {
        !           257:     path = (s1 > s2? s1 : s2) + 1;
        !           258:   }
        !           259:   else if(s1)
        !           260:     path = s1 + 1;
        !           261:   else if(s2)
        !           262:     path = s2 + 1;
        !           263: 
        !           264:   return path;
        !           265: }
        !           266: 
        !           267: #define basename(x)  Curl_basename((x))
        !           268: #endif
        !           269: 
        !           270: 
        !           271: /* Set readback state. */
        !           272: static void mimesetstate(mime_state *state, enum mimestate tok, void *ptr)
        !           273: {
        !           274:   state->state = tok;
        !           275:   state->ptr = ptr;
        !           276:   state->offset = 0;
        !           277: }
        !           278: 
        !           279: 
        !           280: /* Escape header string into allocated memory. */
        !           281: static char *escape_string(const char *src)
        !           282: {
        !           283:   size_t bytecount = 0;
        !           284:   size_t i;
        !           285:   char *dst;
        !           286: 
        !           287:   for(i = 0; src[i]; i++)
        !           288:     if(src[i] == '"' || src[i] == '\\')
        !           289:       bytecount++;
        !           290: 
        !           291:   bytecount += i;
        !           292:   dst = malloc(bytecount + 1);
        !           293:   if(!dst)
        !           294:     return NULL;
        !           295: 
        !           296:   for(i = 0; *src; src++) {
        !           297:     if(*src == '"' || *src == '\\')
        !           298:       dst[i++] = '\\';
        !           299:     dst[i++] = *src;
        !           300:   }
        !           301: 
        !           302:   dst[i] = '\0';
        !           303:   return dst;
        !           304: }
        !           305: 
        !           306: /* Check if header matches. */
        !           307: static char *match_header(struct curl_slist *hdr, const char *lbl, size_t len)
        !           308: {
        !           309:   char *value = NULL;
        !           310: 
        !           311:   if(strncasecompare(hdr->data, lbl, len) && hdr->data[len] == ':')
        !           312:     for(value = hdr->data + len + 1; *value == ' '; value++)
        !           313:       ;
        !           314:   return value;
        !           315: }
        !           316: 
        !           317: /* Get a header from an slist. */
        !           318: static char *search_header(struct curl_slist *hdrlist, const char *hdr)
        !           319: {
        !           320:   size_t len = strlen(hdr);
        !           321:   char *value = NULL;
        !           322: 
        !           323:   for(; !value && hdrlist; hdrlist = hdrlist->next)
        !           324:     value = match_header(hdrlist, hdr, len);
        !           325: 
        !           326:   return value;
        !           327: }
        !           328: 
        !           329: static char *strippath(const char *fullfile)
        !           330: {
        !           331:   char *filename;
        !           332:   char *base;
        !           333:   filename = strdup(fullfile); /* duplicate since basename() may ruin the
        !           334:                                   buffer it works on */
        !           335:   if(!filename)
        !           336:     return NULL;
        !           337:   base = strdup(basename(filename));
        !           338: 
        !           339:   free(filename); /* free temporary buffer */
        !           340: 
        !           341:   return base; /* returns an allocated string or NULL ! */
        !           342: }
        !           343: 
        !           344: /* Initialize data encoder state. */
        !           345: static void cleanup_encoder_state(mime_encoder_state *p)
        !           346: {
        !           347:   p->pos = 0;
        !           348:   p->bufbeg = 0;
        !           349:   p->bufend = 0;
        !           350: }
        !           351: 
        !           352: 
        !           353: /* Dummy encoder. This is used for 8bit and binary content encodings. */
        !           354: static size_t encoder_nop_read(char *buffer, size_t size, bool ateof,
        !           355:                                curl_mimepart *part)
        !           356: {
        !           357:   mime_encoder_state *st = &part->encstate;
        !           358:   size_t insize = st->bufend - st->bufbeg;
        !           359: 
        !           360:   (void) ateof;
        !           361: 
        !           362:   if(!size)
        !           363:     return STOP_FILLING;
        !           364: 
        !           365:   if(size > insize)
        !           366:     size = insize;
        !           367: 
        !           368:   if(size)
        !           369:     memcpy(buffer, st->buf + st->bufbeg, size);
        !           370: 
        !           371:   st->bufbeg += size;
        !           372:   return size;
        !           373: }
        !           374: 
        !           375: static curl_off_t encoder_nop_size(curl_mimepart *part)
        !           376: {
        !           377:   return part->datasize;
        !           378: }
        !           379: 
        !           380: 
        !           381: /* 7bit encoder: the encoder is just a data validity check. */
        !           382: static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof,
        !           383:                                 curl_mimepart *part)
        !           384: {
        !           385:   mime_encoder_state *st = &part->encstate;
        !           386:   size_t cursize = st->bufend - st->bufbeg;
        !           387: 
        !           388:   (void) ateof;
        !           389: 
        !           390:   if(!size)
        !           391:     return STOP_FILLING;
        !           392: 
        !           393:   if(size > cursize)
        !           394:     size = cursize;
        !           395: 
        !           396:   for(cursize = 0; cursize < size; cursize++) {
        !           397:     *buffer = st->buf[st->bufbeg];
        !           398:     if(*buffer++ & 0x80)
        !           399:       return cursize? cursize: READ_ERROR;
        !           400:     st->bufbeg++;
        !           401:   }
        !           402: 
        !           403:   return cursize;
        !           404: }
        !           405: 
        !           406: 
        !           407: /* Base64 content encoder. */
        !           408: static size_t encoder_base64_read(char *buffer, size_t size, bool ateof,
        !           409:                                 curl_mimepart *part)
        !           410: {
        !           411:   mime_encoder_state *st = &part->encstate;
        !           412:   size_t cursize = 0;
        !           413:   int i;
        !           414:   char *ptr = buffer;
        !           415: 
        !           416:   while(st->bufbeg < st->bufend) {
        !           417:     /* Line full ? */
        !           418:     if(st->pos > MAX_ENCODED_LINE_LENGTH - 4) {
        !           419:       /* Yes, we need 2 characters for CRLF. */
        !           420:       if(size < 2) {
        !           421:         if(!cursize)
        !           422:           return STOP_FILLING;
        !           423:         break;
        !           424:       }
        !           425:       *ptr++ = '\r';
        !           426:       *ptr++ = '\n';
        !           427:       st->pos = 0;
        !           428:       cursize += 2;
        !           429:       size -= 2;
        !           430:     }
        !           431: 
        !           432:     /* Be sure there is enough space and input data for a base64 group. */
        !           433:     if(size < 4) {
        !           434:       if(!cursize)
        !           435:         return STOP_FILLING;
        !           436:       break;
        !           437:     }
        !           438:     if(st->bufend - st->bufbeg < 3)
        !           439:       break;
        !           440: 
        !           441:     /* Encode three bytes as four characters. */
        !           442:     i = st->buf[st->bufbeg++] & 0xFF;
        !           443:     i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF);
        !           444:     i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF);
        !           445:     *ptr++ = base64[(i >> 18) & 0x3F];
        !           446:     *ptr++ = base64[(i >> 12) & 0x3F];
        !           447:     *ptr++ = base64[(i >> 6) & 0x3F];
        !           448:     *ptr++ = base64[i & 0x3F];
        !           449:     cursize += 4;
        !           450:     st->pos += 4;
        !           451:     size -= 4;
        !           452:   }
        !           453: 
        !           454:   /* If at eof, we have to flush the buffered data. */
        !           455:   if(ateof) {
        !           456:     if(size < 4) {
        !           457:       if(!cursize)
        !           458:         return STOP_FILLING;
        !           459:     }
        !           460:     else {
        !           461:       /* Buffered data size can only be 0, 1 or 2. */
        !           462:       ptr[2] = ptr[3] = '=';
        !           463:       i = 0;
        !           464:       switch(st->bufend - st->bufbeg) {
        !           465:       case 2:
        !           466:         i = (st->buf[st->bufbeg + 1] & 0xFF) << 8;
        !           467:         /* FALLTHROUGH */
        !           468:       case 1:
        !           469:         i |= (st->buf[st->bufbeg] & 0xFF) << 16;
        !           470:         ptr[0] = base64[(i >> 18) & 0x3F];
        !           471:         ptr[1] = base64[(i >> 12) & 0x3F];
        !           472:         if(++st->bufbeg != st->bufend) {
        !           473:           ptr[2] = base64[(i >> 6) & 0x3F];
        !           474:           st->bufbeg++;
        !           475:         }
        !           476:         cursize += 4;
        !           477:         st->pos += 4;
        !           478:         break;
        !           479:       }
        !           480:     }
        !           481:   }
        !           482: 
        !           483: #ifdef CURL_DOES_CONVERSIONS
        !           484:   /* This is now textual data, Convert character codes. */
        !           485:   if(part->easy && cursize) {
        !           486:     CURLcode result = Curl_convert_to_network(part->easy, buffer, cursize);
        !           487:     if(result)
        !           488:       return READ_ERROR;
        !           489:   }
        !           490: #endif
        !           491: 
        !           492:   return cursize;
        !           493: }
        !           494: 
        !           495: static curl_off_t encoder_base64_size(curl_mimepart *part)
        !           496: {
        !           497:   curl_off_t size = part->datasize;
        !           498: 
        !           499:   if(size <= 0)
        !           500:     return size;    /* Unknown size or no data. */
        !           501: 
        !           502:   /* Compute base64 character count. */
        !           503:   size = 4 * (1 + (size - 1) / 3);
        !           504: 
        !           505:   /* Effective character count must include CRLFs. */
        !           506:   return size + 2 * ((size - 1) / MAX_ENCODED_LINE_LENGTH);
        !           507: }
        !           508: 
        !           509: 
        !           510: /* Quoted-printable lookahead.
        !           511:  *
        !           512:  * Check if a CRLF or end of data is in input buffer at current position + n.
        !           513:  * Return -1 if more data needed, 1 if CRLF or end of data, else 0.
        !           514:  */
        !           515: static int qp_lookahead_eol(mime_encoder_state *st, int ateof, size_t n)
        !           516: {
        !           517:   n += st->bufbeg;
        !           518:   if(n >= st->bufend && ateof)
        !           519:     return 1;
        !           520:   if(n + 2 > st->bufend)
        !           521:     return ateof? 0: -1;
        !           522:   if(qp_class[st->buf[n] & 0xFF] == QP_CR &&
        !           523:      qp_class[st->buf[n + 1] & 0xFF] == QP_LF)
        !           524:     return 1;
        !           525:   return 0;
        !           526: }
        !           527: 
        !           528: /* Quoted-printable encoder. */
        !           529: static size_t encoder_qp_read(char *buffer, size_t size, bool ateof,
        !           530:                               curl_mimepart *part)
        !           531: {
        !           532:   mime_encoder_state *st = &part->encstate;
        !           533:   char *ptr = buffer;
        !           534:   size_t cursize = 0;
        !           535:   int softlinebreak;
        !           536:   char buf[4];
        !           537: 
        !           538:   /* On all platforms, input is supposed to be ASCII compatible: for this
        !           539:      reason, we use hexadecimal ASCII codes in this function rather than
        !           540:      character constants that can be interpreted as non-ascii on some
        !           541:      platforms. Preserve ASCII encoding on output too. */
        !           542:   while(st->bufbeg < st->bufend) {
        !           543:     size_t len = 1;
        !           544:     size_t consumed = 1;
        !           545:     int i = st->buf[st->bufbeg];
        !           546:     buf[0] = (char) i;
        !           547:     buf[1] = aschex[(i >> 4) & 0xF];
        !           548:     buf[2] = aschex[i & 0xF];
        !           549: 
        !           550:     switch(qp_class[st->buf[st->bufbeg] & 0xFF]) {
        !           551:     case QP_OK:          /* Not a special character. */
        !           552:       break;
        !           553:     case QP_SP:          /* Space or tab. */
        !           554:       /* Spacing must be escaped if followed by CRLF. */
        !           555:       switch(qp_lookahead_eol(st, ateof, 1)) {
        !           556:       case -1:          /* More input data needed. */
        !           557:         return cursize;
        !           558:       case 0:           /* No encoding needed. */
        !           559:         break;
        !           560:       default:          /* CRLF after space or tab. */
        !           561:         buf[0] = '\x3D';    /* '=' */
        !           562:         len = 3;
        !           563:         break;
        !           564:       }
        !           565:       break;
        !           566:     case QP_CR:         /* Carriage return. */
        !           567:       /* If followed by a line-feed, output the CRLF pair.
        !           568:          Else escape it. */
        !           569:       switch(qp_lookahead_eol(st, ateof, 0)) {
        !           570:       case -1:          /* Need more data. */
        !           571:         return cursize;
        !           572:       case 1:           /* CRLF found. */
        !           573:         buf[len++] = '\x0A';    /* Append '\n'. */
        !           574:         consumed = 2;
        !           575:         break;
        !           576:       default:          /* Not followed by LF: escape. */
        !           577:         buf[0] = '\x3D';    /* '=' */
        !           578:         len = 3;
        !           579:         break;
        !           580:       }
        !           581:       break;
        !           582:     default:            /* Character must be escaped. */
        !           583:       buf[0] = '\x3D';    /* '=' */
        !           584:       len = 3;
        !           585:       break;
        !           586:     }
        !           587: 
        !           588:     /* Be sure the encoded character fits within maximum line length. */
        !           589:     if(buf[len - 1] != '\x0A') {    /* '\n' */
        !           590:       softlinebreak = st->pos + len > MAX_ENCODED_LINE_LENGTH;
        !           591:       if(!softlinebreak && st->pos + len == MAX_ENCODED_LINE_LENGTH) {
        !           592:         /* We may use the current line only if end of data or followed by
        !           593:            a CRLF. */
        !           594:         switch(qp_lookahead_eol(st, ateof, consumed)) {
        !           595:         case -1:        /* Need more data. */
        !           596:           return cursize;
        !           597:           break;
        !           598:         case 0:         /* Not followed by a CRLF. */
        !           599:           softlinebreak = 1;
        !           600:           break;
        !           601:         }
        !           602:       }
        !           603:       if(softlinebreak) {
        !           604:         strcpy(buf, "\x3D\x0D\x0A");    /* "=\r\n" */
        !           605:         len = 3;
        !           606:         consumed = 0;
        !           607:       }
        !           608:     }
        !           609: 
        !           610:     /* If the output buffer would overflow, do not store. */
        !           611:     if(len > size) {
        !           612:       if(!cursize)
        !           613:         return STOP_FILLING;
        !           614:       break;
        !           615:     }
        !           616: 
        !           617:     /* Append to output buffer. */
        !           618:     memcpy(ptr, buf, len);
        !           619:     cursize += len;
        !           620:     ptr += len;
        !           621:     size -= len;
        !           622:     st->pos += len;
        !           623:     if(buf[len - 1] == '\x0A')    /* '\n' */
        !           624:       st->pos = 0;
        !           625:     st->bufbeg += consumed;
        !           626:   }
        !           627: 
        !           628:   return cursize;
        !           629: }
        !           630: 
        !           631: static curl_off_t encoder_qp_size(curl_mimepart *part)
        !           632: {
        !           633:   /* Determining the size can only be done by reading the data: unless the
        !           634:      data size is 0, we return it as unknown (-1). */
        !           635:   return part->datasize? -1: 0;
        !           636: }
        !           637: 
        !           638: 
        !           639: /* In-memory data callbacks. */
        !           640: /* Argument is a pointer to the mime part. */
        !           641: static size_t mime_mem_read(char *buffer, size_t size, size_t nitems,
        !           642:                             void *instream)
        !           643: {
        !           644:   curl_mimepart *part = (curl_mimepart *) instream;
        !           645:   size_t sz = curlx_sotouz(part->datasize - part->state.offset);
        !           646:   (void) size;   /* Always 1.*/
        !           647: 
        !           648:   if(!nitems)
        !           649:     return STOP_FILLING;
        !           650: 
        !           651:   if(sz > nitems)
        !           652:     sz = nitems;
        !           653: 
        !           654:   if(sz)
        !           655:     memcpy(buffer, part->data + curlx_sotouz(part->state.offset), sz);
        !           656: 
        !           657:   return sz;
        !           658: }
        !           659: 
        !           660: static int mime_mem_seek(void *instream, curl_off_t offset, int whence)
        !           661: {
        !           662:   curl_mimepart *part = (curl_mimepart *) instream;
        !           663: 
        !           664:   switch(whence) {
        !           665:   case SEEK_CUR:
        !           666:     offset += part->state.offset;
        !           667:     break;
        !           668:   case SEEK_END:
        !           669:     offset += part->datasize;
        !           670:     break;
        !           671:   }
        !           672: 
        !           673:   if(offset < 0 || offset > part->datasize)
        !           674:     return CURL_SEEKFUNC_FAIL;
        !           675: 
        !           676:   part->state.offset = offset;
        !           677:   return CURL_SEEKFUNC_OK;
        !           678: }
        !           679: 
        !           680: static void mime_mem_free(void *ptr)
        !           681: {
        !           682:   Curl_safefree(((curl_mimepart *) ptr)->data);
        !           683: }
        !           684: 
        !           685: 
        !           686: /* Named file callbacks. */
        !           687: /* Argument is a pointer to the mime part. */
        !           688: static int mime_open_file(curl_mimepart * part)
        !           689: {
        !           690:   /* Open a MIMEKIND_FILE part. */
        !           691: 
        !           692:   if(part->fp)
        !           693:     return 0;
        !           694:   part->fp = fopen_read(part->data, "rb");
        !           695:   return part->fp? 0: -1;
        !           696: }
        !           697: 
        !           698: static size_t mime_file_read(char *buffer, size_t size, size_t nitems,
        !           699:                              void *instream)
        !           700: {
        !           701:   curl_mimepart *part = (curl_mimepart *) instream;
        !           702: 
        !           703:   if(!nitems)
        !           704:     return STOP_FILLING;
        !           705: 
        !           706:   if(mime_open_file(part))
        !           707:     return READ_ERROR;
        !           708: 
        !           709:   return fread(buffer, size, nitems, part->fp);
        !           710: }
        !           711: 
        !           712: static int mime_file_seek(void *instream, curl_off_t offset, int whence)
        !           713: {
        !           714:   curl_mimepart *part = (curl_mimepart *) instream;
        !           715: 
        !           716:   if(whence == SEEK_SET && !offset && !part->fp)
        !           717:     return CURL_SEEKFUNC_OK;   /* Not open: implicitly already at BOF. */
        !           718: 
        !           719:   if(mime_open_file(part))
        !           720:     return CURL_SEEKFUNC_FAIL;
        !           721: 
        !           722:   return fseek(part->fp, (long) offset, whence)?
        !           723:                CURL_SEEKFUNC_CANTSEEK: CURL_SEEKFUNC_OK;
        !           724: }
        !           725: 
        !           726: static void mime_file_free(void *ptr)
        !           727: {
        !           728:   curl_mimepart *part = (curl_mimepart *) ptr;
        !           729: 
        !           730:   if(part->fp) {
        !           731:     fclose(part->fp);
        !           732:     part->fp = NULL;
        !           733:   }
        !           734:   Curl_safefree(part->data);
        !           735:   part->data = NULL;
        !           736: }
        !           737: 
        !           738: 
        !           739: /* Subparts callbacks. */
        !           740: /* Argument is a pointer to the mime structure. */
        !           741: 
        !           742: /* Readback a byte string segment. */
        !           743: static size_t readback_bytes(mime_state *state,
        !           744:                              char *buffer, size_t bufsize,
        !           745:                              const char *bytes, size_t numbytes,
        !           746:                              const char *trail)
        !           747: {
        !           748:   size_t sz;
        !           749:   size_t offset = curlx_sotouz(state->offset);
        !           750: 
        !           751:   if(numbytes > offset) {
        !           752:     sz = numbytes - offset;
        !           753:     bytes += offset;
        !           754:   }
        !           755:   else {
        !           756:     size_t tsz = strlen(trail);
        !           757: 
        !           758:     sz = offset - numbytes;
        !           759:     if(sz >= tsz)
        !           760:       return 0;
        !           761:     bytes = trail + sz;
        !           762:     sz = tsz - sz;
        !           763:   }
        !           764: 
        !           765:   if(sz > bufsize)
        !           766:     sz = bufsize;
        !           767: 
        !           768:   memcpy(buffer, bytes, sz);
        !           769:   state->offset += sz;
        !           770:   return sz;
        !           771: }
        !           772: 
        !           773: /* Read a non-encoded part content. */
        !           774: static size_t read_part_content(curl_mimepart *part,
        !           775:                                 char *buffer, size_t bufsize, bool *hasread)
        !           776: {
        !           777:   size_t sz = 0;
        !           778: 
        !           779:   switch(part->lastreadstatus) {
        !           780:   case 0:
        !           781:   case CURL_READFUNC_ABORT:
        !           782:   case CURL_READFUNC_PAUSE:
        !           783:   case READ_ERROR:
        !           784:     return part->lastreadstatus;
        !           785:   default:
        !           786:     break;
        !           787:   }
        !           788: 
        !           789:   /* If we can determine we are at end of part data, spare a read. */
        !           790:   if(part->datasize != (curl_off_t) -1 &&
        !           791:      part->state.offset >= part->datasize) {
        !           792:     /* sz is already zero. */
        !           793:   }
        !           794:   else {
        !           795:     switch(part->kind) {
        !           796:     case MIMEKIND_MULTIPART:
        !           797:       /*
        !           798:        * Cannot be processed as other kinds since read function requires
        !           799:        * an additional parameter and is highly recursive.
        !           800:        */
        !           801:        sz = mime_subparts_read(buffer, 1, bufsize, part->arg, hasread);
        !           802:        break;
        !           803:     case MIMEKIND_FILE:
        !           804:       if(part->fp && feof(part->fp))
        !           805:         break;  /* At EOF. */
        !           806:       /* FALLTHROUGH */
        !           807:     default:
        !           808:       if(part->readfunc) {
        !           809:         if(!(part->flags & MIME_FAST_READ)) {
        !           810:           if(*hasread)
        !           811:             return STOP_FILLING;
        !           812:           *hasread = TRUE;
        !           813:         }
        !           814:         sz = part->readfunc(buffer, 1, bufsize, part->arg);
        !           815:       }
        !           816:       break;
        !           817:     }
        !           818:   }
        !           819: 
        !           820:   switch(sz) {
        !           821:   case STOP_FILLING:
        !           822:     break;
        !           823:   case 0:
        !           824:   case CURL_READFUNC_ABORT:
        !           825:   case CURL_READFUNC_PAUSE:
        !           826:   case READ_ERROR:
        !           827:     part->lastreadstatus = sz;
        !           828:     break;
        !           829:   default:
        !           830:     part->state.offset += sz;
        !           831:     part->lastreadstatus = sz;
        !           832:     break;
        !           833:   }
        !           834: 
        !           835:   return sz;
        !           836: }
        !           837: 
        !           838: /* Read and encode part content. */
        !           839: static size_t read_encoded_part_content(curl_mimepart *part, char *buffer,
        !           840:                                         size_t bufsize, bool *hasread)
        !           841: {
        !           842:   mime_encoder_state *st = &part->encstate;
        !           843:   size_t cursize = 0;
        !           844:   size_t sz;
        !           845:   bool ateof = FALSE;
        !           846: 
        !           847:   for(;;) {
        !           848:     if(st->bufbeg < st->bufend || ateof) {
        !           849:       /* Encode buffered data. */
        !           850:       sz = part->encoder->encodefunc(buffer, bufsize, ateof, part);
        !           851:       switch(sz) {
        !           852:       case 0:
        !           853:         if(ateof)
        !           854:           return cursize;
        !           855:         break;
        !           856:       case READ_ERROR:
        !           857:       case STOP_FILLING:
        !           858:         return cursize? cursize: sz;
        !           859:       default:
        !           860:         cursize += sz;
        !           861:         buffer += sz;
        !           862:         bufsize -= sz;
        !           863:         continue;
        !           864:       }
        !           865:     }
        !           866: 
        !           867:     /* We need more data in input buffer. */
        !           868:     if(st->bufbeg) {
        !           869:       size_t len = st->bufend - st->bufbeg;
        !           870: 
        !           871:       if(len)
        !           872:         memmove(st->buf, st->buf + st->bufbeg, len);
        !           873:       st->bufbeg = 0;
        !           874:       st->bufend = len;
        !           875:     }
        !           876:     if(st->bufend >= sizeof(st->buf))
        !           877:       return cursize? cursize: READ_ERROR;    /* Buffer full. */
        !           878:     sz = read_part_content(part, st->buf + st->bufend,
        !           879:                            sizeof(st->buf) - st->bufend, hasread);
        !           880:     switch(sz) {
        !           881:     case 0:
        !           882:       ateof = TRUE;
        !           883:       break;
        !           884:     case CURL_READFUNC_ABORT:
        !           885:     case CURL_READFUNC_PAUSE:
        !           886:     case READ_ERROR:
        !           887:     case STOP_FILLING:
        !           888:       return cursize? cursize: sz;
        !           889:     default:
        !           890:       st->bufend += sz;
        !           891:       break;
        !           892:     }
        !           893:   }
        !           894: 
        !           895:   /* NOTREACHED */
        !           896: }
        !           897: 
        !           898: /* Readback a mime part. */
        !           899: static size_t readback_part(curl_mimepart *part,
        !           900:                             char *buffer, size_t bufsize, bool *hasread)
        !           901: {
        !           902:   size_t cursize = 0;
        !           903: #ifdef CURL_DOES_CONVERSIONS
        !           904:   char *convbuf = buffer;
        !           905: #endif
        !           906: 
        !           907:   /* Readback from part. */
        !           908: 
        !           909:   while(bufsize) {
        !           910:     size_t sz = 0;
        !           911:     struct curl_slist *hdr = (struct curl_slist *) part->state.ptr;
        !           912:     switch(part->state.state) {
        !           913:     case MIMESTATE_BEGIN:
        !           914:       mimesetstate(&part->state,
        !           915:                    (part->flags & MIME_BODY_ONLY)?
        !           916:                      MIMESTATE_BODY: MIMESTATE_CURLHEADERS,
        !           917:                    part->curlheaders);
        !           918:       break;
        !           919:     case MIMESTATE_USERHEADERS:
        !           920:       if(!hdr) {
        !           921:         mimesetstate(&part->state, MIMESTATE_EOH, NULL);
        !           922:         break;
        !           923:       }
        !           924:       if(match_header(hdr, "Content-Type", 12)) {
        !           925:         mimesetstate(&part->state, MIMESTATE_USERHEADERS, hdr->next);
        !           926:         break;
        !           927:       }
        !           928:       /* FALLTHROUGH */
        !           929:     case MIMESTATE_CURLHEADERS:
        !           930:       if(!hdr)
        !           931:         mimesetstate(&part->state, MIMESTATE_USERHEADERS, part->userheaders);
        !           932:       else {
        !           933:         sz = readback_bytes(&part->state, buffer, bufsize,
        !           934:                             hdr->data, strlen(hdr->data), "\r\n");
        !           935:         if(!sz)
        !           936:           mimesetstate(&part->state, part->state.state, hdr->next);
        !           937:       }
        !           938:       break;
        !           939:     case MIMESTATE_EOH:
        !           940:       sz = readback_bytes(&part->state, buffer, bufsize, "\r\n", 2, "");
        !           941:       if(!sz)
        !           942:         mimesetstate(&part->state, MIMESTATE_BODY, NULL);
        !           943:       break;
        !           944:     case MIMESTATE_BODY:
        !           945: #ifdef CURL_DOES_CONVERSIONS
        !           946:       if(part->easy && convbuf < buffer) {
        !           947:         CURLcode result = Curl_convert_to_network(part->easy, convbuf,
        !           948:                                                   buffer - convbuf);
        !           949:         if(result)
        !           950:           return READ_ERROR;
        !           951:         convbuf = buffer;
        !           952:       }
        !           953: #endif
        !           954:       cleanup_encoder_state(&part->encstate);
        !           955:       mimesetstate(&part->state, MIMESTATE_CONTENT, NULL);
        !           956:       break;
        !           957:     case MIMESTATE_CONTENT:
        !           958:       if(part->encoder)
        !           959:         sz = read_encoded_part_content(part, buffer, bufsize, hasread);
        !           960:       else
        !           961:         sz = read_part_content(part, buffer, bufsize, hasread);
        !           962:       switch(sz) {
        !           963:       case 0:
        !           964:         mimesetstate(&part->state, MIMESTATE_END, NULL);
        !           965:         /* Try sparing open file descriptors. */
        !           966:         if(part->kind == MIMEKIND_FILE && part->fp) {
        !           967:           fclose(part->fp);
        !           968:           part->fp = NULL;
        !           969:         }
        !           970:         /* FALLTHROUGH */
        !           971:       case CURL_READFUNC_ABORT:
        !           972:       case CURL_READFUNC_PAUSE:
        !           973:       case READ_ERROR:
        !           974:       case STOP_FILLING:
        !           975:         return cursize? cursize: sz;
        !           976:       }
        !           977:       break;
        !           978:     case MIMESTATE_END:
        !           979:       return cursize;
        !           980:     default:
        !           981:       break;    /* Other values not in part state. */
        !           982:     }
        !           983: 
        !           984:     /* Bump buffer and counters according to read size. */
        !           985:     cursize += sz;
        !           986:     buffer += sz;
        !           987:     bufsize -= sz;
        !           988:   }
        !           989: 
        !           990: #ifdef CURL_DOES_CONVERSIONS
        !           991:       if(part->easy && convbuf < buffer &&
        !           992:          part->state.state < MIMESTATE_BODY) {
        !           993:         CURLcode result = Curl_convert_to_network(part->easy, convbuf,
        !           994:                                                   buffer - convbuf);
        !           995:         if(result)
        !           996:           return READ_ERROR;
        !           997:       }
        !           998: #endif
        !           999: 
        !          1000:   return cursize;
        !          1001: }
        !          1002: 
        !          1003: /* Readback from mime. Warning: not a read callback function. */
        !          1004: static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems,
        !          1005:                                  void *instream, bool *hasread)
        !          1006: {
        !          1007:   curl_mime *mime = (curl_mime *) instream;
        !          1008:   size_t cursize = 0;
        !          1009: #ifdef CURL_DOES_CONVERSIONS
        !          1010:   char *convbuf = buffer;
        !          1011: #endif
        !          1012: 
        !          1013:   (void) size;   /* Always 1. */
        !          1014: 
        !          1015:   while(nitems) {
        !          1016:     size_t sz = 0;
        !          1017:     curl_mimepart *part = mime->state.ptr;
        !          1018:     switch(mime->state.state) {
        !          1019:     case MIMESTATE_BEGIN:
        !          1020:     case MIMESTATE_BODY:
        !          1021: #ifdef CURL_DOES_CONVERSIONS
        !          1022:       convbuf = buffer;
        !          1023: #endif
        !          1024:       mimesetstate(&mime->state, MIMESTATE_BOUNDARY1, mime->firstpart);
        !          1025:       /* The first boundary always follows the header termination empty line,
        !          1026:          so is always preceded by a CRLF. We can then spare 2 characters
        !          1027:          by skipping the leading CRLF in boundary. */
        !          1028:       mime->state.offset += 2;
        !          1029:       break;
        !          1030:     case MIMESTATE_BOUNDARY1:
        !          1031:       sz = readback_bytes(&mime->state, buffer, nitems, "\r\n--", 4, "");
        !          1032:       if(!sz)
        !          1033:         mimesetstate(&mime->state, MIMESTATE_BOUNDARY2, part);
        !          1034:       break;
        !          1035:     case MIMESTATE_BOUNDARY2:
        !          1036:       sz = readback_bytes(&mime->state, buffer, nitems, mime->boundary,
        !          1037:                           strlen(mime->boundary), part? "\r\n": "--\r\n");
        !          1038:       if(!sz) {
        !          1039: #ifdef CURL_DOES_CONVERSIONS
        !          1040:         if(mime->easy && convbuf < buffer) {
        !          1041:           CURLcode result = Curl_convert_to_network(mime->easy, convbuf,
        !          1042:                                                     buffer - convbuf);
        !          1043:           if(result)
        !          1044:             return READ_ERROR;
        !          1045:           convbuf = buffer;
        !          1046:         }
        !          1047: #endif
        !          1048:         mimesetstate(&mime->state, MIMESTATE_CONTENT, part);
        !          1049:       }
        !          1050:       break;
        !          1051:     case MIMESTATE_CONTENT:
        !          1052:       if(!part) {
        !          1053:         mimesetstate(&mime->state, MIMESTATE_END, NULL);
        !          1054:         break;
        !          1055:       }
        !          1056:       sz = readback_part(part, buffer, nitems, hasread);
        !          1057:       switch(sz) {
        !          1058:       case CURL_READFUNC_ABORT:
        !          1059:       case CURL_READFUNC_PAUSE:
        !          1060:       case READ_ERROR:
        !          1061:       case STOP_FILLING:
        !          1062:         return cursize? cursize: sz;
        !          1063:       case 0:
        !          1064: #ifdef CURL_DOES_CONVERSIONS
        !          1065:         convbuf = buffer;
        !          1066: #endif
        !          1067:         mimesetstate(&mime->state, MIMESTATE_BOUNDARY1, part->nextpart);
        !          1068:         break;
        !          1069:       }
        !          1070:       break;
        !          1071:     case MIMESTATE_END:
        !          1072:       return cursize;
        !          1073:     default:
        !          1074:       break;    /* other values not used in mime state. */
        !          1075:     }
        !          1076: 
        !          1077:     /* Bump buffer and counters according to read size. */
        !          1078:     cursize += sz;
        !          1079:     buffer += sz;
        !          1080:     nitems -= sz;
        !          1081:   }
        !          1082: 
        !          1083: #ifdef CURL_DOES_CONVERSIONS
        !          1084:       if(mime->easy && convbuf < buffer &&
        !          1085:          mime->state.state <= MIMESTATE_CONTENT) {
        !          1086:         CURLcode result = Curl_convert_to_network(mime->easy, convbuf,
        !          1087:                                                   buffer - convbuf);
        !          1088:         if(result)
        !          1089:           return READ_ERROR;
        !          1090:       }
        !          1091: #endif
        !          1092: 
        !          1093:   return cursize;
        !          1094: }
        !          1095: 
        !          1096: static int mime_part_rewind(curl_mimepart *part)
        !          1097: {
        !          1098:   int res = CURL_SEEKFUNC_OK;
        !          1099:   enum mimestate targetstate = MIMESTATE_BEGIN;
        !          1100: 
        !          1101:   if(part->flags & MIME_BODY_ONLY)
        !          1102:     targetstate = MIMESTATE_BODY;
        !          1103:   cleanup_encoder_state(&part->encstate);
        !          1104:   if(part->state.state > targetstate) {
        !          1105:     res = CURL_SEEKFUNC_CANTSEEK;
        !          1106:     if(part->seekfunc) {
        !          1107:       res = part->seekfunc(part->arg, (curl_off_t) 0, SEEK_SET);
        !          1108:       switch(res) {
        !          1109:       case CURL_SEEKFUNC_OK:
        !          1110:       case CURL_SEEKFUNC_FAIL:
        !          1111:       case CURL_SEEKFUNC_CANTSEEK:
        !          1112:         break;
        !          1113:       case -1:    /* For fseek() error. */
        !          1114:         res = CURL_SEEKFUNC_CANTSEEK;
        !          1115:         break;
        !          1116:       default:
        !          1117:         res = CURL_SEEKFUNC_FAIL;
        !          1118:         break;
        !          1119:       }
        !          1120:     }
        !          1121:   }
        !          1122: 
        !          1123:   if(res == CURL_SEEKFUNC_OK)
        !          1124:     mimesetstate(&part->state, targetstate, NULL);
        !          1125: 
        !          1126:   part->lastreadstatus = 1; /* Successful read status. */
        !          1127:   return res;
        !          1128: }
        !          1129: 
        !          1130: static int mime_subparts_seek(void *instream, curl_off_t offset, int whence)
        !          1131: {
        !          1132:   curl_mime *mime = (curl_mime *) instream;
        !          1133:   curl_mimepart *part;
        !          1134:   int result = CURL_SEEKFUNC_OK;
        !          1135: 
        !          1136:   if(whence != SEEK_SET || offset)
        !          1137:     return CURL_SEEKFUNC_CANTSEEK;    /* Only support full rewind. */
        !          1138: 
        !          1139:   if(mime->state.state == MIMESTATE_BEGIN)
        !          1140:    return CURL_SEEKFUNC_OK;           /* Already rewound. */
        !          1141: 
        !          1142:   for(part = mime->firstpart; part; part = part->nextpart) {
        !          1143:     int res = mime_part_rewind(part);
        !          1144:     if(res != CURL_SEEKFUNC_OK)
        !          1145:       result = res;
        !          1146:   }
        !          1147: 
        !          1148:   if(result == CURL_SEEKFUNC_OK)
        !          1149:     mimesetstate(&mime->state, MIMESTATE_BEGIN, NULL);
        !          1150: 
        !          1151:   return result;
        !          1152: }
        !          1153: 
        !          1154: /* Release part content. */
        !          1155: static void cleanup_part_content(curl_mimepart *part)
        !          1156: {
        !          1157:   if(part->freefunc)
        !          1158:     part->freefunc(part->arg);
        !          1159: 
        !          1160:   part->readfunc = NULL;
        !          1161:   part->seekfunc = NULL;
        !          1162:   part->freefunc = NULL;
        !          1163:   part->arg = (void *) part;          /* Defaults to part itself. */
        !          1164:   part->data = NULL;
        !          1165:   part->fp = NULL;
        !          1166:   part->datasize = (curl_off_t) 0;    /* No size yet. */
        !          1167:   cleanup_encoder_state(&part->encstate);
        !          1168:   part->kind = MIMEKIND_NONE;
        !          1169:   part->flags &= ~MIME_FAST_READ;
        !          1170:   part->lastreadstatus = 1; /* Successful read status. */
        !          1171: }
        !          1172: 
        !          1173: static void mime_subparts_free(void *ptr)
        !          1174: {
        !          1175:   curl_mime *mime = (curl_mime *) ptr;
        !          1176: 
        !          1177:   if(mime && mime->parent) {
        !          1178:     mime->parent->freefunc = NULL;  /* Be sure we won't be called again. */
        !          1179:     cleanup_part_content(mime->parent);  /* Avoid dangling pointer in part. */
        !          1180:   }
        !          1181:   curl_mime_free(mime);
        !          1182: }
        !          1183: 
        !          1184: /* Do not free subparts: unbind them. This is used for the top level only. */
        !          1185: static void mime_subparts_unbind(void *ptr)
        !          1186: {
        !          1187:   curl_mime *mime = (curl_mime *) ptr;
        !          1188: 
        !          1189:   if(mime && mime->parent) {
        !          1190:     mime->parent->freefunc = NULL;  /* Be sure we won't be called again. */
        !          1191:     cleanup_part_content(mime->parent);  /* Avoid dangling pointer in part. */
        !          1192:     mime->parent = NULL;
        !          1193:   }
        !          1194: }
        !          1195: 
        !          1196: 
        !          1197: void Curl_mime_cleanpart(curl_mimepart *part)
        !          1198: {
        !          1199:   cleanup_part_content(part);
        !          1200:   curl_slist_free_all(part->curlheaders);
        !          1201:   if(part->flags & MIME_USERHEADERS_OWNER)
        !          1202:     curl_slist_free_all(part->userheaders);
        !          1203:   Curl_safefree(part->mimetype);
        !          1204:   Curl_safefree(part->name);
        !          1205:   Curl_safefree(part->filename);
        !          1206:   Curl_mime_initpart(part, part->easy);
        !          1207: }
        !          1208: 
        !          1209: /* Recursively delete a mime handle and its parts. */
        !          1210: void curl_mime_free(curl_mime *mime)
        !          1211: {
        !          1212:   curl_mimepart *part;
        !          1213: 
        !          1214:   if(mime) {
        !          1215:     mime_subparts_unbind(mime);  /* Be sure it's not referenced anymore. */
        !          1216:     while(mime->firstpart) {
        !          1217:       part = mime->firstpart;
        !          1218:       mime->firstpart = part->nextpart;
        !          1219:       Curl_mime_cleanpart(part);
        !          1220:       free(part);
        !          1221:     }
        !          1222:     free(mime);
        !          1223:   }
        !          1224: }
        !          1225: 
        !          1226: CURLcode Curl_mime_duppart(curl_mimepart *dst, const curl_mimepart *src)
        !          1227: {
        !          1228:   curl_mime *mime;
        !          1229:   curl_mimepart *d;
        !          1230:   const curl_mimepart *s;
        !          1231:   CURLcode res = CURLE_OK;
        !          1232: 
        !          1233:   DEBUGASSERT(dst);
        !          1234: 
        !          1235:   /* Duplicate content. */
        !          1236:   switch(src->kind) {
        !          1237:   case MIMEKIND_NONE:
        !          1238:     break;
        !          1239:   case MIMEKIND_DATA:
        !          1240:     res = curl_mime_data(dst, src->data, (size_t) src->datasize);
        !          1241:     break;
        !          1242:   case MIMEKIND_FILE:
        !          1243:     res = curl_mime_filedata(dst, src->data);
        !          1244:     /* Do not abort duplication if file is not readable. */
        !          1245:     if(res == CURLE_READ_ERROR)
        !          1246:       res = CURLE_OK;
        !          1247:     break;
        !          1248:   case MIMEKIND_CALLBACK:
        !          1249:     res = curl_mime_data_cb(dst, src->datasize, src->readfunc,
        !          1250:                             src->seekfunc, src->freefunc, src->arg);
        !          1251:     break;
        !          1252:   case MIMEKIND_MULTIPART:
        !          1253:     /* No one knows about the cloned subparts, thus always attach ownership
        !          1254:        to the part. */
        !          1255:     mime = curl_mime_init(dst->easy);
        !          1256:     res = mime? curl_mime_subparts(dst, mime): CURLE_OUT_OF_MEMORY;
        !          1257: 
        !          1258:     /* Duplicate subparts. */
        !          1259:     for(s = ((curl_mime *) src->arg)->firstpart; !res && s; s = s->nextpart) {
        !          1260:       d = curl_mime_addpart(mime);
        !          1261:       res = d? Curl_mime_duppart(d, s): CURLE_OUT_OF_MEMORY;
        !          1262:     }
        !          1263:     break;
        !          1264:   default:  /* Invalid kind: should not occur. */
        !          1265:     res = CURLE_BAD_FUNCTION_ARGUMENT;  /* Internal error? */
        !          1266:     break;
        !          1267:   }
        !          1268: 
        !          1269:   /* Duplicate headers. */
        !          1270:   if(!res && src->userheaders) {
        !          1271:     struct curl_slist *hdrs = Curl_slist_duplicate(src->userheaders);
        !          1272: 
        !          1273:     if(!hdrs)
        !          1274:       res = CURLE_OUT_OF_MEMORY;
        !          1275:     else {
        !          1276:       /* No one but this procedure knows about the new header list,
        !          1277:          so always take ownership. */
        !          1278:       res = curl_mime_headers(dst, hdrs, TRUE);
        !          1279:       if(res)
        !          1280:         curl_slist_free_all(hdrs);
        !          1281:     }
        !          1282:   }
        !          1283: 
        !          1284:   if(!res) {
        !          1285:     /* Duplicate other fields. */
        !          1286:     dst->encoder = src->encoder;
        !          1287:     res = curl_mime_type(dst, src->mimetype);
        !          1288:   }
        !          1289:   if(!res)
        !          1290:     res = curl_mime_name(dst, src->name);
        !          1291:   if(!res)
        !          1292:     res = curl_mime_filename(dst, src->filename);
        !          1293: 
        !          1294:   /* If an error occurred, rollback. */
        !          1295:   if(res)
        !          1296:     Curl_mime_cleanpart(dst);
        !          1297: 
        !          1298:   return res;
        !          1299: }
        !          1300: 
        !          1301: /*
        !          1302:  * Mime build functions.
        !          1303:  */
        !          1304: 
        !          1305: /* Create a mime handle. */
        !          1306: curl_mime *curl_mime_init(struct Curl_easy *easy)
        !          1307: {
        !          1308:   curl_mime *mime;
        !          1309: 
        !          1310:   mime = (curl_mime *) malloc(sizeof(*mime));
        !          1311: 
        !          1312:   if(mime) {
        !          1313:     mime->easy = easy;
        !          1314:     mime->parent = NULL;
        !          1315:     mime->firstpart = NULL;
        !          1316:     mime->lastpart = NULL;
        !          1317: 
        !          1318:     memset(mime->boundary, '-', 24);
        !          1319:     if(Curl_rand_hex(easy, (unsigned char *) &mime->boundary[24],
        !          1320:                      MIME_RAND_BOUNDARY_CHARS + 1)) {
        !          1321:       /* failed to get random separator, bail out */
        !          1322:       free(mime);
        !          1323:       return NULL;
        !          1324:     }
        !          1325:     mimesetstate(&mime->state, MIMESTATE_BEGIN, NULL);
        !          1326:   }
        !          1327: 
        !          1328:   return mime;
        !          1329: }
        !          1330: 
        !          1331: /* Initialize a mime part. */
        !          1332: void Curl_mime_initpart(curl_mimepart *part, struct Curl_easy *easy)
        !          1333: {
        !          1334:   memset((char *) part, 0, sizeof(*part));
        !          1335:   part->easy = easy;
        !          1336:   part->lastreadstatus = 1; /* Successful read status. */
        !          1337:   mimesetstate(&part->state, MIMESTATE_BEGIN, NULL);
        !          1338: }
        !          1339: 
        !          1340: /* Create a mime part and append it to a mime handle's part list. */
        !          1341: curl_mimepart *curl_mime_addpart(curl_mime *mime)
        !          1342: {
        !          1343:   curl_mimepart *part;
        !          1344: 
        !          1345:   if(!mime)
        !          1346:     return NULL;
        !          1347: 
        !          1348:   part = (curl_mimepart *) malloc(sizeof(*part));
        !          1349: 
        !          1350:   if(part) {
        !          1351:     Curl_mime_initpart(part, mime->easy);
        !          1352:     part->parent = mime;
        !          1353: 
        !          1354:     if(mime->lastpart)
        !          1355:       mime->lastpart->nextpart = part;
        !          1356:     else
        !          1357:       mime->firstpart = part;
        !          1358: 
        !          1359:     mime->lastpart = part;
        !          1360:   }
        !          1361: 
        !          1362:   return part;
        !          1363: }
        !          1364: 
        !          1365: /* Set mime part name. */
        !          1366: CURLcode curl_mime_name(curl_mimepart *part, const char *name)
        !          1367: {
        !          1368:   if(!part)
        !          1369:     return CURLE_BAD_FUNCTION_ARGUMENT;
        !          1370: 
        !          1371:   Curl_safefree(part->name);
        !          1372:   part->name = NULL;
        !          1373: 
        !          1374:   if(name) {
        !          1375:     part->name = strdup(name);
        !          1376:     if(!part->name)
        !          1377:       return CURLE_OUT_OF_MEMORY;
        !          1378:   }
        !          1379: 
        !          1380:   return CURLE_OK;
        !          1381: }
        !          1382: 
        !          1383: /* Set mime part remote file name. */
        !          1384: CURLcode curl_mime_filename(curl_mimepart *part, const char *filename)
        !          1385: {
        !          1386:   if(!part)
        !          1387:     return CURLE_BAD_FUNCTION_ARGUMENT;
        !          1388: 
        !          1389:   Curl_safefree(part->filename);
        !          1390:   part->filename = NULL;
        !          1391: 
        !          1392:   if(filename) {
        !          1393:     part->filename = strdup(filename);
        !          1394:     if(!part->filename)
        !          1395:       return CURLE_OUT_OF_MEMORY;
        !          1396:   }
        !          1397: 
        !          1398:   return CURLE_OK;
        !          1399: }
        !          1400: 
        !          1401: /* Set mime part content from memory data. */
        !          1402: CURLcode curl_mime_data(curl_mimepart *part,
        !          1403:                         const char *data, size_t datasize)
        !          1404: {
        !          1405:   if(!part)
        !          1406:     return CURLE_BAD_FUNCTION_ARGUMENT;
        !          1407: 
        !          1408:   cleanup_part_content(part);
        !          1409: 
        !          1410:   if(data) {
        !          1411:     if(datasize == CURL_ZERO_TERMINATED)
        !          1412:       datasize = strlen(data);
        !          1413: 
        !          1414:     part->data = malloc(datasize + 1);
        !          1415:     if(!part->data)
        !          1416:       return CURLE_OUT_OF_MEMORY;
        !          1417: 
        !          1418:     part->datasize = datasize;
        !          1419: 
        !          1420:     if(datasize)
        !          1421:       memcpy(part->data, data, datasize);
        !          1422:     part->data[datasize] = '\0';    /* Set a nul terminator as sentinel. */
        !          1423: 
        !          1424:     part->readfunc = mime_mem_read;
        !          1425:     part->seekfunc = mime_mem_seek;
        !          1426:     part->freefunc = mime_mem_free;
        !          1427:     part->flags |= MIME_FAST_READ;
        !          1428:     part->kind = MIMEKIND_DATA;
        !          1429:   }
        !          1430: 
        !          1431:   return CURLE_OK;
        !          1432: }
        !          1433: 
        !          1434: /* Set mime part content from named local file. */
        !          1435: CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename)
        !          1436: {
        !          1437:   CURLcode result = CURLE_OK;
        !          1438: 
        !          1439:   if(!part)
        !          1440:     return CURLE_BAD_FUNCTION_ARGUMENT;
        !          1441: 
        !          1442:   cleanup_part_content(part);
        !          1443: 
        !          1444:   if(filename) {
        !          1445:     char *base;
        !          1446:     struct_stat sbuf;
        !          1447: 
        !          1448:     if(stat(filename, &sbuf) || access(filename, R_OK))
        !          1449:       result = CURLE_READ_ERROR;
        !          1450: 
        !          1451:     part->data = strdup(filename);
        !          1452:     if(!part->data)
        !          1453:       result = CURLE_OUT_OF_MEMORY;
        !          1454: 
        !          1455:     part->datasize = -1;
        !          1456:     if(!result && S_ISREG(sbuf.st_mode)) {
        !          1457:       part->datasize = filesize(filename, sbuf);
        !          1458:       part->seekfunc = mime_file_seek;
        !          1459:     }
        !          1460: 
        !          1461:     part->readfunc = mime_file_read;
        !          1462:     part->freefunc = mime_file_free;
        !          1463:     part->kind = MIMEKIND_FILE;
        !          1464: 
        !          1465:     /* As a side effect, set the filename to the current file's base name.
        !          1466:        It is possible to withdraw this by explicitly calling
        !          1467:        curl_mime_filename() with a NULL filename argument after the current
        !          1468:        call. */
        !          1469:     base = strippath(filename);
        !          1470:     if(!base)
        !          1471:       result = CURLE_OUT_OF_MEMORY;
        !          1472:     else {
        !          1473:       CURLcode res = curl_mime_filename(part, base);
        !          1474: 
        !          1475:       if(res)
        !          1476:         result = res;
        !          1477:       free(base);
        !          1478:     }
        !          1479:   }
        !          1480:   return result;
        !          1481: }
        !          1482: 
        !          1483: /* Set mime part type. */
        !          1484: CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype)
        !          1485: {
        !          1486:   if(!part)
        !          1487:     return CURLE_BAD_FUNCTION_ARGUMENT;
        !          1488: 
        !          1489:   Curl_safefree(part->mimetype);
        !          1490:   part->mimetype = NULL;
        !          1491: 
        !          1492:   if(mimetype) {
        !          1493:     part->mimetype = strdup(mimetype);
        !          1494:     if(!part->mimetype)
        !          1495:       return CURLE_OUT_OF_MEMORY;
        !          1496:   }
        !          1497: 
        !          1498:   return CURLE_OK;
        !          1499: }
        !          1500: 
        !          1501: /* Set mime data transfer encoder. */
        !          1502: CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding)
        !          1503: {
        !          1504:   CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
        !          1505:   const mime_encoder *mep;
        !          1506: 
        !          1507:   if(!part)
        !          1508:     return result;
        !          1509: 
        !          1510:   part->encoder = NULL;
        !          1511: 
        !          1512:   if(!encoding)
        !          1513:     return CURLE_OK;    /* Removing current encoder. */
        !          1514: 
        !          1515:   for(mep = encoders; mep->name; mep++)
        !          1516:     if(strcasecompare(encoding, mep->name)) {
        !          1517:       part->encoder = mep;
        !          1518:       result = CURLE_OK;
        !          1519:     }
        !          1520: 
        !          1521:   return result;
        !          1522: }
        !          1523: 
        !          1524: /* Set mime part headers. */
        !          1525: CURLcode curl_mime_headers(curl_mimepart *part,
        !          1526:                            struct curl_slist *headers, int take_ownership)
        !          1527: {
        !          1528:   if(!part)
        !          1529:     return CURLE_BAD_FUNCTION_ARGUMENT;
        !          1530: 
        !          1531:   if(part->flags & MIME_USERHEADERS_OWNER) {
        !          1532:     if(part->userheaders != headers)  /* Allow setting twice the same list. */
        !          1533:       curl_slist_free_all(part->userheaders);
        !          1534:     part->flags &= ~MIME_USERHEADERS_OWNER;
        !          1535:   }
        !          1536:   part->userheaders = headers;
        !          1537:   if(headers && take_ownership)
        !          1538:     part->flags |= MIME_USERHEADERS_OWNER;
        !          1539:   return CURLE_OK;
        !          1540: }
        !          1541: 
        !          1542: /* Set mime part content from callback. */
        !          1543: CURLcode curl_mime_data_cb(curl_mimepart *part, curl_off_t datasize,
        !          1544:                            curl_read_callback readfunc,
        !          1545:                            curl_seek_callback seekfunc,
        !          1546:                            curl_free_callback freefunc, void *arg)
        !          1547: {
        !          1548:   if(!part)
        !          1549:     return CURLE_BAD_FUNCTION_ARGUMENT;
        !          1550: 
        !          1551:   cleanup_part_content(part);
        !          1552: 
        !          1553:   if(readfunc) {
        !          1554:     part->readfunc = readfunc;
        !          1555:     part->seekfunc = seekfunc;
        !          1556:     part->freefunc = freefunc;
        !          1557:     part->arg = arg;
        !          1558:     part->datasize = datasize;
        !          1559:     part->kind = MIMEKIND_CALLBACK;
        !          1560:   }
        !          1561: 
        !          1562:   return CURLE_OK;
        !          1563: }
        !          1564: 
        !          1565: /* Set mime part content from subparts. */
        !          1566: CURLcode Curl_mime_set_subparts(curl_mimepart *part,
        !          1567:                                 curl_mime *subparts, int take_ownership)
        !          1568: {
        !          1569:   curl_mime *root;
        !          1570: 
        !          1571:   if(!part)
        !          1572:     return CURLE_BAD_FUNCTION_ARGUMENT;
        !          1573: 
        !          1574:   /* Accept setting twice the same subparts. */
        !          1575:   if(part->kind == MIMEKIND_MULTIPART && part->arg == subparts)
        !          1576:     return CURLE_OK;
        !          1577: 
        !          1578:   cleanup_part_content(part);
        !          1579: 
        !          1580:   if(subparts) {
        !          1581:     /* Must belong to the same data handle. */
        !          1582:     if(part->easy && subparts->easy && part->easy != subparts->easy)
        !          1583:       return CURLE_BAD_FUNCTION_ARGUMENT;
        !          1584: 
        !          1585:     /* Should not have been attached already. */
        !          1586:     if(subparts->parent)
        !          1587:       return CURLE_BAD_FUNCTION_ARGUMENT;
        !          1588: 
        !          1589:     /* Should not be the part's root. */
        !          1590:     root = part->parent;
        !          1591:     if(root) {
        !          1592:       while(root->parent && root->parent->parent)
        !          1593:         root = root->parent->parent;
        !          1594:       if(subparts == root) {
        !          1595:         if(part->easy)
        !          1596:           failf(part->easy, "Can't add itself as a subpart!");
        !          1597:         return CURLE_BAD_FUNCTION_ARGUMENT;
        !          1598:       }
        !          1599:     }
        !          1600: 
        !          1601:     subparts->parent = part;
        !          1602:     /* Subparts are processed internally: no read callback. */
        !          1603:     part->seekfunc = mime_subparts_seek;
        !          1604:     part->freefunc = take_ownership? mime_subparts_free: mime_subparts_unbind;
        !          1605:     part->arg = subparts;
        !          1606:     part->datasize = -1;
        !          1607:     part->kind = MIMEKIND_MULTIPART;
        !          1608:   }
        !          1609: 
        !          1610:   return CURLE_OK;
        !          1611: }
        !          1612: 
        !          1613: CURLcode curl_mime_subparts(curl_mimepart *part, curl_mime *subparts)
        !          1614: {
        !          1615:   return Curl_mime_set_subparts(part, subparts, TRUE);
        !          1616: }
        !          1617: 
        !          1618: 
        !          1619: /* Readback from top mime. */
        !          1620: /* Argument is the dummy top part. */
        !          1621: size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream)
        !          1622: {
        !          1623:   curl_mimepart *part = (curl_mimepart *) instream;
        !          1624:   size_t ret;
        !          1625:   bool hasread;
        !          1626: 
        !          1627:   (void) size;   /* Always 1. */
        !          1628: 
        !          1629:   do {
        !          1630:     hasread = FALSE;
        !          1631:     ret = readback_part(part, buffer, nitems, &hasread);
        !          1632:     /*
        !          1633:      * If this is not possible to get some data without calling more than
        !          1634:      * one read callback (probably because a content encoder is not able to
        !          1635:      * deliver a new bunch for the few data accumulated so far), force another
        !          1636:      * read until we get enough data or a special exit code.
        !          1637:      */
        !          1638:   } while(ret == STOP_FILLING);
        !          1639: 
        !          1640:   return ret;
        !          1641: }
        !          1642: 
        !          1643: /* Rewind mime stream. */
        !          1644: CURLcode Curl_mime_rewind(curl_mimepart *part)
        !          1645: {
        !          1646:   return mime_part_rewind(part) == CURL_SEEKFUNC_OK?
        !          1647:          CURLE_OK: CURLE_SEND_FAIL_REWIND;
        !          1648: }
        !          1649: 
        !          1650: /* Compute header list size. */
        !          1651: static size_t slist_size(struct curl_slist *s,
        !          1652:                          size_t overhead, const char *skip)
        !          1653: {
        !          1654:   size_t size = 0;
        !          1655:   size_t skiplen = skip? strlen(skip): 0;
        !          1656: 
        !          1657:   for(; s; s = s->next)
        !          1658:     if(!skip || !match_header(s, skip, skiplen))
        !          1659:       size += strlen(s->data) + overhead;
        !          1660:   return size;
        !          1661: }
        !          1662: 
        !          1663: /* Get/compute multipart size. */
        !          1664: static curl_off_t multipart_size(curl_mime *mime)
        !          1665: {
        !          1666:   curl_off_t size;
        !          1667:   size_t boundarysize;
        !          1668:   curl_mimepart *part;
        !          1669: 
        !          1670:   if(!mime)
        !          1671:     return 0;           /* Not present -> empty. */
        !          1672: 
        !          1673:   boundarysize = 4 + strlen(mime->boundary) + 2;
        !          1674:   size = boundarysize;  /* Final boundary - CRLF after headers. */
        !          1675: 
        !          1676:   for(part = mime->firstpart; part; part = part->nextpart) {
        !          1677:     curl_off_t sz = Curl_mime_size(part);
        !          1678: 
        !          1679:     if(sz < 0)
        !          1680:       size = sz;
        !          1681: 
        !          1682:     if(size >= 0)
        !          1683:       size += boundarysize + sz;
        !          1684:   }
        !          1685: 
        !          1686:   return size;
        !          1687: }
        !          1688: 
        !          1689: /* Get/compute mime size. */
        !          1690: curl_off_t Curl_mime_size(curl_mimepart *part)
        !          1691: {
        !          1692:   curl_off_t size;
        !          1693: 
        !          1694:   if(part->kind == MIMEKIND_MULTIPART)
        !          1695:     part->datasize = multipart_size(part->arg);
        !          1696: 
        !          1697:   size = part->datasize;
        !          1698: 
        !          1699:   if(part->encoder)
        !          1700:     size = part->encoder->sizefunc(part);
        !          1701: 
        !          1702:   if(size >= 0 && !(part->flags & MIME_BODY_ONLY)) {
        !          1703:     /* Compute total part size. */
        !          1704:     size += slist_size(part->curlheaders, 2, NULL);
        !          1705:     size += slist_size(part->userheaders, 2, "Content-Type");
        !          1706:     size += 2;    /* CRLF after headers. */
        !          1707:   }
        !          1708:   return size;
        !          1709: }
        !          1710: 
        !          1711: /* Add a header. */
        !          1712: /* VARARGS2 */
        !          1713: CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...)
        !          1714: {
        !          1715:   struct curl_slist *hdr = NULL;
        !          1716:   char *s = NULL;
        !          1717:   va_list ap;
        !          1718: 
        !          1719:   va_start(ap, fmt);
        !          1720:   s = curl_mvaprintf(fmt, ap);
        !          1721:   va_end(ap);
        !          1722: 
        !          1723:   if(s) {
        !          1724:     hdr = Curl_slist_append_nodup(*slp, s);
        !          1725:     if(hdr)
        !          1726:       *slp = hdr;
        !          1727:     else
        !          1728:       free(s);
        !          1729:   }
        !          1730: 
        !          1731:   return hdr? CURLE_OK: CURLE_OUT_OF_MEMORY;
        !          1732: }
        !          1733: 
        !          1734: /* Add a content type header. */
        !          1735: static CURLcode add_content_type(struct curl_slist **slp,
        !          1736:                                  const char *type, const char *boundary)
        !          1737: {
        !          1738:   return Curl_mime_add_header(slp, "Content-Type: %s%s%s", type,
        !          1739:                               boundary? "; boundary=": "",
        !          1740:                               boundary? boundary: "");
        !          1741: }
        !          1742: 
        !          1743: const char *Curl_mime_contenttype(const char *filename)
        !          1744: {
        !          1745:   /*
        !          1746:    * If no content type was specified, we scan through a few well-known
        !          1747:    * extensions and pick the first we match!
        !          1748:    */
        !          1749:   struct ContentType {
        !          1750:     const char *extension;
        !          1751:     const char *type;
        !          1752:   };
        !          1753:   static const struct ContentType ctts[] = {
        !          1754:     {".gif",  "image/gif"},
        !          1755:     {".jpg",  "image/jpeg"},
        !          1756:     {".jpeg", "image/jpeg"},
        !          1757:     {".png",  "image/png"},
        !          1758:     {".svg",  "image/svg+xml"},
        !          1759:     {".txt",  "text/plain"},
        !          1760:     {".htm",  "text/html"},
        !          1761:     {".html", "text/html"},
        !          1762:     {".pdf",  "application/pdf"},
        !          1763:     {".xml",  "application/xml"}
        !          1764:   };
        !          1765: 
        !          1766:   if(filename) {
        !          1767:     size_t len1 = strlen(filename);
        !          1768:     const char *nameend = filename + len1;
        !          1769:     unsigned int i;
        !          1770: 
        !          1771:     for(i = 0; i < sizeof(ctts) / sizeof(ctts[0]); i++) {
        !          1772:       size_t len2 = strlen(ctts[i].extension);
        !          1773: 
        !          1774:       if(len1 >= len2 && strcasecompare(nameend - len2, ctts[i].extension))
        !          1775:           return ctts[i].type;
        !          1776:     }
        !          1777:   }
        !          1778:   return NULL;
        !          1779: }
        !          1780: 
        !          1781: static bool content_type_match(const char *contenttype, const char *target)
        !          1782: {
        !          1783:   size_t len = strlen(target);
        !          1784: 
        !          1785:   if(contenttype && strncasecompare(contenttype, target, len))
        !          1786:     switch(contenttype[len]) {
        !          1787:     case '\0':
        !          1788:     case '\t':
        !          1789:     case '\r':
        !          1790:     case '\n':
        !          1791:     case ' ':
        !          1792:     case ';':
        !          1793:       return TRUE;
        !          1794:     }
        !          1795:   return FALSE;
        !          1796: }
        !          1797: 
        !          1798: CURLcode Curl_mime_prepare_headers(curl_mimepart *part,
        !          1799:                                    const char *contenttype,
        !          1800:                                    const char *disposition,
        !          1801:                                    enum mimestrategy strategy)
        !          1802: {
        !          1803:   curl_mime *mime = NULL;
        !          1804:   const char *boundary = NULL;
        !          1805:   char *customct;
        !          1806:   const char *cte = NULL;
        !          1807:   CURLcode ret = CURLE_OK;
        !          1808: 
        !          1809:   /* Get rid of previously prepared headers. */
        !          1810:   curl_slist_free_all(part->curlheaders);
        !          1811:   part->curlheaders = NULL;
        !          1812: 
        !          1813:   /* Be sure we won't access old headers later. */
        !          1814:   if(part->state.state == MIMESTATE_CURLHEADERS)
        !          1815:     mimesetstate(&part->state, MIMESTATE_CURLHEADERS, NULL);
        !          1816: 
        !          1817:   /* Check if content type is specified. */
        !          1818:   customct = part->mimetype;
        !          1819:   if(!customct)
        !          1820:     customct = search_header(part->userheaders, "Content-Type");
        !          1821:   if(customct)
        !          1822:     contenttype = customct;
        !          1823: 
        !          1824:   /* If content type is not specified, try to determine it. */
        !          1825:   if(!contenttype) {
        !          1826:     switch(part->kind) {
        !          1827:     case MIMEKIND_MULTIPART:
        !          1828:       contenttype = MULTIPART_CONTENTTYPE_DEFAULT;
        !          1829:       break;
        !          1830:     case MIMEKIND_FILE:
        !          1831:       contenttype = Curl_mime_contenttype(part->filename);
        !          1832:       if(!contenttype)
        !          1833:         contenttype = Curl_mime_contenttype(part->data);
        !          1834:       if(!contenttype && part->filename)
        !          1835:         contenttype = FILE_CONTENTTYPE_DEFAULT;
        !          1836:       break;
        !          1837:     default:
        !          1838:       contenttype = Curl_mime_contenttype(part->filename);
        !          1839:       break;
        !          1840:     }
        !          1841:   }
        !          1842: 
        !          1843:   if(part->kind == MIMEKIND_MULTIPART) {
        !          1844:     mime = (curl_mime *) part->arg;
        !          1845:     if(mime)
        !          1846:       boundary = mime->boundary;
        !          1847:   }
        !          1848:   else if(contenttype && !customct &&
        !          1849:           content_type_match(contenttype, "text/plain"))
        !          1850:     if(strategy == MIMESTRATEGY_MAIL || !part->filename)
        !          1851:       contenttype = NULL;
        !          1852: 
        !          1853:   /* Issue content-disposition header only if not already set by caller. */
        !          1854:   if(!search_header(part->userheaders, "Content-Disposition")) {
        !          1855:     if(!disposition)
        !          1856:       if(part->filename || part->name ||
        !          1857:         (contenttype && !strncasecompare(contenttype, "multipart/", 10)))
        !          1858:           disposition = DISPOSITION_DEFAULT;
        !          1859:     if(disposition && curl_strequal(disposition, "attachment") &&
        !          1860:      !part->name && !part->filename)
        !          1861:       disposition = NULL;
        !          1862:     if(disposition) {
        !          1863:       char *name = NULL;
        !          1864:       char *filename = NULL;
        !          1865: 
        !          1866:       if(part->name) {
        !          1867:         name = escape_string(part->name);
        !          1868:         if(!name)
        !          1869:           ret = CURLE_OUT_OF_MEMORY;
        !          1870:       }
        !          1871:       if(!ret && part->filename) {
        !          1872:         filename = escape_string(part->filename);
        !          1873:         if(!filename)
        !          1874:           ret = CURLE_OUT_OF_MEMORY;
        !          1875:       }
        !          1876:       if(!ret)
        !          1877:         ret = Curl_mime_add_header(&part->curlheaders,
        !          1878:                                    "Content-Disposition: %s%s%s%s%s%s%s",
        !          1879:                                    disposition,
        !          1880:                                    name? "; name=\"": "",
        !          1881:                                    name? name: "",
        !          1882:                                    name? "\"": "",
        !          1883:                                    filename? "; filename=\"": "",
        !          1884:                                    filename? filename: "",
        !          1885:                                    filename? "\"": "");
        !          1886:       Curl_safefree(name);
        !          1887:       Curl_safefree(filename);
        !          1888:       if(ret)
        !          1889:         return ret;
        !          1890:       }
        !          1891:     }
        !          1892: 
        !          1893:   /* Issue Content-Type header. */
        !          1894:   if(contenttype) {
        !          1895:     ret = add_content_type(&part->curlheaders, contenttype, boundary);
        !          1896:     if(ret)
        !          1897:       return ret;
        !          1898:   }
        !          1899: 
        !          1900:   /* Content-Transfer-Encoding header. */
        !          1901:   if(!search_header(part->userheaders, "Content-Transfer-Encoding")) {
        !          1902:     if(part->encoder)
        !          1903:       cte = part->encoder->name;
        !          1904:     else if(contenttype && strategy == MIMESTRATEGY_MAIL &&
        !          1905:      part->kind != MIMEKIND_MULTIPART)
        !          1906:       cte = "8bit";
        !          1907:     if(cte) {
        !          1908:       ret = Curl_mime_add_header(&part->curlheaders,
        !          1909:                                  "Content-Transfer-Encoding: %s", cte);
        !          1910:       if(ret)
        !          1911:         return ret;
        !          1912:     }
        !          1913:   }
        !          1914: 
        !          1915:   /* If we were reading curl-generated headers, restart with new ones (this
        !          1916:      should not occur). */
        !          1917:   if(part->state.state == MIMESTATE_CURLHEADERS)
        !          1918:     mimesetstate(&part->state, MIMESTATE_CURLHEADERS, part->curlheaders);
        !          1919: 
        !          1920:   /* Process subparts. */
        !          1921:   if(part->kind == MIMEKIND_MULTIPART && mime) {
        !          1922:     curl_mimepart *subpart;
        !          1923: 
        !          1924:     disposition = NULL;
        !          1925:     if(content_type_match(contenttype, "multipart/form-data"))
        !          1926:       disposition = "form-data";
        !          1927:     for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart) {
        !          1928:       ret = Curl_mime_prepare_headers(subpart, NULL, disposition, strategy);
        !          1929:       if(ret)
        !          1930:         return ret;
        !          1931:     }
        !          1932:   }
        !          1933:   return ret;
        !          1934: }
        !          1935: 
        !          1936: /* Recursively reset paused status in the given part. */
        !          1937: void Curl_mime_unpause(curl_mimepart *part)
        !          1938: {
        !          1939:   if(part) {
        !          1940:     if(part->lastreadstatus == CURL_READFUNC_PAUSE)
        !          1941:       part->lastreadstatus = 1; /* Successful read status. */
        !          1942:     if(part->kind == MIMEKIND_MULTIPART) {
        !          1943:       curl_mime *mime = (curl_mime *) part->arg;
        !          1944: 
        !          1945:       if(mime) {
        !          1946:         curl_mimepart *subpart;
        !          1947: 
        !          1948:         for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart)
        !          1949:           Curl_mime_unpause(subpart);
        !          1950:       }
        !          1951:     }
        !          1952:   }
        !          1953: }
        !          1954: 
        !          1955: 
        !          1956: #else /* !CURL_DISABLE_HTTP || !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP */
        !          1957: 
        !          1958: /* Mime not compiled in: define stubs for externally-referenced functions. */
        !          1959: curl_mime *curl_mime_init(CURL *easy)
        !          1960: {
        !          1961:   (void) easy;
        !          1962:   return NULL;
        !          1963: }
        !          1964: 
        !          1965: void curl_mime_free(curl_mime *mime)
        !          1966: {
        !          1967:   (void) mime;
        !          1968: }
        !          1969: 
        !          1970: curl_mimepart *curl_mime_addpart(curl_mime *mime)
        !          1971: {
        !          1972:   (void) mime;
        !          1973:   return NULL;
        !          1974: }
        !          1975: 
        !          1976: CURLcode curl_mime_name(curl_mimepart *part, const char *name)
        !          1977: {
        !          1978:   (void) part;
        !          1979:   (void) name;
        !          1980:   return CURLE_NOT_BUILT_IN;
        !          1981: }
        !          1982: 
        !          1983: CURLcode curl_mime_filename(curl_mimepart *part, const char *filename)
        !          1984: {
        !          1985:   (void) part;
        !          1986:   (void) filename;
        !          1987:   return CURLE_NOT_BUILT_IN;
        !          1988: }
        !          1989: 
        !          1990: CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype)
        !          1991: {
        !          1992:   (void) part;
        !          1993:   (void) mimetype;
        !          1994:   return CURLE_NOT_BUILT_IN;
        !          1995: }
        !          1996: 
        !          1997: CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding)
        !          1998: {
        !          1999:   (void) part;
        !          2000:   (void) encoding;
        !          2001:   return CURLE_NOT_BUILT_IN;
        !          2002: }
        !          2003: 
        !          2004: CURLcode curl_mime_data(curl_mimepart *part,
        !          2005:                         const char *data, size_t datasize)
        !          2006: {
        !          2007:   (void) part;
        !          2008:   (void) data;
        !          2009:   (void) datasize;
        !          2010:   return CURLE_NOT_BUILT_IN;
        !          2011: }
        !          2012: 
        !          2013: CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename)
        !          2014: {
        !          2015:   (void) part;
        !          2016:   (void) filename;
        !          2017:   return CURLE_NOT_BUILT_IN;
        !          2018: }
        !          2019: 
        !          2020: CURLcode curl_mime_data_cb(curl_mimepart *part,
        !          2021:                            curl_off_t datasize,
        !          2022:                            curl_read_callback readfunc,
        !          2023:                            curl_seek_callback seekfunc,
        !          2024:                            curl_free_callback freefunc,
        !          2025:                            void *arg)
        !          2026: {
        !          2027:   (void) part;
        !          2028:   (void) datasize;
        !          2029:   (void) readfunc;
        !          2030:   (void) seekfunc;
        !          2031:   (void) freefunc;
        !          2032:   (void) arg;
        !          2033:   return CURLE_NOT_BUILT_IN;
        !          2034: }
        !          2035: 
        !          2036: CURLcode curl_mime_subparts(curl_mimepart *part, curl_mime *subparts)
        !          2037: {
        !          2038:   (void) part;
        !          2039:   (void) subparts;
        !          2040:   return CURLE_NOT_BUILT_IN;
        !          2041: }
        !          2042: 
        !          2043: CURLcode curl_mime_headers(curl_mimepart *part,
        !          2044:                            struct curl_slist *headers, int take_ownership)
        !          2045: {
        !          2046:   (void) part;
        !          2047:   (void) headers;
        !          2048:   (void) take_ownership;
        !          2049:   return CURLE_NOT_BUILT_IN;
        !          2050: }
        !          2051: 
        !          2052: CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...)
        !          2053: {
        !          2054:   (void)slp;
        !          2055:   (void)fmt;
        !          2056:   return CURLE_NOT_BUILT_IN;
        !          2057: }
        !          2058: 
        !          2059: #endif /* if disabled */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>