static GF_Err WriteFlat(MovieWriter *mw, u8 moovFirst, GF_BitStream *bs, Bool non_seakable, Bool for_fragments, GF_BitStream *moov_bs)
{
	GF_Err e;
	u32 i;
	u64 offset, finalOffset, totSize, begin, firstSize, finalSize;
	GF_Box *a, *cprt_box=NULL;
	GF_List *writers = gf_list_new();
	GF_ISOFile *movie = mw->movie;
	s32 moov_meta_pos=-1;

	//in case we did a read on the file while producing it, seek to end of edit
	totSize = gf_bs_get_size(bs);
	if (gf_bs_get_position(bs) != totSize) {
		gf_bs_seek(bs, totSize);
	}
	begin = totSize = 0;

	//first setup the writers
	e = SetupWriters(mw, writers, 0);
	if (e) goto exit;

	if (!moovFirst) {
		if ((movie->openMode == GF_ISOM_OPEN_WRITE) && !non_seakable) {
			begin = 0;
			totSize = gf_isom_datamap_get_offset(movie->editFileMap);
			/*start boxes have not been written yet, do it*/
			if (!totSize) {
				if (movie->is_jp2) {
					gf_bs_write_u32(movie->editFileMap->bs, 12);
					gf_bs_write_u32(movie->editFileMap->bs, GF_ISOM_BOX_TYPE_JP);
					gf_bs_write_u32(movie->editFileMap->bs, 0x0D0A870A);
					totSize += 12;
					begin += 12;
				}
				if (movie->brand) {
					e = gf_isom_box_size((GF_Box *)movie->brand);
					if (e) goto exit;
					e = gf_isom_box_write((GF_Box *)movie->brand, movie->editFileMap->bs);
					if (e) goto exit;
					totSize += movie->brand->size;
					begin += movie->brand->size;
				}
				if (movie->pdin) {
					e = gf_isom_box_size((GF_Box *)movie->pdin);
					if (e) goto exit;
					e = gf_isom_box_write((GF_Box *)movie->pdin, movie->editFileMap->bs);
					if (e) goto exit;
					totSize += movie->pdin->size;
					begin += movie->pdin->size;
				}
			} else {
				if (movie->is_jp2) begin += 12;
				if (movie->brand) begin += movie->brand->size;
				if (movie->pdin) begin += movie->pdin->size;
			}
			totSize -= begin;
		} else if (!non_seakable || for_fragments) {
			if (movie->is_jp2) {
				gf_bs_write_u32(bs, 12);
				gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_JP);
				gf_bs_write_u32(bs, 0x0D0A870A);
			}
			if (movie->brand) {
				e = gf_isom_box_size((GF_Box *)movie->brand);
				if (e) goto exit;
				e = gf_isom_box_write((GF_Box *)movie->brand, bs);
				if (e) goto exit;
			}
			/*then progressive download*/
			if (movie->pdin) {
				e = gf_isom_box_size((GF_Box *)movie->pdin);
				if (e) goto exit;
				e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
				if (e) goto exit;
			}
		}

		//if the moov is at the end, write directly
		i=0;
		while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
			switch (a->type) {
			/*written by hand*/
			case GF_ISOM_BOX_TYPE_MOOV:
			case GF_ISOM_BOX_TYPE_META:
				moov_meta_pos = i-1;
			case GF_ISOM_BOX_TYPE_FTYP:
			case GF_ISOM_BOX_TYPE_PDIN:
#ifndef GPAC_DISABLE_ISOM_ADOBE
			case GF_ISOM_BOX_TYPE_AFRA:
			case GF_ISOM_BOX_TYPE_ABST:
#endif
				break;
			case GF_ISOM_BOX_TYPE_MDAT:
				//in case we're capturing
				if (movie->openMode == GF_ISOM_OPEN_WRITE) {
					//emulate a write to recreate our tables (media data already written)
					e = DoWrite(mw, writers, bs, 1, begin);
					if (e) goto exit;
					continue;
				}
				if (non_seakable) {
					begin = gf_bs_get_position(bs);
					//do a sim pass to get the true mdat size
					e = DoWrite(mw, writers, bs, 1, begin);
					if (e) goto exit;

					if (movie->mdat->dataSize > 0xFFFFFFFF) {
						gf_bs_write_u32(bs, 1);
					} else {
						gf_bs_write_u32(bs, (u32) movie->mdat->dataSize + 8);
					}
					gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
					if (movie->mdat->dataSize > 0xFFFFFFFF) gf_bs_write_u64(bs, movie->mdat->dataSize + 8 + 8);
					//reset writers and write samples
					ResetWriters(writers);
					e = DoWrite(mw, writers, bs, 0, gf_bs_get_position(bs));
					if (e) goto exit;
					movie->mdat->size = movie->mdat->dataSize;
					totSize = 0;
				} else {
					//to avoid computing the size each time write always 4 + 4 + 8 bytes before
					begin = gf_bs_get_position(bs);
					gf_bs_write_u64(bs, 0);
					gf_bs_write_u64(bs, 0);
					e = DoWrite(mw, writers, bs, 0, gf_bs_get_position(bs));
					if (e) goto exit;
					totSize = gf_bs_get_position(bs) - begin;
				}
				break;

			case GF_ISOM_BOX_TYPE_FREE:
				//for backward compat with old arch, keep copyright before moov
				if (((GF_FreeSpaceBox*)a)->dataSize>4) {
					GF_FreeSpaceBox *fr = (GF_FreeSpaceBox*) a;
					if ((fr->dataSize>20) && !strncmp(fr->data, "IsoMedia File", 13)) {
						e = gf_isom_box_size(a);
						if (e) goto exit;
						e = gf_isom_box_write(a, bs);
						if (e) goto exit;
						cprt_box = a;
						break;
					}
				}
			default:
				if (moov_meta_pos < 0) {
					e = gf_isom_box_size(a);
					if (e) goto exit;
					e = gf_isom_box_write(a, bs);
					if (e) goto exit;
				}
				break;
			}
		}

		if (moov_bs) {
			e = DoWrite(mw, writers, bs, 1, movie->mdat->bsOffset);
			if (e) goto exit;

			firstSize = GetMoovAndMetaSize(movie, writers);

			offset = firstSize;
			e = ShiftOffset(movie, writers, offset);
			if (e) goto exit;
			//get the size and see if it has changed (eg, we moved to 64 bit offsets)
			finalSize = GetMoovAndMetaSize(movie, writers);
			if (firstSize != finalSize) {
				finalOffset = finalSize;
				//OK, now we're sure about the final size.
				//we don't need to re-emulate, as the only thing that changed is the offset
				//so just shift the offset
				e = ShiftOffset(movie, writers, finalOffset - offset);
				if (e) goto exit;
			}
		}
		//get real sample offsets for meta items
		if (movie->meta) {
			store_meta_item_sample_ref_offsets(movie, writers, movie->meta);
		}
		//OK, write the movie box.
		e = WriteMoovAndMeta(movie, writers, moov_bs ? moov_bs : bs);
		if (e) goto exit;

#ifndef GPAC_DISABLE_ISOM_ADOBE
		i=0;
		while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
			switch (a->type) {
			case GF_ISOM_BOX_TYPE_AFRA:
			case GF_ISOM_BOX_TYPE_ABST:
				e = gf_isom_box_size(a);
				if (e) goto exit;
				e = gf_isom_box_write(a, bs);
				if (e) goto exit;
				break;
			}
		}
#endif

		/*if data has been written, update mdat size*/
		if (totSize) {
			offset = gf_bs_get_position(bs);
			e = gf_bs_seek(bs, begin);
			if (e) goto exit;
			if (totSize > 0xFFFFFFFF) {
				gf_bs_write_u32(bs, 1);
			} else {
				gf_bs_write_u32(bs, (u32) totSize);
			}
			gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
			if (totSize > 0xFFFFFFFF) gf_bs_write_u64(bs, totSize);
			e = gf_bs_seek(bs, offset);
			movie->mdat->size = totSize;
		}

		//then the rest
		i = (u32) (moov_meta_pos + 1);
		while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
			if (a==cprt_box) continue;

			switch (a->type) {
			case GF_ISOM_BOX_TYPE_MOOV:
			case GF_ISOM_BOX_TYPE_META:
			case GF_ISOM_BOX_TYPE_FTYP:
			case GF_ISOM_BOX_TYPE_PDIN:
			case GF_ISOM_BOX_TYPE_MDAT:
				break;
			default:
				e = gf_isom_box_size(a);
				if (e) goto exit;
				e = gf_isom_box_write(a, bs);
				if (e) goto exit;
			}
		}
		goto exit;
	}

	//nope, we have to write the moov first. The pb is that
	//1 - we don't know its size till the mdat is written
	//2 - we don't know the ofset at which the mdat will start...
	//3 - once the mdat is written, the chunkOffset table can have changed...

	if (movie->is_jp2) {
		gf_bs_write_u32(bs, 12);
		gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_JP);
		gf_bs_write_u32(bs, 0x0D0A870A);
	}
	if (movie->brand) {
		e = gf_isom_box_size((GF_Box *)movie->brand);
		if (e) goto exit;
		e = gf_isom_box_write((GF_Box *)movie->brand, bs);
		if (e) goto exit;
	}
	/*then progressive dnload*/
	if (movie->pdin) {
		e = gf_isom_box_size((GF_Box *)movie->pdin);
		if (e) goto exit;
		e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
		if (e) goto exit;
	}

	//write all boxes before moov
	i=0;
	while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
		switch (a->type) {
		case GF_ISOM_BOX_TYPE_MOOV:
		case GF_ISOM_BOX_TYPE_META:
			moov_meta_pos = i-1;
			break;
		case GF_ISOM_BOX_TYPE_FTYP:
		case GF_ISOM_BOX_TYPE_PDIN:
		case GF_ISOM_BOX_TYPE_MDAT:
			break;
		//for backward compat with old arch keep out copyright after moov
		case GF_ISOM_BOX_TYPE_FREE:
			if (((GF_FreeSpaceBox*)a)->dataSize>4) {
				GF_FreeSpaceBox *fr = (GF_FreeSpaceBox*) a;
				if ((fr->dataSize>20) && !strncmp(fr->data, "IsoMedia File", 13)) {
					cprt_box = a;
					break;
				}
			}
		default:
			if (moov_meta_pos<0) {
				e = gf_isom_box_size(a);
				if (e) goto exit;
				e = gf_isom_box_write(a, bs);
				if (e) goto exit;
			}
			break;
		}
	}

	//What we will do is first emulate the write from the beginning...
	//note: this will set the size of the mdat
	e = DoWrite(mw, writers, bs, 1, gf_bs_get_position(bs));
	if (e) goto exit;

	firstSize = GetMoovAndMetaSize(movie, writers);
	//offset = (firstSize > 0xFFFFFFFF ? firstSize + 8 : firstSize) + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
	offset = firstSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
	e = ShiftOffset(movie, writers, offset);
	if (e) goto exit;
	//get the size and see if it has changed (eg, we moved to 64 bit offsets)
	finalSize = GetMoovAndMetaSize(movie, writers);
	if (firstSize != finalSize) {
		finalOffset = finalSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
		//OK, now we're sure about the final size.
		//we don't need to re-emulate, as the only thing that changed is the offset
		//so just shift the offset
		e = ShiftOffset(movie, writers, finalOffset - offset);
		if (e) goto exit;
	}
	//now write our stuff
	e = WriteMoovAndMeta(movie, writers, bs);
	if (e) goto exit;
	e = gf_isom_box_size((GF_Box *)movie->mdat);
	if (e) goto exit;
	e = gf_isom_box_write((GF_Box *)movie->mdat, bs);
	if (e) goto exit;

	//we don't need the offset as the moov is already written...
	ResetWriters(writers);
	e = DoWrite(mw, writers, bs, 0, 0);
	if (e) goto exit;

	//then the rest
	i=0;
	while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
		if ((i-1<= (u32) moov_meta_pos) && (a!=cprt_box)) continue;
		switch (a->type) {
		case GF_ISOM_BOX_TYPE_MOOV:
		case GF_ISOM_BOX_TYPE_META:
		case GF_ISOM_BOX_TYPE_FTYP:
		case GF_ISOM_BOX_TYPE_PDIN:
		case GF_ISOM_BOX_TYPE_MDAT:
			break;
		default:
			e = gf_isom_box_size(a);
			if (e) goto exit;
			e = gf_isom_box_write(a, bs);
			if (e) goto exit;
		}
	}

exit:
	CleanWriters(writers);
	gf_list_del(writers);
	return e;
}


GF_Err WriteSample(MovieWriter *mw, u32 size, u64 offset, u8 isEdited, GF_BitStream *bs, u32 nb_samp)
{
	GF_DataMap *map;
	u32 bytes;

	if (!size) return GF_OK;

	if (size>mw->alloc_size) {
		mw->buffer = (char*)gf_realloc(mw->buffer, size);
		mw->alloc_size = size;
	}

	if (!mw->buffer) return GF_OUT_OF_MEM;

	if (isEdited) {
		map = mw->movie->editFileMap;
	} else {
		map = mw->movie->movieFileMap;
	}
	//get the payload...
	bytes = gf_isom_datamap_get_data(map, mw->buffer, size, offset);
	if (bytes != size)
		return GF_IO_ERR;
	//write it to our stream...
	bytes = gf_bs_write_data(bs, mw->buffer, size);
	if (bytes != size)
		return GF_IO_ERR;

	mw->nb_done+=nb_samp;
	muxer_report_progress(mw);
	return GF_OK;
}

An issue was discovered in GPAC version 0.8.0 and 1.0.1. There is an invalid pointer dereference in the function SetupWriters() in isomedia/isom_store.c.


GF_Err gf_isom_write_compressed_box(GF_ISOFile *mov, GF_Box *root_box, u32 repl_type, GF_BitStream *bs, u32 *box_csize)
{
#ifdef GPAC_DISABLE_ZLIB
	return GF_NOT_SUPPORTED;
#else
	GF_Err e;
	GF_BitStream *comp_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
	e = gf_isom_box_write(root_box, comp_bs);

	if (!e) {
		u8 *box_data;
		u32 box_size, comp_size;

		if (box_csize)
			*box_csize = (u32) root_box->size;

		gf_bs_get_content(comp_bs, &box_data, &box_size);
		gf_gz_compress_payload_ex(&box_data, box_size, &comp_size, 8, GF_TRUE, NULL);
		if (mov->force_compress || (comp_size + COMP_BOX_COST_BYTES < box_size)) {
			if (bs) {
				gf_bs_write_u32(bs, comp_size+8);
				gf_bs_write_u32(bs, repl_type);
				gf_bs_write_data(bs, box_data, comp_size);
			}
			if (box_csize)
				*box_csize = comp_size + COMP_BOX_COST_BYTES;
		} else if (bs) {
			gf_bs_write_data(bs, box_data, box_size);
		}
		gf_free(box_data);
	}
	gf_bs_del(comp_bs);
	return e;
#endif /*GPAC_DISABLE_ZLIB*/
}


static GF_Err WriteInterleaved(MovieWriter *mw, GF_BitStream *bs, Bool drift_inter)
{
	GF_Err e;
	u32 i;
	s32 moov_meta_pos=-1;
	GF_Box *a, *cprt_box=NULL;
	u64 firstSize, finalSize, offset, finalOffset;
	GF_List *writers = gf_list_new();
	GF_ISOFile *movie = mw->movie;

	//first setup the writers
	e = SetupWriters(mw, writers, 1);
	if (e) goto exit;

	if (movie->is_jp2) {
		gf_bs_write_u32(bs, 12);
		gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_JP);
		gf_bs_write_u32(bs, 0x0D0A870A);
	}
	if (movie->brand) {
		e = gf_isom_box_size((GF_Box *)movie->brand);
		if (e) goto exit;
		e = gf_isom_box_write((GF_Box *)movie->brand, bs);
		if (e) goto exit;
	}
	if (movie->pdin) {
		e = gf_isom_box_size((GF_Box *)movie->pdin);
		if (e) goto exit;
		e = gf_isom_box_write((GF_Box *)movie->pdin, bs);
		if (e) goto exit;
	}

	//write all boxes before moov
	i=0;
	while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
		switch (a->type) {
		case GF_ISOM_BOX_TYPE_MOOV:
		case GF_ISOM_BOX_TYPE_META:
			moov_meta_pos = i-1;
			break;
		case GF_ISOM_BOX_TYPE_FTYP:
		case GF_ISOM_BOX_TYPE_PDIN:
		case GF_ISOM_BOX_TYPE_MDAT:
			break;
		case GF_ISOM_BOX_TYPE_FREE:
			//for backward compat with old arch, keep copyright before moov
			if (((GF_FreeSpaceBox*)a)->dataSize>4) {
				GF_FreeSpaceBox *fr = (GF_FreeSpaceBox*) a;
				if ((fr->dataSize>20) && !strncmp(fr->data, "IsoMedia File", 13)) {
					cprt_box = a;
					break;
				}
			}
		default:
			if (moov_meta_pos<0) {
				e = gf_isom_box_size(a);
				if (e) goto exit;
				e = gf_isom_box_write(a, bs);
				if (e) goto exit;
			}
			break;
		}
	}

	e = DoInterleave(mw, writers, bs, 1, gf_bs_get_position(bs), drift_inter);
	if (e) goto exit;

	firstSize = GetMoovAndMetaSize(movie, writers);
	offset = firstSize;
	if (movie->mdat && movie->mdat->dataSize) offset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
	e = ShiftOffset(movie, writers, offset);
	if (e) goto exit;
	//get the size and see if it has changed (eg, we moved to 64 bit offsets)
	finalSize = GetMoovAndMetaSize(movie, writers);
	if (firstSize != finalSize) {
		finalOffset = finalSize;
		if (movie->mdat && movie->mdat->dataSize) finalOffset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0);
		//OK, now we're sure about the final size -> shift the offsets
		//we don't need to re-emulate, as the only thing that changed is the offset
		//so just shift the offset
		e = ShiftOffset(movie, writers, finalOffset - offset);
		if (e) goto exit;
		/*firstSize = */GetMoovAndMetaSize(movie, writers);
	}
	//get real sample offsets for meta items
	if (movie->meta) {
		store_meta_item_sample_ref_offsets(movie, writers, movie->meta);
	}
	//now write our stuff
	e = WriteMoovAndMeta(movie, writers, bs);
	if (e) goto exit;

	/*we have 8 extra bytes for large size (not computed in gf_isom_box_size) */
	if (movie->mdat && movie->mdat->dataSize) {
		if (movie->mdat->dataSize > 0xFFFFFFFF) movie->mdat->dataSize += 8;
		e = gf_isom_box_size((GF_Box *)movie->mdat);
		if (e) goto exit;
		e = gf_isom_box_write((GF_Box *)movie->mdat, bs);
		if (e) goto exit;
	}

	//we don't need the offset as we are writing...
	ResetWriters(writers);
	e = DoInterleave(mw, writers, bs, 0, 0, drift_inter);
	if (e) goto exit;

	//then the rest
	i=0;
	while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) {
		if ((i-1 < (u32) moov_meta_pos) && (a != cprt_box))
			continue;
		switch (a->type) {
		case GF_ISOM_BOX_TYPE_MOOV:
		case GF_ISOM_BOX_TYPE_META:
		case GF_ISOM_BOX_TYPE_FTYP:
		case GF_ISOM_BOX_TYPE_PDIN:
		case GF_ISOM_BOX_TYPE_MDAT:
			break;
		default:
			e = gf_isom_box_size(a);
			if (e) goto exit;
			e = gf_isom_box_write(a, bs);
			if (e) goto exit;
		}
	}

exit:
	CleanWriters(writers);
	gf_list_del(writers);
	return e;
}


GF_Err DoWriteMeta(GF_ISOFile *file, GF_MetaBox *meta, GF_BitStream *bs, Bool Emulation, u64 baseOffset, u64 *mdatSize)
{
	GF_ItemExtentEntry *entry;
	u64 maxExtendOffset, maxExtendSize;
	u32 i, j, count;

	maxExtendOffset = 0;
	maxExtendSize = 0;
	if (mdatSize) *mdatSize = 0;
	if (!meta->item_locations) return GF_OK;

	count = gf_list_count(meta->item_locations->location_entries);
	for (i=0; i<count; i++) {
		u64 it_size;
		GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i);
		/*get item info*/
		GF_ItemInfoEntryBox *iinf = NULL;
		j=0;
		while ((iinf = (GF_ItemInfoEntryBox *)gf_list_enum(meta->item_infos->item_infos, &j))) {
			if (iinf->item_ID==iloc->item_ID) break;
			iinf = NULL;
		}

		if (!iloc->base_offset && (gf_list_count(iloc->extent_entries)==1)) {
			entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
			if (!entry->extent_length && !entry->original_extent_offset && !entry->extent_index) {
				entry->extent_offset = 0;
				continue;
			}
		}

		it_size = 0;
		/*for self contained only*/
		if (!iloc->data_reference_index) {
			if (iloc->construction_method != 2) {
				iloc->base_offset = baseOffset;
			}

			/*new resource*/
			if (iinf && (iinf->full_path || (iinf->tk_id && iinf->sample_num))) {
				FILE *src=NULL;

				if (!iinf->data_len && iinf->full_path) {
					src = gf_fopen(iinf->full_path, "rb");
					if (!src) continue;
					it_size = gf_fsize(src);
				} else {
					it_size = iinf->data_len;
				}
				if (maxExtendSize<it_size) maxExtendSize = it_size;

				if (!gf_list_count(iloc->extent_entries)) {
					GF_SAFEALLOC(entry, GF_ItemExtentEntry);
					if (!entry) return GF_OUT_OF_MEM;
					gf_list_add(iloc->extent_entries, entry);
				}
				entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
				entry->extent_offset = 0;
				entry->extent_length = it_size;

				//shared data, do not count it
				if (iinf->tk_id && iinf->sample_num) {
					it_size = 0;
					maxExtendOffset = 0xFFFFFFFFFFUL;
					if (Emulation) {
						meta->use_item_sample_sharing = GF_TRUE;
					}
				}

				/*OK write to mdat*/
				if (!Emulation) {
					if (iinf->tk_id && iinf->sample_num) {
					}
					else if (src) {
						char cache_data[4096];
						u64 remain = entry->extent_length;
						while (remain) {
							u32 size_cache = (remain>4096) ? 4096 : (u32) remain;
							size_t read = gf_fread(cache_data, size_cache, src);
							if (read ==(size_t) -1) break;
							gf_bs_write_data(bs, cache_data, (u32) read);
							remain -= (u32) read;
						}
					} else {
						gf_bs_write_data(bs, iinf->full_path, iinf->data_len);
					}
				}
				if (src) gf_fclose(src);
			}
			else if (gf_list_count(iloc->extent_entries)) {
				j=0;
				while ((entry = (GF_ItemExtentEntry *)gf_list_enum(iloc->extent_entries, &j))) {
					if (entry->extent_index) continue;
					if (j && (maxExtendOffset<it_size) ) maxExtendOffset = it_size;
					/*compute new offset*/
					entry->extent_offset = baseOffset + it_size;

					it_size += entry->extent_length;
					if (maxExtendSize<entry->extent_length) maxExtendSize = entry->extent_length;

					/*Reading from the input file*/
					if (!Emulation) {
						char cache_data[4096];
						u64 remain = entry->extent_length;
						gf_bs_seek(file->movieFileMap->bs, entry->original_extent_offset + iloc->original_base_offset);
						while (remain) {
							u32 size_cache = (remain>4096) ? 4096 : (u32) remain;
							gf_bs_read_data(file->movieFileMap->bs, cache_data, size_cache);
							/*Writing to the output file*/
							gf_bs_write_data(bs, cache_data, size_cache);
							remain -= size_cache;
						}
					}
				}
			}
			baseOffset += it_size;
			if (mdatSize)
				*mdatSize += it_size;
		} else {
			/*we MUST have at least one extent for the dref data*/
			if (!gf_list_count(iloc->extent_entries)) {
				GF_SAFEALLOC(entry, GF_ItemExtentEntry);
				if (!entry) return GF_OUT_OF_MEM;
				gf_list_add(iloc->extent_entries, entry);
			}
			entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
			entry->extent_offset = 0;
			/*0 means full length of referenced file*/
			entry->extent_length = 0;
		}
	}

	/*update offset & size length fields*/
	if (baseOffset>0xFFFFFFFF) meta->item_locations->base_offset_size = 8;
	else if (baseOffset) meta->item_locations->base_offset_size = 4;

	if (maxExtendSize>0xFFFFFFFF) meta->item_locations->length_size = 8;
	else if (maxExtendSize) meta->item_locations->length_size = 4;

	if (maxExtendOffset>0xFFFFFFFF) meta->item_locations->offset_size = 8;
	else if (maxExtendOffset) meta->item_locations->offset_size = 4;
	return GF_OK;
}


GF_Err DoFullInterleave(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset)
{

	u32 i, tracksDone;
	TrackWriter *tmp, *curWriter, *prevWriter;
	GF_Err e;
	u64 DTS, DTStmp, TStmp;
	s64 res;
	u32 descIndex, sampSize, chunkNumber;
	u16 curGroupID, curTrackPriority;
	Bool forceNewChunk, writeGroup;
	GF_StscEntry *stsc_ent;
	//this is used to emulate the write ...
	u64 offset, totSize, sampOffset;
	GF_ISOFile *movie = mw->movie;

	totSize = 0;
	curGroupID = 1;

	prevWriter = NULL;
	//we emulate a write from this offset...
	offset = StartOffset;
	tracksDone = 0;

	//browse each groups
	while (1) {
		writeGroup = 1;

		//proceed a group
		while (writeGroup) {
			u32 nb_samp = 1;
			Bool self_contained, chunked_forced=GF_FALSE;
			//first get the appropriated sample for the min time in this group
			curWriter = NULL;
			DTStmp = (u64) -1;
			TStmp = 0;
			curTrackPriority = (u16) -1;

			i=0;
			while ((tmp = (TrackWriter*)gf_list_enum(writers, &i))) {

				//is it done writing ?
				//is it in our group ??
				if (tmp->isDone || tmp->stbl->groupID != curGroupID) continue;

				//OK, get the current sample in this track
				stbl_GetSampleDTS(tmp->stbl->TimeToSample, tmp->sampleNumber, &DTS);
				res = TStmp ? DTStmp * tmp->timeScale - DTS * TStmp : 0;
				if (res < 0) continue;
				if ((!res) && curTrackPriority <= tmp->stbl->trackPriority) continue;
				curWriter = tmp;
				curTrackPriority = tmp->stbl->trackPriority;
				DTStmp = DTS;
				TStmp = tmp->timeScale;
			}
			//no sample found, we're done with this group
			if (!curWriter) {
				//we're done with the group
				writeGroup = 0;
				continue;
			}
			//To Check: are empty sample tables allowed ???
			if (curWriter->sampleNumber > curWriter->stbl->SampleSize->sampleCount) {
				curWriter->isDone = 1;
				tracksDone ++;
				continue;
			}

			e = stbl_GetSampleInfos(curWriter->stbl, curWriter->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &stsc_ent);
			if (e) return e;
			e = stbl_GetSampleSize(curWriter->stbl->SampleSize, curWriter->sampleNumber, &sampSize);
			if (e) return e;

			update_writer_constant_dur(movie, curWriter, stsc_ent, &nb_samp, &sampSize, GF_FALSE);

			if (curWriter->stbl->MaxChunkSize && (curWriter->chunkSize + sampSize > curWriter->stbl->MaxChunkSize)) {
				curWriter->chunkSize = 0;
				chunked_forced = forceNewChunk = 1;
			}
			curWriter->chunkSize += sampSize;

			self_contained = ((curWriter->all_dref_mode==ISOM_DREF_SELF) || Media_IsSelfContained(curWriter->mdia, descIndex) ) ? GF_TRUE : GF_FALSE;

			//do we actually write, or do we emulate ?
			if (Emulation) {
				//are we in the same track ??? If not, force a new chunk when adding this sample
				if (!chunked_forced) {
					if (curWriter != prevWriter) {
						forceNewChunk = 1;
					} else {
						forceNewChunk = 0;
					}
				}
				//update our offsets...
				if (self_contained) {
					e = stbl_SetChunkAndOffset(curWriter->stbl, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, offset, forceNewChunk, nb_samp);
					if (e) return e;
					offset += sampSize;
					totSize += sampSize;
				} else {
//					if (curWriter->prev_offset != sampOffset) forceNewChunk = 1;
					curWriter->prev_offset = sampOffset + sampSize;

					//we have a DataRef, so use the offset idicated in sampleToChunk
					//and ChunkOffset tables...
					e = stbl_SetChunkAndOffset(curWriter->stbl, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, sampOffset, chunked_forced, nb_samp);
					if (e) return e;
				}
			} else {
				//this is no game, we're writing ....
				if (self_contained) {
					e = WriteSample(mw, sampSize, sampOffset, stsc_ent->isEdited, bs, 1);
					if (e) return e;
				}
			}
			//ok, the sample is done
			if (curWriter->sampleNumber == curWriter->stbl->SampleSize->sampleCount) {
				curWriter->isDone = 1;
				//one more track done...
				tracksDone ++;
			} else {
				curWriter->sampleNumber += nb_samp;
			}
			prevWriter = curWriter;
		}
		//if all our track are done, break
		if (tracksDone == gf_list_count(writers)) break;
		//go to next group
		curGroupID ++;
	}
	if (movie->mdat)
		movie->mdat->dataSize = totSize;
	return GF_OK;
}


static void ShiftMetaOffset(GF_MetaBox *meta, u64 offset)
{
	u32 i, count;
	if (!meta->item_locations) return;

	count = gf_list_count(meta->item_locations->location_entries);
	for (i=0; i<count; i++) {
		GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i);
		if (iloc->data_reference_index) continue;
		if (iloc->construction_method == 2) continue;
		if (!iloc->base_offset) {
			GF_ItemExtentEntry *entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0);
			if (entry && !entry->extent_length && !entry->original_extent_offset && (gf_list_count(iloc->extent_entries)==1) )
				continue;
		}

		iloc->base_offset += offset;
	}
}


u64 GetMoovAndMetaSize(GF_ISOFile *movie, GF_List *writers)
{
	u32 i;
	u64 size;

	size = 0;
	if (movie->moov) {
		TrackWriter *writer;
		gf_isom_box_size((GF_Box *)movie->moov);
		size = movie->moov->size;
		if (size > 0xFFFFFFFF) size += 8;

		i=0;
		while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) {
			size -= writer->stbl->ChunkOffset->size;
			size -= writer->stbl->SampleToChunk->size;
			gf_isom_box_size((GF_Box *)writer->stsc);
			gf_isom_box_size(writer->stco);
			size += writer->stsc->size;
			size += writer->stco->size;
		}
	}
	if (movie->meta) {
		u64 msize;
		gf_isom_box_size((GF_Box *)movie->meta);
		msize = movie->meta->size;
		if (msize > 0xFFFFFFFF) msize += 8;
		size += msize;
	}
	return size;
}


GF_Err WriteToFile(GF_ISOFile *movie, Bool for_fragments)
{
	MovieWriter mw;
	GF_Err e = GF_OK;
	if (!movie) return GF_BAD_PARAM;

	if (movie->openMode == GF_ISOM_OPEN_READ) return GF_BAD_PARAM;

	e = gf_isom_insert_copyright(movie);
	if (e) return e;

	memset(&mw, 0, sizeof(mw));
	mw.movie = movie;

	if (movie->moov) {
		u32 i;
		GF_TrackBox *trak;
		if (gf_sys_is_test_mode()) {
			movie->moov->mvhd->creationTime = 0;
			movie->moov->mvhd->modificationTime = 0;
		}
		i=0;
		while ( (trak = gf_list_enum(movie->moov->trackList, &i))) {
			if (gf_sys_is_test_mode()) {
				trak->Header->creationTime = 0;
				trak->Header->modificationTime = 0;
				if (trak->Media->handler->nameUTF8 && strstr(trak->Media->handler->nameUTF8, "@GPAC")) {
					gf_free(trak->Media->handler->nameUTF8);
					trak->Media->handler->nameUTF8 = gf_strdup("MediaHandler");
				}
				trak->Media->mediaHeader->creationTime = 0;
				trak->Media->mediaHeader->modificationTime = 0;
			}
			if (trak->chunk_cache) {
				gf_isom_flush_chunk(trak, GF_TRUE);
			}
		}
	}
	//capture mode: we don't need a new bitstream
	if (movie->openMode == GF_ISOM_OPEN_WRITE) {
		if (!strcmp(movie->fileName, "_gpac_isobmff_redirect")) {
			GF_BitStream *bs, *moov_bs=NULL;
			u64 mdat_end = gf_bs_get_position(movie->editFileMap->bs);
			u64 mdat_start = movie->mdat->bsOffset;
			u64 mdat_size = mdat_end - mdat_start;

			if (for_fragments) {
				if (!movie->on_block_out) {
					GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Missing output block callback, cannot write\n"));
					return GF_BAD_PARAM;
				}
				bs = gf_bs_new_cbk(movie->on_block_out, movie->on_block_out_usr_data, movie->on_block_out_block_size);
				e = WriteFlat(&mw, 0, bs, GF_TRUE, GF_TRUE, NULL);
				movie->fragmented_file_pos = gf_bs_get_position(bs);
				gf_bs_del(bs);
				return e;
			}
			//seek at end in case we had a read of the file
			gf_bs_seek(movie->editFileMap->bs, gf_bs_get_size(movie->editFileMap->bs) );

			if ((movie->storageMode==GF_ISOM_STORE_FASTSTART) && mdat_start && mdat_size) {
				moov_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
			}
			//write as non-seekable
			e = WriteFlat(&mw, 0, movie->editFileMap->bs, GF_TRUE, GF_FALSE, moov_bs);

			movie->fragmented_file_pos = gf_bs_get_position(movie->editFileMap->bs);

			if (mdat_start && mdat_size) {
				u8 data[16];
				if (!movie->on_block_out) {
					GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Missing output block patch callback, cannot patch mdat size in flat storage\n"));
					return GF_BAD_PARAM;
				}

				//create a patch packet for mdat covering out 16 bytes (cf FlushCapture)
				bs = gf_bs_new(data, 16, GF_BITSTREAM_WRITE);
				gf_bs_write_u32(bs, (mdat_size>0xFFFFFFFF) ? 1 : (u32) mdat_size);
				gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT);
				if  (mdat_size>0xFFFFFFFF)
					gf_bs_write_u64(bs, mdat_size);
				else
					gf_bs_write_u64(bs, 0);
				gf_bs_del(bs);
				movie->on_block_patch(movie->on_block_out_usr_data, data, 16, mdat_start, GF_FALSE);
			}

			if (moov_bs) {
				u8 *moov_data;
				u32 moov_size;

				gf_bs_get_content(moov_bs, &moov_data, &moov_size);
				gf_bs_del(moov_bs);

				movie->on_block_patch(movie->on_block_out_usr_data, moov_data, moov_size, mdat_start, GF_TRUE);
				gf_free(moov_data);
			}
		} else {
			GF_BitStream *moov_bs = NULL;
			if ((movie->storageMode==GF_ISOM_STORE_STREAMABLE) || (movie->storageMode==GF_ISOM_STORE_FASTSTART) ) {
				moov_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
			}
			e = WriteFlat(&mw, 0, movie->editFileMap->bs, GF_FALSE, GF_FALSE, moov_bs);
			if (moov_bs) {
				u8 *moov_data;
				u32 moov_size;

				gf_bs_get_content(moov_bs, &moov_data, &moov_size);
				gf_bs_del(moov_bs);
				if (!e)
					e = gf_bs_insert_data(movie->editFileMap->bs, moov_data, moov_size, movie->mdat->bsOffset);

				gf_free(moov_data);
			}
		}
	} else {
		FILE *stream=NULL;
		Bool is_stdout = GF_FALSE;
		GF_BitStream *bs=NULL;
		if (!strcmp(movie->finalName, "_gpac_isobmff_redirect")) {
			if (!movie->on_block_out) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOBMFF] Missing output block callback, cannot write\n"));
				return GF_BAD_PARAM;
			}
			bs = gf_bs_new_cbk(movie->on_block_out, movie->on_block_out_usr_data, movie->on_block_out_block_size);
			is_stdout = GF_TRUE;
		} else {
			if (!strcmp(movie->finalName, "std"))
				is_stdout = GF_TRUE;

			//OK, we need a new bitstream
			stream = is_stdout ? stdout : gf_fopen(movie->finalName, "w+b");
			if (!stream)
				return GF_IO_ERR;
			bs = gf_bs_from_file(stream, GF_BITSTREAM_WRITE);
		}
		if (!bs) {
			if (!is_stdout)
				gf_fclose(stream);
			return GF_OUT_OF_MEM;
		}

		switch (movie->storageMode) {
		case GF_ISOM_STORE_TIGHT:
		case GF_ISOM_STORE_INTERLEAVED:
			e = WriteInterleaved(&mw, bs, 0);
			break;
		case GF_ISOM_STORE_DRIFT_INTERLEAVED:
			e = WriteInterleaved(&mw, bs, 1);
			break;
		case GF_ISOM_STORE_STREAMABLE:
			e = WriteFlat(&mw, 1, bs, is_stdout, GF_FALSE, NULL);
			break;
		default:
			e = WriteFlat(&mw, 0, bs, is_stdout, GF_FALSE, NULL);
			break;
		}

		gf_bs_del(bs);
		if (!is_stdout)
			gf_fclose(stream);
	}
	if (mw.buffer) gf_free(mw.buffer);
	if (mw.nb_done<mw.total_samples) {
		mw.nb_done = mw.total_samples;
		muxer_report_progress(&mw);
	}
	return e;
}


GF_Err SetupWriters(MovieWriter *mw, GF_List *writers, u8 interleaving)
{
	u32 i, trackCount;
	TrackWriter *writer;
	GF_TrackBox *trak;
	GF_ISOFile *movie = mw->movie;

	mw->total_samples = mw->nb_done = 0;
	if (!movie->moov) return GF_OK;

	trackCount = gf_list_count(movie->moov->trackList);
	for (i = 0; i < trackCount; i++) {
		GF_SampleTableBox *stbl;
		trak = gf_isom_get_track(movie->moov, i+1);

		stbl = (trak->Media && trak->Media->information) ? trak->Media->information->sampleTable : NULL;
		if (!stbl || !stbl->SampleSize || !stbl->ChunkOffset || !stbl->SampleToChunk) {
			return GF_ISOM_INVALID_FILE;
		}

		GF_SAFEALLOC(writer, TrackWriter);
		if (!writer) goto exit;
		writer->sampleNumber = 1;
		writer->mdia = trak->Media;
		writer->stbl = trak->Media->information->sampleTable;
		writer->timeScale = trak->Media->mediaHeader->timeScale;
		writer->all_dref_mode = Media_SelfContainedType(writer->mdia);

		if (trak->sample_encryption)
			writer->prevent_dispatch = GF_TRUE;

		writer->isDone = 0;
		writer->DTSprev = 0;
		writer->chunkDur = 0;
		writer->chunkSize = 0;
		writer->constant_size = writer->constant_dur = 0;
		if (writer->stbl->SampleSize->sampleSize)
			writer->constant_size = writer->stbl->SampleSize->sampleSize;
		if (writer->stbl->TimeToSample->nb_entries==1) {
			writer->constant_dur = writer->stbl->TimeToSample->entries[0].sampleDelta;
			if (writer->constant_dur>1) writer->constant_dur = 0;
		}
		if (!writer->constant_dur || !writer->constant_size || (writer->constant_size>=10))
			writer->constant_size = writer->constant_dur = 0;

		writer->stsc = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC);
		if (!writer->stsc) return GF_OUT_OF_MEM;
		if (writer->stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) {
			writer->stco = gf_isom_box_new(GF_ISOM_BOX_TYPE_STCO);
		} else {
			writer->stco = gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64);
		}
		if (!writer->stco) return GF_OUT_OF_MEM;
		/*stops from chunk escape*/
		if (interleaving) writer->stbl->MaxSamplePerChunk = 0;
		/*for progress, assume only one descIndex*/
		if (Media_IsSelfContained(writer->mdia, 1))
			mw->total_samples += writer->stbl->SampleSize->sampleCount;
		/*optimization for interleaving: put audio last (this can be overridden by priorities)*/
		if (movie->storageMode != GF_ISOM_STORE_INTERLEAVED) {
			gf_list_add(writers, writer);
		} else {
			if (writer->mdia->information->InfoHeader && writer->mdia->information->InfoHeader->type == GF_ISOM_BOX_TYPE_SMHD) {
				gf_list_add(writers, writer);
			} else {
				gf_list_insert(writers, writer, 0);
			}
		}
		if (movie->sample_groups_in_traf && trak->Media->information->sampleTable) {
			gf_isom_box_array_del_parent(&trak->Media->information->sampleTable->child_boxes, trak->Media->information->sampleTable->sampleGroupsDescription);
			trak->Media->information->sampleTable->sampleGroupsDescription = NULL;
		}
	}
	return GF_OK;

exit:
	CleanWriters(writers);
	return GF_OUT_OF_MEM;
}


GF_Err ResetWriters(GF_List *writers)
{
	u32 i;
	TrackWriter *writer;
	i=0;
	while ((writer = (TrackWriter *)gf_list_enum(writers, &i))) {
		writer->isDone = 0;
		writer->chunkDur = 0;
		writer->chunkSize = 0;
		writer->DTSprev = 0;
		writer->sampleNumber = 1;
		gf_isom_box_del((GF_Box *)writer->stsc);
		writer->stsc = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC);
		if (!writer->stsc) return GF_OUT_OF_MEM;
		if (writer->stco->type == GF_ISOM_BOX_TYPE_STCO) {
			gf_free(((GF_ChunkOffsetBox *)writer->stco)->offsets);
			((GF_ChunkOffsetBox *)writer->stco)->offsets = NULL;
			((GF_ChunkOffsetBox *)writer->stco)->nb_entries = 0;
			((GF_ChunkOffsetBox *)writer->stco)->alloc_size = 0;
		} else {
			gf_free(((GF_ChunkLargeOffsetBox *)writer->stco)->offsets);
			((GF_ChunkLargeOffsetBox *)writer->stco)->offsets = NULL;
			((GF_ChunkLargeOffsetBox *)writer->stco)->nb_entries = 0;
			((GF_ChunkLargeOffsetBox *)writer->stco)->alloc_size = 0;
		}
	}
	return GF_OK;
}


static GF_Err gf_isom_insert_copyright(GF_ISOFile *movie)
{
	u32 i;
	GF_Box *a;
	GF_FreeSpaceBox *_free;
	i=0;
	while ((a = (GF_Box *)gf_list_enum(movie->TopBoxes, &i))) {
		if (a->type == GF_ISOM_BOX_TYPE_FREE) {
			_free = (GF_FreeSpaceBox *)a;
			if (_free->dataSize) {
				if (!strcmp(_free->data, GPAC_ISOM_CPRT_NOTICE_VERSION)) return GF_OK;
				if (strstr(_free->data, GPAC_ISOM_CPRT_NOTICE)) {
					gf_free(_free->data);
					_free->data = gf_strdup(gf_sys_is_test_mode() ? GPAC_ISOM_CPRT_NOTICE : GPAC_ISOM_CPRT_NOTICE_VERSION);
					_free->dataSize = 1 + (u32) strlen(_free->data);
					return GF_OK;
				}
			}
		}
	}
	a = gf_isom_box_new(GF_ISOM_BOX_TYPE_FREE);
	if (!a) return GF_OUT_OF_MEM;
	_free = (GF_FreeSpaceBox *)a;
	_free->data = gf_strdup(gf_sys_is_test_mode() ? GPAC_ISOM_CPRT_NOTICE : GPAC_ISOM_CPRT_NOTICE_VERSION);
	_free->dataSize = (u32) strlen(_free->data) + 1;
	if (!_free->data) return GF_OUT_OF_MEM;
	return gf_list_add(movie->TopBoxes, _free);
}


Prevent malformed upload path causing arbitrary write (#1174)

const std::string &HttpFile::getFileName() const
{
    return implPtr_->getFileName();
}

This affects the package drogonframework/drogon before 1.7.5. The unsafe handling of file names during upload using HttpFile::save() method may enable attackers to write files to arbitrary locations outside the designated target folder.


int HttpFile::save(const std::string &path) const
{
    return implPtr_->save(path);
}


string_view HttpFile::getFileExtension() const
{
    return implPtr_->getFileExtension();
}


int HttpFileImpl::save() const
{
    return save(HttpAppFrameworkImpl::instance().getUploadPath());
}


int HttpFileImpl::saveTo(const filesystem::path &pathAndFileName) const
{
    LOG_TRACE << "save uploaded file:" << pathAndFileName;
    auto wPath = utils::toNativePath(pathAndFileName.native());
    std::ofstream file(wPath, std::ios::binary);
    if (file.is_open())
    {
        file.write(fileContent_.data(), fileContent_.size());
        file.close();
        return 0;
    }
    else
    {
        LOG_ERROR << "save failed!";
        return -1;
    }
}


void HttpFile::setFileName(const std::string &fileName)
{
    implPtr_->setFileName(fileName);
}


HttpFile::HttpFile(std::shared_ptr<HttpFileImpl> &&implPtr)
    : implPtr_(std::move(implPtr))
{
}


const char *HttpFile::fileData() const noexcept
{
    return implPtr_->fileData();
}


std::string HttpFile::getMd5() const
{
    return implPtr_->getMd5();
}


FileType HttpFile::getFileType() const
{
    return implPtr_->getFileType();
}


DROGON_TEST(HttpFile)
{
    SUBSECTION(Save)
    {
        HttpFileImpl file;
        file.setFileName("test_file_name");
        file.setFile("test", 4);
        auto out = file.save("./test_uploads_dir");

        CHECK(out == 0);
        CHECK(filesystem::exists("./test_uploads_dir/test_file_name"));

        filesystem::remove_all("./test_uploads_dir");
    }

    SUBSECTION(SavePathRelativeTraversal)
    {
        auto uploadPath = filesystem::current_path() / "test_uploads_dir";

        HttpFileImpl file;
        file.setFileName("../test_malicious_file_name");
        file.setFile("test", 4);
        auto out = file.save(uploadPath.string());

        CHECK(out == -1);
        CHECK(!filesystem::exists(uploadPath / "../test_malicious_file_name"));

        filesystem::remove_all(uploadPath);
        filesystem::remove(uploadPath / "../test_malicious_file_name");
    }

    SUBSECTION(SavePathAbsoluteTraversal)
    {
        HttpFileImpl file;
        file.setFileName("/tmp/test_malicious_file_name");
        file.setFile("test", 4);
        auto out = file.save("./test_uploads_dir");

        CHECK(out == -1);
        CHECK(!filesystem::exists("/tmp/test_malicious_file_name"));

        filesystem::remove_all("test_uploads_dir");
        filesystem::remove_all("/tmp/test_malicious_file_name");
    }
}


std::string HttpFileImpl::getMd5() const
{
    return utils::getMd5(fileContent_.data(), fileContent_.size());
}


int HttpFile::saveAs(const std::string &fileName) const
{
    return implPtr_->saveAs(fileName);
}


size_t HttpFile::fileLength() const noexcept
{
    return implPtr_->fileLength();
}


int HttpFileImpl::saveAs(const std::string &fileName) const
{
    assert(!fileName.empty());
    filesystem::path fsFileName(utils::toNativePath(fileName));
    if (!fsFileName.is_absolute() && (!fsFileName.has_parent_path() ||
                                      (fsFileName.begin()->string() != "." &&
                                       fsFileName.begin()->string() != "..")))
    {
        filesystem::path fsUploadPath(utils::toNativePath(
            HttpAppFrameworkImpl::instance().getUploadPath()));
        fsFileName = fsUploadPath / fsFileName;
    }
    if (fsFileName.has_parent_path() &&
        !filesystem::exists(fsFileName.parent_path()))
    {
        LOG_TRACE << "create path:" << fsFileName.parent_path();
        drogon::error_code err;
        filesystem::create_directories(fsFileName.parent_path(), err);
        if (err)
        {
            LOG_SYSERR;
            return -1;
        }
    }
    return saveTo(fsFileName);
}


void HttpFile::setFile(const char *data, size_t length)
{
    implPtr_->setFile(data, length);
}


int HttpFileImpl::save(const std::string &path) const
{
    assert(!path.empty());
    if (fileName_.empty())
        return -1;
    filesystem::path fsUploadDir(utils::toNativePath(path));

    if (!fsUploadDir.is_absolute() && (!fsUploadDir.has_parent_path() ||
                                       (fsUploadDir.begin()->string() != "." &&
                                        fsUploadDir.begin()->string() != "..")))
    {
        fsUploadDir = utils::toNativePath(
                          HttpAppFrameworkImpl::instance().getUploadPath()) /
                      fsUploadDir;
    }

    fsUploadDir = filesystem::weakly_canonical(fsUploadDir);

    if (!filesystem::exists(fsUploadDir))
    {
        LOG_TRACE << "create path:" << fsUploadDir;
        drogon::error_code err;
        filesystem::create_directories(fsUploadDir, err);
        if (err)
        {
            LOG_SYSERR;
            return -1;
        }
    }

    filesystem::path fsSaveToPath(filesystem::weakly_canonical(
        fsUploadDir / utils::toNativePath(fileName_)));

    if (!std::equal(fsUploadDir.begin(),
                    fsUploadDir.end(),
                    fsSaveToPath.begin()))
    {
        LOG_ERROR
            << "Attempt writing outside of upload directory detected. Path: "
            << fileName_;
        return -1;
    }

    return saveTo(fsSaveToPath);
}


const std::string &HttpFile::getItemName() const
{
    return implPtr_->getItemName();
}


int HttpFile::save() const
{
    return implPtr_->save();
}


static inline void RelinquishDCMMemory(DCMInfo *info,DCMMap *map,
  DCMStreamInfo *stream_info,LinkedListInfo *stack,unsigned char *data)
{
  if (info->scale != (Quantum *) NULL)
    info->scale=(Quantum *) RelinquishMagickMemory(info->scale);
  if (map->gray != (int *) NULL)
    map->gray=(int *) RelinquishMagickMemory(map->gray);
  if (map->blue != (int *) NULL)
    map->blue=(int *) RelinquishMagickMemory(map->blue);
  if (map->green != (int *) NULL)
    map->green=(int *) RelinquishMagickMemory(map->green);
  if (map->red != (int *) NULL)
    map->red=(int *) RelinquishMagickMemory(map->red);
  if (stream_info->offsets != (ssize_t *) NULL)
    stream_info->offsets=(ssize_t *) RelinquishMagickMemory(
      stream_info->offsets);
  if (stream_info != (DCMStreamInfo *) NULL)
    stream_info=(DCMStreamInfo *) RelinquishMagickMemory(stream_info);
  if (stack != (LinkedListInfo *) NULL)
    stack=DestroyLinkedList(stack,RelinquishDCMInfo);
  if (data != (unsigned char *) NULL)
    data=(unsigned char *) RelinquishMagickMemory(data);
}


static MagickBooleanType ReadDCMPixels(Image *image,DCMInfo *info,
  DCMStreamInfo *stream_info,MagickBooleanType first_segment,
  ExceptionInfo *exception)
{
  int
    byte,
    index;

  MagickBooleanType
    status;

  PixelPacket
    pixel;

  ssize_t
    i,
    x;

  Quantum
    *q;

  ssize_t
    y;

  /*
    Convert DCM Medical image to pixel packets.
  */
  byte=0;
  i=0;
  status=MagickTrue;
  (void) memset(&pixel,0,sizeof(pixel));
  for (y=0; y < (ssize_t) image->rows; y++)
  {
    q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
    if (q == (Quantum *) NULL)
      break;
    for (x=0; x < (ssize_t) image->columns; x++)
    {
      if (info->samples_per_pixel == 1)
        {
          int
            pixel_value;

          if (info->bytes_per_pixel == 1)
            pixel_value=info->polarity != MagickFalse ?
              ((int) info->max_value-ReadDCMByte(stream_info,image)) :
              ReadDCMByte(stream_info,image);
          else
            if ((info->bits_allocated != 12) || (info->significant_bits != 12))
              {
                if (info->signed_data != 0)
                  pixel_value=ReadDCMSignedShort(stream_info,image);
                else
                  pixel_value=(int) ReadDCMShort(stream_info,image);
                if (info->polarity != MagickFalse)
                  pixel_value=(int)info->max_value-pixel_value;
              }
            else
              {
                if ((i & 0x01) != 0)
                  {
                    pixel_value=byte;
                    byte=ReadDCMByte(stream_info,image);
                    if (byte >= 0)
                      pixel_value|=(byte << 8);
                  }
                else
                  {
                    pixel_value=ReadDCMSignedShort(stream_info,image);
                    byte=(int) (pixel_value & 0x0f);
                    pixel_value>>=4;
                  }
                i++;
              }
          if (info->signed_data == 1)
            pixel_value-=32767;
          index=pixel_value;
          if (info->rescale != MagickFalse)
            {
              double
                scaled_value;

              scaled_value=pixel_value*info->rescale_slope+
                info->rescale_intercept;
              index=(int) scaled_value;
              if (info->window_width != 0)
                {
                  double
                    window_max,
                    window_min;

                  window_min=ceil(info->window_center-
                    (info->window_width-1.0)/2.0-0.5);
                  window_max=floor(info->window_center+
                    (info->window_width-1.0)/2.0+0.5);
                  if (scaled_value <= window_min)
                    index=0;
                  else
                    if (scaled_value > window_max)
                      index=(int) info->max_value;
                    else
                      index=(int) (info->max_value*(((scaled_value-
                        info->window_center-0.5)/(info->window_width-1))+0.5));
                }
            }
          index&=info->mask;
          index=(int) ConstrainColormapIndex(image,(ssize_t) index,exception);
          if (first_segment != MagickFalse)
            SetPixelIndex(image,(Quantum) index,q);
          else
            SetPixelIndex(image,(Quantum) (((size_t) index) |
              (((size_t) GetPixelIndex(image,q)) << 8)),q);
          pixel.red=(unsigned int) image->colormap[index].red;
          pixel.green=(unsigned int) image->colormap[index].green;
          pixel.blue=(unsigned int) image->colormap[index].blue;
        }
      else
        {
          if (info->bytes_per_pixel == 1)
            {
              pixel.red=(unsigned int) ReadDCMByte(stream_info,image);
              pixel.green=(unsigned int) ReadDCMByte(stream_info,image);
              pixel.blue=(unsigned int) ReadDCMByte(stream_info,image);
            }
          else
            {
              pixel.red=ReadDCMShort(stream_info,image);
              pixel.green=ReadDCMShort(stream_info,image);
              pixel.blue=ReadDCMShort(stream_info,image);
            }
          pixel.red&=info->mask;
          pixel.green&=info->mask;
          pixel.blue&=info->mask;
          if (info->scale != (Quantum *) NULL)
            {
              if ((MagickSizeType) pixel.red <= GetQuantumRange(info->depth))
                pixel.red=(unsigned int) info->scale[pixel.red];
              if ((MagickSizeType) pixel.green <= GetQuantumRange(info->depth))
                pixel.green=(unsigned int) info->scale[pixel.green];
              if ((MagickSizeType) pixel.blue <= GetQuantumRange(info->depth))
                pixel.blue=(unsigned int) info->scale[pixel.blue];
            }
        }
      if (first_segment != MagickFalse)
        {
          SetPixelRed(image,(Quantum) pixel.red,q);
          SetPixelGreen(image,(Quantum) pixel.green,q);
          SetPixelBlue(image,(Quantum) pixel.blue,q);
        }
      else
        {
          SetPixelRed(image,(Quantum) (((size_t) pixel.red) |
            (((size_t) GetPixelRed(image,q)) << 8)),q);
          SetPixelGreen(image,(Quantum) (((size_t) pixel.green) |
            (((size_t) GetPixelGreen(image,q)) << 8)),q);
          SetPixelBlue(image,(Quantum) (((size_t) pixel.blue) |
            (((size_t) GetPixelBlue(image,q)) << 8)),q);
        }
      q+=GetPixelChannels(image);
    }
    if (SyncAuthenticPixels(image,exception) == MagickFalse)
      break;
    if (image->previous == (Image *) NULL)
      {
        status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
          image->rows);
        if (status == MagickFalse)
          break;
      }
  }
  return(status);
}


static Image *ReadDCMImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
#define ThrowDCMException(exception,message) \
{ \
  RelinquishDCMMemory(&info,&map,stream_info,stack,data); \
  if (info_copy != (DCMInfo *) NULL) \
    info_copy=(DCMInfo *) RelinquishDCMInfo(info_copy); \
  ThrowReaderException((exception),(message)); \
}

  char
    explicit_vr[MagickPathExtent],
    implicit_vr[MagickPathExtent],
    magick[MagickPathExtent],
    photometric[MagickPathExtent];

  DCMInfo
    info,
    *info_copy = (DCMInfo *) NULL;

  DCMMap
    map;

  DCMStreamInfo
    *stream_info;

  Image
    *image;

  int
    datum;

  LinkedListInfo
    *stack;

  MagickBooleanType
    explicit_file,
    explicit_retry,
    use_explicit;

  MagickOffsetType
    blob_size,
    offset;

  unsigned char
    *p;

  ssize_t
    i;

  size_t
    colors,
    length,
    number_scenes,
    quantum,
    status;

  ssize_t
    count,
    scene,
    sequence_depth;

  unsigned char
    *data;

  unsigned short
    group,
    element;

  /*
    Open image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  if (image_info->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickCoreSignature);
  image=AcquireImage(image_info,exception);
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  image->depth=8UL;
  image->endian=LSBEndian;
  /*
    Read DCM preamble.
  */
  (void) memset(&info,0,sizeof(info));
  (void) memset(&map,0,sizeof(map));
  data=(unsigned char *) NULL;
  stream_info=(DCMStreamInfo *) AcquireMagickMemory(sizeof(*stream_info));
  sequence_depth=0;
  stack=NewLinkedList(256);
  if (stream_info == (DCMStreamInfo *) NULL)
    ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
  (void) memset(stream_info,0,sizeof(*stream_info));
  count=ReadBlob(image,128,(unsigned char *) magick);
  if (count != 128)
    ThrowDCMException(CorruptImageError,"ImproperImageHeader")
  count=ReadBlob(image,4,(unsigned char *) magick);
  if ((count != 4) || (LocaleNCompare(magick,"DICM",4) != 0))
    {
      offset=SeekBlob(image,0L,SEEK_SET);
      if (offset < 0)
        ThrowDCMException(CorruptImageError,"ImproperImageHeader")
    }
  /*
    Read DCM Medical image.
  */
  (void) CopyMagickString(photometric,"MONOCHROME1 ",MagickPathExtent);
  info.bits_allocated=8;
  info.bytes_per_pixel=1;
  info.depth=8;
  info.mask=0xffff;
  info.max_value=255UL;
  info.samples_per_pixel=1;
  info.signed_data=(~0UL);
  info.rescale_slope=1.0;
  element=0;
  explicit_vr[2]='\0';
  explicit_file=MagickFalse;
  colors=0;
  number_scenes=1;
  use_explicit=MagickFalse;
  explicit_retry=MagickFalse;
  blob_size=(MagickOffsetType) GetBlobSize(image);
  while (TellBlob(image) < blob_size)
  {
    for (group=0; (group != 0x7FE0) || (element != 0x0010) ; )
    {
      /*
        Read a group.
      */
      image->offset=(ssize_t) TellBlob(image);
      group=ReadBlobLSBShort(image);
      element=ReadBlobLSBShort(image);
      if ((group == 0xfffc) && (element == 0xfffc))
        break;
      if ((group != 0x0002) && (image->endian == MSBEndian))
        {
          group=(unsigned short) ((group << 8) | ((group >> 8) & 0xFF));
          element=(unsigned short) ((element << 8) | ((element >> 8) & 0xFF));
        }
      quantum=0;
      /*
        Find corresponding VR for this group and element.
      */
      for (i=0; dicom_info[i].group < 0xffff; i++)
        if ((group == dicom_info[i].group) &&
            (element == dicom_info[i].element))
          break;
      (void) CopyMagickString(implicit_vr,dicom_info[i].vr,MagickPathExtent);
      count=ReadBlob(image,2,(unsigned char *) explicit_vr);
      if (count != 2)
        ThrowDCMException(CorruptImageError,"ImproperImageHeader")
      /*
        Check for "explicitness", but meta-file headers always explicit.
      */
      if ((explicit_file == MagickFalse) && (group != 0x0002))
        explicit_file=(isupper((int) ((unsigned char) *explicit_vr)) != 0) &&
          (isupper((int) ((unsigned char) *(explicit_vr+1))) != 0) ?
          MagickTrue : MagickFalse;
      use_explicit=((group == 0x0002) && (explicit_retry == MagickFalse)) ||
        (explicit_file != MagickFalse) ? MagickTrue : MagickFalse;
      if ((use_explicit != MagickFalse) && (strncmp(implicit_vr,"xs",2) == 0))
        (void) CopyMagickString(implicit_vr,explicit_vr,MagickPathExtent);
      if ((use_explicit == MagickFalse) || (strncmp(implicit_vr,"!!",2) == 0))
        {
          offset=SeekBlob(image,(MagickOffsetType) -2,SEEK_CUR);
          if (offset < 0)
            ThrowDCMException(CorruptImageError,"ImproperImageHeader")
          quantum=4;
        }
      else
        {
          /*
            Assume explicit type.
          */
          quantum=2;
          if ((strcmp(explicit_vr,"OB") == 0) ||
              (strcmp(explicit_vr,"OW") == 0) ||
              (strcmp(explicit_vr,"OF") == 0) ||
              (strcmp(explicit_vr,"SQ") == 0) ||
              (strcmp(explicit_vr,"UN") == 0) ||
              (strcmp(explicit_vr,"UT") == 0))
            {
              (void) ReadBlobLSBShort(image);
              quantum=4;
            }
        }
      if ((group == 0xFFFE) && (element == 0xE0DD))
        {
          /*
            If we're exiting a sequence, restore the previous image parameters,
            effectively undoing any parameter changes that happened inside the
            sequence.
          */
          sequence_depth--;
          info_copy=(DCMInfo *) RemoveLastElementFromLinkedList(stack);
          if (info_copy == (DCMInfo *)NULL)
            {
              /*
                The sequence's entry and exit points don't line up (tried to
                exit one more sequence than we entered).
              */
              ThrowDCMException(CorruptImageError,"ImproperImageHeader")
            }
          if (info.scale != (Quantum *) NULL)
            info.scale=(Quantum *) RelinquishMagickMemory(info.scale);
          (void) memcpy(&info,info_copy,sizeof(info));
          info_copy=(DCMInfo *) RelinquishMagickMemory(info_copy);
        }
      if (strcmp(explicit_vr,"SQ") == 0)
        {
          /*
            If we're entering a sequence, push the current image parameters
            onto the stack, so we can restore them at the end of the sequence.
          */
          DCMInfo *clone_info = (DCMInfo *) AcquireMagickMemory(sizeof(info));
          if (clone_info == (DCMInfo *) NULL)
            ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
          (void) memcpy(clone_info,&info,sizeof(info));
          clone_info->scale=(Quantum *) AcquireQuantumMemory(
            clone_info->scale_size+1,sizeof(*clone_info->scale));
          if (clone_info->scale == (Quantum *) NULL)
            ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
          (void) memcpy(clone_info->scale,info.scale,clone_info->scale_size*
            sizeof(*clone_info->scale));
          AppendValueToLinkedList(stack,clone_info);
          sequence_depth++;
        }
      datum=0;
      if (quantum == 4)
        {
          if (group == 0x0002)
            datum=ReadBlobLSBSignedLong(image);
          else
            datum=ReadBlobSignedLong(image);
        }
      else
        if (quantum == 2)
          {
            if (group == 0x0002)
              datum=ReadBlobLSBSignedShort(image);
            else
              datum=ReadBlobSignedShort(image);
          }
      quantum=0;
      length=1;
      if (datum != 0)
        {
          if ((strncmp(implicit_vr,"OW",2) == 0) ||
              (strncmp(implicit_vr,"SS",2) == 0) ||
              (strncmp(implicit_vr,"US",2) == 0))
            quantum=2;
          else
            if ((strncmp(implicit_vr,"FL",2) == 0) ||
                (strncmp(implicit_vr,"OF",2) == 0) ||
                (strncmp(implicit_vr,"SL",2) == 0) ||
                (strncmp(implicit_vr,"UL",2) == 0))
              quantum=4;
            else
              if (strncmp(implicit_vr,"FD",2) == 0)
                quantum=8;
              else
                quantum=1;
          if (datum != ~0)
            length=(size_t) datum/quantum;
          else
            {
              /*
                Sequence and item of undefined length.
              */
              quantum=0;
              length=0;
            }
        }
      if (image_info->verbose != MagickFalse)
        {
          /*
            Display Dicom info.
          */
          if (use_explicit == MagickFalse)
            explicit_vr[0]='\0';
          for (i=0; dicom_info[i].description != (char *) NULL; i++)
            if ((group == dicom_info[i].group) &&
                (element == dicom_info[i].element))
              break;
          (void) FormatLocaleFile(stdout,
            "0x%04lX %4ld S%ld %s-%s (0x%04lx,0x%04lx)",
            (unsigned long) image->offset,(long) length,(long) sequence_depth,
            implicit_vr,explicit_vr,(unsigned long) group,
            (unsigned long) element);
          if (dicom_info[i].description != (char *) NULL)
            (void) FormatLocaleFile(stdout," %s",dicom_info[i].description);
          (void) FormatLocaleFile(stdout,": ");
        }
      if ((group == 0x7FE0) && (element == 0x0010))
        {
          if (image_info->verbose != MagickFalse)
            (void) FormatLocaleFile(stdout,"\n");
          break;
        }
      /*
        Allocate space and read an array.
      */
      data=(unsigned char *) NULL;
      if ((length == 1) && (quantum == 1))
        datum=ReadBlobByte(image);
      else
        if ((length == 1) && (quantum == 2))
          {
            if (group == 0x0002)
              datum=ReadBlobLSBSignedShort(image);
            else
              datum=ReadBlobSignedShort(image);
          }
        else
          if ((length == 1) && (quantum == 4))
            {
              if (group == 0x0002)
                datum=ReadBlobLSBSignedLong(image);
              else
                datum=ReadBlobSignedLong(image);
            }
          else
            if ((quantum != 0) && (length != 0))
              {
                if (length > (size_t) GetBlobSize(image))
                  ThrowDCMException(CorruptImageError,
                    "InsufficientImageDataInFile")
                if (~length >= 1)
                  data=(unsigned char *) AcquireQuantumMemory(length+1,quantum*
                    sizeof(*data));
                if (data == (unsigned char *) NULL)
                  ThrowDCMException(ResourceLimitError,
                    "MemoryAllocationFailed")
                count=ReadBlob(image,(size_t) quantum*length,data);
                if (count != (ssize_t) (quantum*length))
                  {
                    if (image_info->verbose != MagickFalse)
                      (void) FormatLocaleFile(stdout,"count=%d quantum=%d "
                        "length=%d group=%d\n",(int) count,(int) quantum,(int)
                        length,(int) group);
                     ThrowDCMException(CorruptImageError,
                       "InsufficientImageDataInFile")
                  }
                data[length*quantum]='\0';
              }
      if ((((unsigned int) group << 16) | element) == 0xFFFEE0DD)
        {
          if (data != (unsigned char *) NULL)
            data=(unsigned char *) RelinquishMagickMemory(data);
          continue;
        }
      switch (group)
      {
        case 0x0002:
        {
          switch (element)
          {
            case 0x0010:
            {
              char
                transfer_syntax[MagickPathExtent];

              /*
                Transfer Syntax.
              */
              if ((datum == 0) && (explicit_retry == MagickFalse))
                {
                  explicit_retry=MagickTrue;
                  (void) SeekBlob(image,(MagickOffsetType) 0,SEEK_SET);
                  group=0;
                  element=0;
                  if (image_info->verbose != MagickFalse)
                    (void) FormatLocaleFile(stdout,
                      "Corrupted image - trying explicit format\n");
                  break;
                }
              *transfer_syntax='\0';
              if (data != (unsigned char *) NULL)
                (void) CopyMagickString(transfer_syntax,(char *) data,
                  MagickPathExtent);
              if (image_info->verbose != MagickFalse)
                (void) FormatLocaleFile(stdout,"transfer_syntax=%s\n",
                  (const char *) transfer_syntax);
              if (strncmp(transfer_syntax,"1.2.840.10008.1.2",17) == 0)
                {
                  int
                    subtype,
                    type;

                  type=1;
                  subtype=0;
                  if (strlen(transfer_syntax) > 17)
                    {
                      count=(ssize_t) sscanf(transfer_syntax+17,".%d.%d",&type,
                        &subtype);
                      if (count < 1)
                        ThrowDCMException(CorruptImageError,
                          "ImproperImageHeader")
                    }
                  switch (type)
                  {
                    case 1:
                    {
                      image->endian=LSBEndian;
                      break;
                    }
                    case 2:
                    {
                      image->endian=MSBEndian;
                      break;
                    }
                    case 4:
                    {
                      if ((subtype >= 80) && (subtype <= 81))
                        image->compression=JPEGCompression;
                      else
                        if ((subtype >= 90) && (subtype <= 93))
                          image->compression=JPEG2000Compression;
                        else
                          image->compression=JPEGCompression;
                      break;
                    }
                    case 5:
                    {
                      image->compression=RLECompression;
                      break;
                    }
                  }
                }
              break;
            }
            default:
              break;
          }
          break;
        }
        case 0x0028:
        {
          switch (element)
          {
            case 0x0002:
            {
              /*
                Samples per pixel.
              */
              info.samples_per_pixel=(size_t) datum;
              if ((info.samples_per_pixel == 0) || (info.samples_per_pixel > 4))
                ThrowDCMException(CorruptImageError,"ImproperImageHeader")
              break;
            }
            case 0x0004:
            {
              /*
                Photometric interpretation.
              */
              if (data == (unsigned char *) NULL)
                break;
              for (i=0; i < (ssize_t) MagickMin(length,MagickPathExtent-1); i++)
                photometric[i]=(char) data[i];
              photometric[i]='\0';
              info.polarity=LocaleCompare(photometric,"MONOCHROME1 ") == 0 ?
                MagickTrue : MagickFalse;
              break;
            }
            case 0x0006:
            {
              /*
                Planar configuration.
              */
              if (datum == 1)
                image->interlace=PlaneInterlace;
              break;
            }
            case 0x0008:
            {
              /*
                Number of frames.
              */
              if (data == (unsigned char *) NULL)
                break;
              number_scenes=StringToUnsignedLong((char *) data);
              break;
            }
            case 0x0010:
            {
              /*
                Image rows.
              */
              info.height=(size_t) datum;
              break;
            }
            case 0x0011:
            {
              /*
                Image columns.
              */
              info.width=(size_t) datum;
              break;
            }
            case 0x0100:
            {
              /*
                Bits allocated.
              */
              info.bits_allocated=(size_t) datum;
              info.bytes_per_pixel=1;
              if (datum > 8)
                info.bytes_per_pixel=2;
              info.depth=info.bits_allocated;
              if ((info.depth == 0) || (info.depth > 32))
                ThrowDCMException(CorruptImageError,"ImproperImageHeader")
              info.max_value=(1UL << info.bits_allocated)-1;
              image->depth=info.depth;
              break;
            }
            case 0x0101:
            {
              /*
                Bits stored.
              */
              info.significant_bits=(size_t) datum;
              info.bytes_per_pixel=1;
              if (info.significant_bits > 8)
                info.bytes_per_pixel=2;
              info.depth=info.significant_bits;
              if ((info.depth == 0) || (info.depth > 16))
                ThrowDCMException(CorruptImageError,"ImproperImageHeader")
              info.max_value=(1UL << info.significant_bits)-1;
              info.mask=(size_t) GetQuantumRange(info.significant_bits);
              image->depth=info.depth;
              break;
            }
            case 0x0102:
            {
              /*
                High bit.
              */
              break;
            }
            case 0x0103:
            {
              /*
                Pixel representation.
              */
              info.signed_data=(size_t) datum;
              break;
            }
            case 0x1050:
            {
              /*
                Visible pixel range: center.
              */
              if (data != (unsigned char *) NULL)
                info.window_center=StringToDouble((char *) data,(char **) NULL);
              break;
            }
            case 0x1051:
            {
              /*
                Visible pixel range: width.
              */
              if (data != (unsigned char *) NULL)
                info.window_width=StringToDouble((char *) data,(char **) NULL);
              break;
            }
            case 0x1052:
            {
              /*
                Rescale intercept
              */
              if (data != (unsigned char *) NULL)
                info.rescale_intercept=StringToDouble((char *) data,
                  (char **) NULL);
              break;
            }
            case 0x1053:
            {
              /*
                Rescale slope
              */
              if (data != (unsigned char *) NULL)
                info.rescale_slope=StringToDouble((char *) data,(char **) NULL);
              break;
            }
            case 0x1200:
            case 0x3006:
            {
              /*
                Populate graymap.
              */
              if (data == (unsigned char *) NULL)
                break;
              colors=(size_t) (length/info.bytes_per_pixel);
              datum=(int) colors;
              if (map.gray != (int *) NULL)
                map.gray=(int *) RelinquishMagickMemory(map.gray);
              map.gray=(int *) AcquireQuantumMemory(MagickMax(colors,65536),
                sizeof(*map.gray));
              if (map.gray == (int *) NULL)
                ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
              (void) memset(map.gray,0,MagickMax(colors,65536)*
                sizeof(*map.gray));
              for (i=0; i < (ssize_t) colors; i++)
                if (info.bytes_per_pixel == 1)
                  map.gray[i]=(int) data[i];
                else
                  map.gray[i]=(int) ((short *) data)[i];
              break;
            }
            case 0x1201:
            {
              unsigned short
                index;

              /*
                Populate redmap.
              */
              if (data == (unsigned char *) NULL)
                break;
              colors=(size_t) (length/info.bytes_per_pixel);
              datum=(int) colors;
              if (map.red != (int *) NULL)
                map.red=(int *) RelinquishMagickMemory(map.red);
              map.red=(int *) AcquireQuantumMemory(MagickMax(colors,65536),
                sizeof(*map.red));
              if (map.red == (int *) NULL)
                ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
              (void) memset(map.red,0,MagickMax(colors,65536)*
                sizeof(*map.red));
              p=data;
              for (i=0; i < (ssize_t) colors; i++)
              {
                if (image->endian == MSBEndian)
                  index=(unsigned short) ((*p << 8) | *(p+1));
                else
                  index=(unsigned short) (*p | (*(p+1) << 8));
                map.red[i]=(int) index;
                p+=2;
              }
              break;
            }
            case 0x1202:
            {
              unsigned short
                index;

              /*
                Populate greenmap.
              */
              if (data == (unsigned char *) NULL)
                break;
              colors=(size_t) (length/info.bytes_per_pixel);
              datum=(int) colors;
              if (map.green != (int *) NULL)
                map.green=(int *) RelinquishMagickMemory(map.green);
              map.green=(int *) AcquireQuantumMemory(MagickMax(colors,65536),
                sizeof(*map.green));
              if (map.green == (int *) NULL)
                ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
              (void) memset(map.green,0,MagickMax(colors,65536)*
                sizeof(*map.green));
              p=data;
              for (i=0; i < (ssize_t) colors; i++)
              {
                if (image->endian == MSBEndian)
                  index=(unsigned short) ((*p << 8) | *(p+1));
                else
                  index=(unsigned short) (*p | (*(p+1) << 8));
                map.green[i]=(int) index;
                p+=2;
              }
              break;
            }
            case 0x1203:
            {
              unsigned short
                index;

              /*
                Populate bluemap.
              */
              if (data == (unsigned char *) NULL)
                break;
              colors=(size_t) (length/info.bytes_per_pixel);
              datum=(int) colors;
              if (map.blue != (int *) NULL)
                map.blue=(int *) RelinquishMagickMemory(map.blue);
              map.blue=(int *) AcquireQuantumMemory(MagickMax(colors,65536),
                sizeof(*map.blue));
              if (map.blue == (int *) NULL)
                ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
              (void) memset(map.blue,0,MagickMax(colors,65536)*
                sizeof(*map.blue));
              p=data;
              for (i=0; i < (ssize_t) colors; i++)
              {
                if (image->endian == MSBEndian)
                  index=(unsigned short) ((*p << 8) | *(p+1));
                else
                  index=(unsigned short) (*p | (*(p+1) << 8));
                map.blue[i]=(int) index;
                p+=2;
              }
              break;
            }
            default:
              break;
          }
          break;
        }
        case 0x2050:
        {
          switch (element)
          {
            case 0x0020:
            {
              if ((data != (unsigned char *) NULL) &&
                  (strncmp((char *) data,"INVERSE",7) == 0))
                info.polarity=MagickTrue;
              break;
            }
            default:
              break;
          }
          break;
        }
        default:
          break;
      }
      if (data != (unsigned char *) NULL)
        {
          char
            *attribute;

          for (i=0; dicom_info[i].description != (char *) NULL; i++)
            if ((group == dicom_info[i].group) &&
                (element == dicom_info[i].element))
              break;
          if (dicom_info[i].description != (char *) NULL)
            {
              attribute=AcquireString("dcm:");
              (void) ConcatenateString(&attribute,dicom_info[i].description);
              for (i=0; i < (ssize_t) MagickMax(length,4); i++)
                if (isprint((int) data[i]) == 0)
                  break;
              if ((i == (ssize_t) length) || (length > 4))
                {
                  (void) SubstituteString(&attribute," ","");
                  (void) SetImageProperty(image,attribute,(char *) data,
                    exception);
                }
              attribute=DestroyString(attribute);
            }
        }
      if (image_info->verbose != MagickFalse)
        {
          if (data == (unsigned char *) NULL)
            (void) FormatLocaleFile(stdout,"%d\n",datum);
          else
            {
              /*
                Display group data.
              */
              for (i=0; i < (ssize_t) MagickMax(length,4); i++)
                if (isprint((int) data[i]) == 0)
                  break;
              if ((i != (ssize_t) length) && (length <= 4))
                {
                  ssize_t
                    j;

                  datum=0;
                  for (j=(ssize_t) length-1; j >= 0; j--)
                    datum=(256*datum+data[j]);
                  (void) FormatLocaleFile(stdout,"%d",datum);
                }
              else
                for (i=0; i < (ssize_t) length; i++)
                  if (isprint((int) data[i]) != 0)
                    (void) FormatLocaleFile(stdout,"%c",data[i]);
                  else
                    (void) FormatLocaleFile(stdout,"%c",'.');
              (void) FormatLocaleFile(stdout,"\n");
            }
        }
      if (data != (unsigned char *) NULL)
        data=(unsigned char *) RelinquishMagickMemory(data);
      if (EOFBlob(image) != MagickFalse)
        {
          ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
            image->filename);
          group=0xfffc;
          break;
        }
    }
    if ((group == 0xfffc) && (element == 0xfffc))
      {
        Image
          *last;

        last=RemoveLastImageFromList(&image);
        if (last != (Image *) NULL)
          last=DestroyImage(last);
        break;
      }
    if ((info.width == 0) || (info.height == 0))
      ThrowDCMException(CorruptImageError,"ImproperImageHeader")
    image->columns=info.width;
    image->rows=info.height;
    if (info.signed_data == 0xffff)
      info.signed_data=(size_t) (info.significant_bits == 16 ? 1 : 0);
    if ((image->compression == JPEGCompression) ||
        (image->compression == JPEG2000Compression))
      {
        Image
          *images;

        ImageInfo
          *read_info;

        int
          c;

        /*
          Read offset table.
        */
        for (i=0; i < (ssize_t) stream_info->remaining; i++)
          if (ReadBlobByte(image) == EOF)
            break;
        (void) (((ssize_t) ReadBlobLSBShort(image) << 16) |
          ReadBlobLSBShort(image));
        length=(size_t) ReadBlobLSBLong(image);
        if (length > (size_t) GetBlobSize(image))
          ThrowDCMException(CorruptImageError,"InsufficientImageDataInFile")
        stream_info->offset_count=length >> 2;
        if (stream_info->offset_count != 0)
          {
            if (stream_info->offsets != (ssize_t *) NULL)
              stream_info->offsets=(ssize_t *) RelinquishMagickMemory(
                stream_info->offsets);
            stream_info->offsets=(ssize_t *) AcquireQuantumMemory(
              stream_info->offset_count,sizeof(*stream_info->offsets));
            if (stream_info->offsets == (ssize_t *) NULL)
              ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
            for (i=0; i < (ssize_t) stream_info->offset_count; i++)
              stream_info->offsets[i]=(ssize_t) ReadBlobLSBSignedLong(image);
            offset=TellBlob(image);
            for (i=0; i < (ssize_t) stream_info->offset_count; i++)
              stream_info->offsets[i]+=offset;
          }
        /*
          Handle non-native image formats.
        */
        read_info=CloneImageInfo(image_info);
        SetImageInfoBlob(read_info,(void *) NULL,0);
        images=NewImageList();
        for (scene=0; scene < (ssize_t) number_scenes; scene++)
        {
          char
            filename[MagickPathExtent];

          const char
            *property;

          FILE
            *file;

          Image
            *jpeg_image;

          int
            unique_file;

          unsigned int
            tag;

          tag=((unsigned int) ReadBlobLSBShort(image) << 16) |
            ReadBlobLSBShort(image);
          length=(size_t) ReadBlobLSBLong(image);
          if (length > (size_t) GetBlobSize(image))
            {
              images=DestroyImageList(images);
              read_info=DestroyImageInfo(read_info);
              ThrowDCMException(CorruptImageError,"InsufficientImageDataInFile")
            }
          if (EOFBlob(image) != MagickFalse)
            {
              status=MagickFalse;
              break;
            }
          if (tag == 0xFFFEE0DD)
            break; /* sequence delimiter tag */
          if (tag != 0xFFFEE000)
            {
              status=MagickFalse;
              break;
            }
          file=(FILE *) NULL;
          unique_file=AcquireUniqueFileResource(filename);
          if (unique_file != -1)
            file=fdopen(unique_file,"wb");
          if (file == (FILE *) NULL)
            {
              (void) RelinquishUniqueFileResource(filename);
              ThrowFileException(exception,FileOpenError,
                "UnableToCreateTemporaryFile",filename);
              break;
            }
          for (c=EOF; length != 0; length--)
          {
            c=ReadBlobByte(image);
            if (c == EOF)
              {
                ThrowFileException(exception,CorruptImageError,
                  "UnexpectedEndOfFile",image->filename);
                break;
              }
            if (fputc(c,file) != c)
              break;
          }
          (void) fclose(file);
          if (c == EOF)
            break;
          (void) FormatLocaleString(read_info->filename,MagickPathExtent,
            "jpeg:%s",filename);
          if (image->compression == JPEG2000Compression)
            (void) FormatLocaleString(read_info->filename,MagickPathExtent,
              "j2k:%s",filename);
          jpeg_image=ReadImage(read_info,exception);
          if (jpeg_image != (Image *) NULL)
            {
              ResetImagePropertyIterator(image);
              property=GetNextImageProperty(image);
              while (property != (const char *) NULL)
              {
                (void) SetImageProperty(jpeg_image,property,
                  GetImageProperty(image,property,exception),exception);
                property=GetNextImageProperty(image);
              }
              AppendImageToList(&images,jpeg_image);
            }
          (void) RelinquishUniqueFileResource(filename);
        }
        read_info=DestroyImageInfo(read_info);
        image=DestroyImageList(image);
        if ((status == MagickFalse) && (exception->severity < ErrorException))
          {
            images=DestroyImageList(images);
            ThrowDCMException(CorruptImageError,"CorruptImageError")
          }
        else
          RelinquishDCMMemory(&info,&map,stream_info,stack,data);
        return(GetFirstImageInList(images));
      }
    if (info.depth != (1UL*MAGICKCORE_QUANTUM_DEPTH))
      {
        QuantumAny
          range;

        /*
          Compute pixel scaling table.
        */
        length=(size_t) (GetQuantumRange(info.depth)+1);
        if (length > (size_t) GetBlobSize(image))
          ThrowDCMException(CorruptImageError,"InsufficientImageDataInFile")
        if (info.scale != (Quantum *) NULL)
          info.scale=(Quantum *) RelinquishMagickMemory(info.scale);
        info.scale_size=MagickMax(length,MaxMap)+1;
        info.scale=(Quantum *) AcquireQuantumMemory(info.scale_size,
          sizeof(*info.scale));
        if (info.scale == (Quantum *) NULL)
          ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
        (void) memset(info.scale,0,(MagickMax(length,MaxMap)+1)*
          sizeof(*info.scale));
        range=GetQuantumRange(info.depth);
        for (i=0; i <= (ssize_t) GetQuantumRange(info.depth); i++)
          info.scale[i]=ScaleAnyToQuantum((size_t) i,range);
      }
    if (image->compression == RLECompression)
      {
        unsigned int
          tag;

        /*
          Read RLE offset table.
        */
        for (i=0; i < (ssize_t) stream_info->remaining; i++)
        {
          int
            c;

          c=ReadBlobByte(image);
          if (c == EOF)
            break;
        }
        tag=((unsigned int) ReadBlobLSBShort(image) << 16) |
          ReadBlobLSBShort(image);
        (void) tag;
        length=(size_t) ReadBlobLSBLong(image);
        if (length > (size_t) GetBlobSize(image))
          ThrowDCMException(CorruptImageError,"InsufficientImageDataInFile")
        stream_info->offset_count=length >> 2;
        if (stream_info->offset_count != 0)
          {
            if (stream_info->offsets != (ssize_t *) NULL)
              stream_info->offsets=(ssize_t *)
                RelinquishMagickMemory(stream_info->offsets);
            stream_info->offsets=(ssize_t *) AcquireQuantumMemory(
              stream_info->offset_count,sizeof(*stream_info->offsets));
            if (stream_info->offsets == (ssize_t *) NULL)
              ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
            for (i=0; i < (ssize_t) stream_info->offset_count; i++)
            {
              offset=(MagickOffsetType) ReadBlobLSBSignedLong(image);
              if (offset > (MagickOffsetType) GetBlobSize(image))
                ThrowDCMException(CorruptImageError,
                  "InsufficientImageDataInFile")
              stream_info->offsets[i]=(ssize_t) offset;
              if (EOFBlob(image) != MagickFalse)
                break;
            }
            offset=TellBlob(image)+8;
            for (i=0; i < (ssize_t) stream_info->offset_count; i++)
              stream_info->offsets[i]+=offset;
          }
      }
    for (scene=0; scene < (ssize_t) number_scenes; scene++)
    {
      image->columns=info.width;
      image->rows=info.height;
      image->depth=info.depth;
      status=SetImageExtent(image,image->columns,image->rows,exception);
      if (status == MagickFalse)
        break;
      image->colorspace=RGBColorspace;
      (void) SetImageBackgroundColor(image,exception);
      if ((image->colormap == (PixelInfo *) NULL) &&
          (info.samples_per_pixel == 1))
        {
          int
            index;

          size_t
            one;

          one=1;
          if (colors == 0)
            colors=one << info.depth;
          if (AcquireImageColormap(image,colors,exception) == MagickFalse)
            ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed")
          if (map.red != (int *) NULL)
            for (i=0; i < (ssize_t) colors; i++)
            {
              index=map.red[i];
              if ((info.scale != (Quantum *) NULL) && (index >= 0) &&
                  (index <= (int) info.max_value))
                index=(int) info.scale[index];
              image->colormap[i].red=(MagickRealType) index;
            }
          if (map.green != (int *) NULL)
            for (i=0; i < (ssize_t) colors; i++)
            {
              index=map.green[i];
              if ((info.scale != (Quantum *) NULL) && (index >= 0) &&
                  (index <= (int) info.max_value))
                index=(int) info.scale[index];
              image->colormap[i].green=(MagickRealType) index;
            }
          if (map.blue != (int *) NULL)
            for (i=0; i < (ssize_t) colors; i++)
            {
              index=map.blue[i];
              if ((info.scale != (Quantum *) NULL) && (index >= 0) &&
                  (index <= (int) info.max_value))
                index=(int) info.scale[index];
              image->colormap[i].blue=(MagickRealType) index;
            }
          if (map.gray != (int *) NULL)
            for (i=0; i < (ssize_t) colors; i++)
            {
              index=map.gray[i];
              if ((info.scale != (Quantum *) NULL) && (index >= 0) &&
                  (index <= (int) info.max_value))
                index=(int) info.scale[index];
              image->colormap[i].red=(MagickRealType) index;
              image->colormap[i].green=(MagickRealType) index;
              image->colormap[i].blue=(MagickRealType) index;
            }
        }
      if (image->compression == RLECompression)
        {
          unsigned int
            tag;

          /*
            Read RLE segment table.
          */
          for (i=0; i < (ssize_t) stream_info->remaining; i++)
          {
            int
              c;

            c=ReadBlobByte(image);
            if (c == EOF)
              break;
          }
          tag=((unsigned int) ReadBlobLSBShort(image) << 16) |
            ReadBlobLSBShort(image);
          stream_info->remaining=(size_t) ReadBlobLSBLong(image);
          if ((tag != 0xFFFEE000) || (stream_info->remaining <= 64) ||
              (EOFBlob(image) != MagickFalse))
            {
              if (stream_info->offsets != (ssize_t *) NULL)
                stream_info->offsets=(ssize_t *)
                  RelinquishMagickMemory(stream_info->offsets);
              ThrowDCMException(CorruptImageError,"ImproperImageHeader")
            }
          stream_info->count=0;
          stream_info->segment_count=ReadBlobLSBLong(image);
          for (i=0; i < 15; i++)
            stream_info->segments[i]=(ssize_t) ReadBlobLSBSignedLong(image);
          stream_info->remaining-=64;
          if (stream_info->segment_count > 1)
            {
              info.bytes_per_pixel=1;
              info.depth=8;
              if (stream_info->offset_count > 0)
                (void) SeekBlob(image,(MagickOffsetType)
                  stream_info->offsets[0]+stream_info->segments[0],SEEK_SET);
            }
        }
      if ((info.samples_per_pixel > 1) && (image->interlace == PlaneInterlace))
        {
          Quantum
            *q;

          ssize_t
            x,
            y;

          /*
            Convert Planar RGB DCM Medical image to pixel packets.
          */
          for (i=0; i < (ssize_t) info.samples_per_pixel; i++)
          {
            for (y=0; y < (ssize_t) image->rows; y++)
            {
              q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
              if (q == (Quantum *) NULL)
                break;
              for (x=0; x < (ssize_t) image->columns; x++)
              {
                switch ((int) i)
                {
                  case 0:
                  {
                    SetPixelRed(image,ScaleCharToQuantum((unsigned char)
                      ReadDCMByte(stream_info,image)),q);
                    break;
                  }
                  case 1:
                  {
                    SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
                      ReadDCMByte(stream_info,image)),q);
                    break;
                  }
                  case 2:
                  {
                    SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
                      ReadDCMByte(stream_info,image)),q);
                    break;
                  }
                  case 3:
                  {
                    SetPixelAlpha(image,ScaleCharToQuantum((unsigned char)
                      ReadDCMByte(stream_info,image)),q);
                    break;
                  }
                  default:
                    break;
                }
                q+=GetPixelChannels(image);
              }
              if (SyncAuthenticPixels(image,exception) == MagickFalse)
                break;
              if (image->previous == (Image *) NULL)
                {
                  status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
                    y,image->rows);
                  if (status == MagickFalse)
                    break;
                }
            }
          }
        }
      else
        {
          const char
            *option;

          /*
            Convert DCM Medical image to pixel packets.
          */
          option=GetImageOption(image_info,"dcm:display-range");
          if (option != (const char *) NULL)
            {
              if (LocaleCompare(option,"reset") == 0)
                info.window_width=0;
            }
          option=GetImageOption(image_info,"dcm:window");
          if (option != (char *) NULL)
            {
              GeometryInfo
                geometry_info;

              MagickStatusType
                flags;

              flags=ParseGeometry(option,&geometry_info);
              if (flags & RhoValue)
                info.window_center=geometry_info.rho;
              if (flags & SigmaValue)
                info.window_width=geometry_info.sigma;
              info.rescale=MagickTrue;
            }
          option=GetImageOption(image_info,"dcm:rescale");
          if (option != (char *) NULL)
            info.rescale=IsStringTrue(option);
          if ((info.window_center != 0) && (info.window_width == 0))
            info.window_width=info.window_center;
          status=ReadDCMPixels(image,&info,stream_info,MagickTrue,exception);
          if ((status != MagickFalse) && (stream_info->segment_count > 1))
            {
              if (stream_info->offset_count > 0)
                (void) SeekBlob(image,(MagickOffsetType)
                  stream_info->offsets[0]+stream_info->segments[1],SEEK_SET);
              (void) ReadDCMPixels(image,&info,stream_info,MagickFalse,
                exception);
            }
        }
      if (IdentifyImageCoderGray(image,exception) != MagickFalse)
        (void) SetImageColorspace(image,GRAYColorspace,exception);
      if (EOFBlob(image) != MagickFalse)
        {
          ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
            image->filename);
          break;
        }
      /*
        Proceed to next image.
      */
      if (image_info->number_scenes != 0)
        if (image->scene >= (image_info->scene+image_info->number_scenes-1))
          break;
      if (scene < (ssize_t) (number_scenes-1))
        {
          /*
            Allocate next image structure.
          */
          AcquireNextImage(image_info,image,exception);
          if (GetNextImageInList(image) == (Image *) NULL)
            {
              status=MagickFalse;
              break;
            }
          image=SyncNextImageInList(image);
          status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
            GetBlobSize(image));
          if (status == MagickFalse)
            break;
        }
    }
    if (TellBlob(image) < (MagickOffsetType) GetBlobSize(image))
      {
        /*
          Allocate next image structure.
        */
        AcquireNextImage(image_info,image,exception);
        if (GetNextImageInList(image) == (Image *) NULL)
          {
            status=MagickFalse;
            break;
          }
        image=SyncNextImageInList(image);
        status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
          GetBlobSize(image));
        if (status == MagickFalse)
          break;
     }
  }
  /*
    Free resources.
  */
  RelinquishDCMMemory(&info,&map,stream_info,stack,data);
  if (image == (Image *) NULL)
    return(image);
  (void) CloseBlob(image);
  if (status == MagickFalse)
    return(DestroyImageList(image));
  return(GetFirstImageInList(image));
}


static void *RelinquishDCMInfo(void *memory)
{
  DCMInfo
    *info;

  info=(DCMInfo *) memory;
  if (info->scale != (Quantum *) NULL)
    info->scale=(Quantum *) RelinquishMagickMemory(info->scale);
  return(RelinquishMagickMemory(info));
}


Prevent dereferencing of null pointers in TFLite's `add.cc`.

PiperOrigin-RevId: 387244946
Change-Id: I56094233327fbd8439b92e1dbb1262176e00eeb9

inline void BinaryBroadcastFiveFold(const ArithmeticParams& unswitched_params,
                                    const RuntimeShape& unswitched_input1_shape,
                                    const T* unswitched_input1_data,
                                    const RuntimeShape& unswitched_input2_shape,
                                    const T* unswitched_input2_data,
                                    const RuntimeShape& output_shape,
                                    T* output_data, ElementwiseF elementwise_f,
                                    ScalarBroadcastF scalar_broadcast_f) {
  ArithmeticParams switched_params = unswitched_params;
  switched_params.input1_offset = unswitched_params.input2_offset;
  switched_params.input1_multiplier = unswitched_params.input2_multiplier;
  switched_params.input1_shift = unswitched_params.input2_shift;
  switched_params.input2_offset = unswitched_params.input1_offset;
  switched_params.input2_multiplier = unswitched_params.input1_multiplier;
  switched_params.input2_shift = unswitched_params.input1_shift;

  const bool use_unswitched =
      unswitched_params.broadcast_category ==
      tflite::BroadcastableOpCategory::kFirstInputBroadcastsFast;

  const ArithmeticParams& params =
      use_unswitched ? unswitched_params : switched_params;
  const T* input1_data =
      use_unswitched ? unswitched_input1_data : unswitched_input2_data;
  const T* input2_data =
      use_unswitched ? unswitched_input2_data : unswitched_input1_data;

  // Fivefold nested loops. The second input resets its position for each
  // iteration of the second loop. The first input resets its position at the
  // beginning of the fourth loop. The innermost loop is an elementwise add of
  // sections of the arrays.
  T* output_data_ptr = output_data;
  const T* input1_data_ptr = input1_data;
  const T* input2_data_reset = input2_data;
  // In the fivefold pattern, y0, y2 and y4 are not broadcast, and so shared
  // between input shapes. y3 for input 1 is always broadcast, and so the
  // dimension there is 1, whereas optionally y1 might be broadcast for
  // input 2. Put another way, input1.shape.FlatSize = y0 * y1 * y2 * y4,
  // input2.shape.FlatSize = y0 * y2 * y3 * y4.
  int y0 = params.broadcast_shape[0];
  int y1 = params.broadcast_shape[1];
  int y2 = params.broadcast_shape[2];
  int y3 = params.broadcast_shape[3];
  int y4 = params.broadcast_shape[4];
  if (y4 > 1) {
    // General fivefold pattern, with y4 > 1 so there is a non-broadcast inner
    // dimension.
    for (int i0 = 0; i0 < y0; ++i0) {
      const T* input2_data_ptr = nullptr;
      for (int i1 = 0; i1 < y1; ++i1) {
        input2_data_ptr = input2_data_reset;
        for (int i2 = 0; i2 < y2; ++i2) {
          for (int i3 = 0; i3 < y3; ++i3) {
            elementwise_f(y4, params, input1_data_ptr, input2_data_ptr,
                          output_data_ptr);
            input2_data_ptr += y4;
            output_data_ptr += y4;
          }
          // We have broadcast y4 of input1 data y3 times, and now move on.
          input1_data_ptr += y4;
        }
      }
      // We have broadcast y2*y3*y4 of input2 data y1 times, and now move on.
      input2_data_reset = input2_data_ptr;
    }
  } else if (input1_data_ptr != nullptr) {
    // Special case of y4 == 1, in which the innermost loop is a single
    // element and can be combined with the next (y3) as an inner broadcast.
    //
    // Note that this handles the case of pure scalar broadcast when
    // y0 == y1 == y2 == 1. With low overhead it handles cases such as scalar
    // broadcast with batch (as y2 > 1).
    //
    // NOTE The process is the same as the above general case except
    // simplified for y4 == 1 and the loop over y3 is contained within the
    // AddScalarBroadcast function.
    for (int i0 = 0; i0 < y0; ++i0) {
      const T* input2_data_ptr = nullptr;
      for (int i1 = 0; i1 < y1; ++i1) {
        input2_data_ptr = input2_data_reset;
        for (int i2 = 0; i2 < y2; ++i2) {
          scalar_broadcast_f(y3, params, *input1_data_ptr, input2_data_ptr,
                             output_data_ptr);
          input2_data_ptr += y3;
          output_data_ptr += y3;
          input1_data_ptr += 1;
        }
      }
      input2_data_reset = input2_data_ptr;
    }
  }
}

TensorFlow is an end-to-end open source platform for machine learning. In affected versions an attacker can craft a TFLite model that would trigger a null pointer dereference, which would result in a crash and denial of service. The [implementation](https://github.com/tensorflow/tensorflow/blob/149562d49faa709ea80df1d99fc41d005b81082a/tensorflow/lite/kernels/internal/optimized/optimized_ops.h#L268-L285) unconditionally dereferences a pointer. We have patched the issue in GitHub commit 15691e456c7dc9bd6be203b09765b063bf4a380c. The fix will be included in TensorFlow 2.6.0. We will also cherrypick this commit on TensorFlow 2.5.1, TensorFlow 2.4.3, and TensorFlow 2.3.4, as these are also affected and still in supported range.


Make line_no with too large value(2**20) invalid. Fixes #124

static unsigned short reverseLutFromBitmap(
    const unsigned char bitmap[BITMAP_SIZE], unsigned short lut[USHORT_RANGE]) {
  int k = 0;

  for (int i = 0; i < USHORT_RANGE; ++i) {
    if ((i == 0) || (bitmap[i >> 3] & (1 << (i & 7)))) lut[k++] = i;
  }

  int n = k - 1;

  while (k < USHORT_RANGE) lut[k++] = 0;

  return n;  // maximum k where lut[k] is non-zero,
}  // i.e. number of ones in bitmap minus 1

tinyexr 0.9.5 has a integer overflow over-write in tinyexr::DecodePixelData in tinyexr.h, related to OpenEXR code.


static void cpy2(unsigned short *dst_val, const unsigned short *src_val) {
  unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val);
  const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val);

  dst[0] = src[0];
  dst[1] = src[1];
}


inline long long getBits(int nBits, long long &c, int &lc, const char *&in) {
  while (lc < nBits) {
    c = (c << 8) | *(reinterpret_cast<const unsigned char *>(in++));
    lc += 8;
  }

  lc -= nBits;
  return (c >> lc) & ((1 << nBits) - 1);
}