1// files.cpp - written and placed in the public domain by Wei Dai
2
3#include "pch.h"
4
5#ifndef CRYPTOPP_IMPORTS
6
7#include "files.h"
8
9#include <limits>
10
11NAMESPACE_BEGIN(CryptoPP)
12
13using namespace std;
14
15void Files_TestInstantiations()
16{
17	FileStore f0;
18	FileSource f1;
19	FileSink f2;
20}
21
22void FileStore::StoreInitialize(const NameValuePairs &parameters)
23{
24	m_file.reset(new std::ifstream);
25	const char *fileName;
26	if (parameters.GetValue(Name::InputFileName(), fileName))
27	{
28		ios::openmode binary = parameters.GetValueWithDefault(Name::InputBinaryMode(), true) ? ios::binary : ios::openmode(0);
29		m_file->open(fileName, ios::in | binary);
30		if (!*m_file)
31			throw OpenErr(fileName);
32		m_stream = m_file.get();
33	}
34	else
35	{
36		m_stream = NULL;
37		parameters.GetValue(Name::InputStreamPointer(), m_stream);
38	}
39	m_waiting = false;
40}
41
42lword FileStore::MaxRetrievable() const
43{
44	if (!m_stream)
45		return 0;
46
47	streampos current = m_stream->tellg();
48	streampos end = m_stream->seekg(0, ios::end).tellg();
49	m_stream->seekg(current);
50	return end-current;
51}
52
53size_t FileStore::TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel, bool blocking)
54{
55	if (!m_stream)
56	{
57		transferBytes = 0;
58		return 0;
59	}
60
61	lword size=transferBytes;
62	transferBytes = 0;
63
64	if (m_waiting)
65		goto output;
66
67	while (size && m_stream->good())
68	{
69		{
70		size_t spaceSize = 1024;
71		m_space = HelpCreatePutSpace(target, channel, 1, UnsignedMin(size_t(0)-1, size), spaceSize);
72
73		m_stream->read((char *)m_space, (unsigned int)STDMIN(size, (lword)spaceSize));
74		}
75		m_len = m_stream->gcount();
76		size_t blockedBytes;
77output:
78		blockedBytes = target.ChannelPutModifiable2(channel, m_space, m_len, 0, blocking);
79		m_waiting = blockedBytes > 0;
80		if (m_waiting)
81			return blockedBytes;
82		size -= m_len;
83		transferBytes += m_len;
84	}
85
86	if (!m_stream->good() && !m_stream->eof())
87		throw ReadErr();
88
89	return 0;
90}
91
92size_t FileStore::CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end, const std::string &channel, bool blocking) const
93{
94	if (!m_stream)
95		return 0;
96
97	if (begin == 0 && end == 1)
98	{
99		int result = m_stream->peek();
100		if (result == char_traits<char>::eof())
101			return 0;
102		else
103		{
104			size_t blockedBytes = target.ChannelPut(channel, byte(result), blocking);
105			begin += 1-blockedBytes;
106			return blockedBytes;
107		}
108	}
109
110	// TODO: figure out what happens on cin
111	streampos current = m_stream->tellg();
112	streampos endPosition = m_stream->seekg(0, ios::end).tellg();
113	streampos newPosition = current + (streamoff)begin;
114
115	if (newPosition >= endPosition)
116	{
117		m_stream->seekg(current);
118		return 0;	// don't try to seek beyond the end of file
119	}
120	m_stream->seekg(newPosition);
121	try
122	{
123		assert(!m_waiting);
124		lword copyMax = end-begin;
125		size_t blockedBytes = const_cast<FileStore *>(this)->TransferTo2(target, copyMax, channel, blocking);
126		begin += copyMax;
127		if (blockedBytes)
128		{
129			const_cast<FileStore *>(this)->m_waiting = false;
130			return blockedBytes;
131		}
132	}
133	catch(...)
134	{
135		m_stream->clear();
136		m_stream->seekg(current);
137		throw;
138	}
139	m_stream->clear();
140	m_stream->seekg(current);
141
142	return 0;
143}
144
145lword FileStore::Skip(lword skipMax)
146{
147	lword oldPos = m_stream->tellg();
148	std::istream::off_type offset;
149	if (!SafeConvert(skipMax, offset))
150		throw InvalidArgument("FileStore: maximum seek offset exceeded");
151	m_stream->seekg(offset, ios::cur);
152	return (lword)m_stream->tellg() - oldPos;
153}
154
155void FileSink::IsolatedInitialize(const NameValuePairs &parameters)
156{
157	m_file.reset(new std::ofstream);
158	const char *fileName;
159	if (parameters.GetValue(Name::OutputFileName(), fileName))
160	{
161		ios::openmode binary = parameters.GetValueWithDefault(Name::OutputBinaryMode(), true) ? ios::binary : ios::openmode(0);
162		m_file->open(fileName, ios::out | ios::trunc | binary);
163		if (!*m_file)
164			throw OpenErr(fileName);
165		m_stream = m_file.get();
166	}
167	else
168	{
169		m_stream = NULL;
170		parameters.GetValue(Name::OutputStreamPointer(), m_stream);
171	}
172}
173
174bool FileSink::IsolatedFlush(bool hardFlush, bool blocking)
175{
176	if (!m_stream)
177		throw Err("FileSink: output stream not opened");
178
179	m_stream->flush();
180	if (!m_stream->good())
181		throw WriteErr();
182
183	return false;
184}
185
186size_t FileSink::Put2(const byte *inString, size_t length, int messageEnd, bool blocking)
187{
188	if (!m_stream)
189		throw Err("FileSink: output stream not opened");
190
191	while (length > 0)
192	{
193		std::streamsize size;
194		if (!SafeConvert(length, size))
195			size = numeric_limits<std::streamsize>::max();
196		m_stream->write((const char *)inString, size);
197		inString += size;
198		length -= size;
199	}
200
201	if (messageEnd)
202		m_stream->flush();
203
204	if (!m_stream->good())
205		throw WriteErr();
206
207	return 0;
208}
209
210NAMESPACE_END
211
212#endif
213