1 /*
2  * ggit-blob-output-stream.c
3  * This file is part of libgit2-glib
4  *
5  * Copyright (C) 2013 - Jesse van den Kieboom
6  *
7  * libgit2-glib is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * libgit2-glib is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with libgit2-glib. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "ggit-blob-output-stream.h"
22 #include "ggit-repository.h"
23 #include "ggit-oid.h"
24 #include "ggit-error.h"
25 
26 #include <git2.h>
27 #include <string.h>
28 
29 /**
30  * GgitBlobOutputStream:
31  *
32  * Represents a blob stream object.
33  */
34 
35 typedef struct _GgitBlobOutputStreamPrivate
36 {
37 	GgitRepository *repository;
38 
39 	git_writestream *stream;
40 	gboolean something_written;
41 
42 	gint ret;
43 	GgitOId *oid;
44 } GgitBlobOutputStreamPrivate;
45 
46 G_DEFINE_TYPE_WITH_PRIVATE (GgitBlobOutputStream, ggit_blob_output_stream, G_TYPE_OUTPUT_STREAM)
47 
48 enum
49 {
50 	PROP_0,
51 	PROP_REPOSITORY
52 };
53 
54 static gboolean
ggit_blob_output_stream_close(GOutputStream * object,GCancellable * cancellable,GError ** error)55 ggit_blob_output_stream_close (GOutputStream  *object,
56                                GCancellable   *cancellable,
57                                GError        **error)
58 {
59 	GgitBlobOutputStream *stream = GGIT_BLOB_OUTPUT_STREAM (object);
60 	GgitBlobOutputStreamPrivate *priv;
61 
62 	priv = ggit_blob_output_stream_get_instance_private (stream);
63 
64 	if (g_cancellable_set_error_if_cancelled (cancellable, error))
65 	{
66 		return FALSE;
67 	}
68 
69 	if (priv->ret != GIT_OK)
70 	{
71 		return TRUE;
72 	}
73 
74 	if (priv->something_written)
75 	{
76 		git_oid oid;
77 
78 		if (git_blob_create_fromstream_commit (&oid, priv->stream) == GIT_OK)
79 		{
80 			priv->oid = _ggit_oid_wrap (&oid);
81 		}
82 		else
83 		{
84 			g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
85 			             "Could not create object id");
86 			return FALSE;
87 		}
88 	}
89 	else
90 	{
91 		if (priv->stream->close (priv->stream) != GIT_OK)
92 		{
93 			g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
94 			             "Could not close write stream");
95 			return FALSE;
96 		}
97 	}
98 
99 	return TRUE;
100 }
101 
102 static gboolean
ggit_blob_output_stream_flush(GOutputStream * stream,GCancellable * cancellable,GError ** error)103 ggit_blob_output_stream_flush (GOutputStream  *stream,
104                                GCancellable   *cancellable,
105                                GError        **error)
106 {
107 	if (g_cancellable_set_error_if_cancelled (cancellable, error))
108 	{
109 		return FALSE;
110 	}
111 
112 	return TRUE;
113 }
114 
115 static gssize
ggit_blob_output_stream_write(GOutputStream * object,const void * buffer,gsize count,GCancellable * cancellable,GError ** error)116 ggit_blob_output_stream_write (GOutputStream  *object,
117                                const void     *buffer,
118                                gsize           count,
119                                GCancellable   *cancellable,
120                                GError        **error)
121 {
122 	GgitBlobOutputStream *stream = GGIT_BLOB_OUTPUT_STREAM (object);
123 	GgitBlobOutputStreamPrivate *priv;
124 
125 	priv = ggit_blob_output_stream_get_instance_private (stream);
126 
127 	if (g_cancellable_set_error_if_cancelled (cancellable, error))
128 	{
129 		return -1;
130 	}
131 
132 	if (priv->ret != GIT_OK)
133 	{
134 		g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
135 		             "Could not create write stream");
136 		return -1;
137 	}
138 
139 	if (count == 0)
140 	{
141 		return 0;
142 	}
143 
144 	if (count > 0)
145 	{
146 		gint ret = 0;
147 
148 		ret = priv->stream->write (priv->stream, buffer, count);
149 
150 		if (ret == GIT_OK)
151 		{
152 			priv->something_written = TRUE;
153 			return count;
154 		}
155 	}
156 
157 	g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
158 	             "Could not write the blob");
159 
160 	return -1;
161 }
162 
163 static void
ggit_blob_output_stream_finalize(GObject * object)164 ggit_blob_output_stream_finalize (GObject *object)
165 {
166 	GgitBlobOutputStream *stream;
167 	GgitBlobOutputStreamPrivate *priv;
168 
169 	stream = GGIT_BLOB_OUTPUT_STREAM (object);
170 	priv = ggit_blob_output_stream_get_instance_private (stream);
171 
172 
173 	if (priv->oid)
174 	{
175 		ggit_oid_free (priv->oid);
176 	}
177 	else
178 	{
179 		/* NOTE: if we have an oid the stream is already freed */
180 		priv->stream->free (priv->stream);
181 	}
182 
183 	g_clear_object (&priv->repository);
184 
185 	G_OBJECT_CLASS (ggit_blob_output_stream_parent_class)->finalize (object);
186 }
187 
188 static void
ggit_blob_output_stream_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)189 ggit_blob_output_stream_set_property (GObject      *object,
190                                       guint         prop_id,
191                                       const GValue *value,
192                                       GParamSpec   *pspec)
193 {
194 	GgitBlobOutputStream *stream = GGIT_BLOB_OUTPUT_STREAM (object);
195 	GgitBlobOutputStreamPrivate *priv;
196 
197 	priv = ggit_blob_output_stream_get_instance_private (stream);
198 
199 	switch (prop_id)
200 	{
201 	case PROP_REPOSITORY:
202 		g_clear_object (&priv->repository);
203 		priv->repository = g_value_dup_object (value);
204 		break;
205 	default:
206 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
207 		break;
208 	}
209 }
210 
211 static void
ggit_blob_output_stream_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)212 ggit_blob_output_stream_get_property (GObject    *object,
213                                       guint       prop_id,
214                                       GValue     *value,
215                                       GParamSpec *pspec)
216 {
217 	switch (prop_id)
218 	{
219 	default:
220 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
221 		break;
222 	}
223 }
224 
225 static void
ggit_blob_output_stream_constructed(GObject * object)226 ggit_blob_output_stream_constructed (GObject *object)
227 {
228 	GgitBlobOutputStream *stream;
229 	GgitBlobOutputStreamPrivate *priv;
230 
231 	stream = GGIT_BLOB_OUTPUT_STREAM (object);
232 	priv = ggit_blob_output_stream_get_instance_private (stream);
233 
234 	priv->ret = git_blob_create_fromstream (&priv->stream,
235 	                                        _ggit_native_get (priv->repository),
236 	                                       NULL);
237 
238 	G_OBJECT_CLASS (ggit_blob_output_stream_parent_class)->constructed (object);
239 }
240 
241 static void
ggit_blob_output_stream_class_init(GgitBlobOutputStreamClass * klass)242 ggit_blob_output_stream_class_init (GgitBlobOutputStreamClass *klass)
243 {
244 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
245 	GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass);
246 
247 	object_class->finalize = ggit_blob_output_stream_finalize;
248 	object_class->get_property = ggit_blob_output_stream_get_property;
249 	object_class->set_property = ggit_blob_output_stream_set_property;
250 	object_class->constructed = ggit_blob_output_stream_constructed;
251 
252 	stream_class->write_fn = ggit_blob_output_stream_write;
253 	stream_class->close_fn = ggit_blob_output_stream_close;
254 	stream_class->flush = ggit_blob_output_stream_flush;
255 
256 	g_object_class_install_property (object_class,
257 	                                 PROP_REPOSITORY,
258 	                                 g_param_spec_object ("repository",
259 	                                                      "Repository",
260 	                                                      "Repository",
261 	                                                      GGIT_TYPE_REPOSITORY,
262 	                                                      G_PARAM_WRITABLE |
263 	                                                      G_PARAM_CONSTRUCT_ONLY |
264 	                                                      G_PARAM_STATIC_STRINGS));
265 }
266 
267 static void
ggit_blob_output_stream_init(GgitBlobOutputStream * stream)268 ggit_blob_output_stream_init (GgitBlobOutputStream *stream)
269 {
270 }
271 
272 GgitBlobOutputStream *
_ggit_blob_output_stream_new(GgitRepository * repository)273 _ggit_blob_output_stream_new (GgitRepository  *repository)
274 {
275 	return g_object_new (GGIT_TYPE_BLOB_OUTPUT_STREAM,
276 	                     "repository", repository,
277 	                     NULL);
278 }
279 
280 /**
281  * ggit_blob_output_stream_get_id:
282  * @stream: a #GgitBlobOutputStream.
283  * @error: a #GError for error reporting, or %NULL.
284  *
285  * Get the id of the written blob. The blob id is only available after the
286  * stream has been properly closed. If an error occurred while writing the blob,
287  * the %NULL is returned and @error is set accordingly.
288  *
289  * Returns: (transfer full) (nullable): a #GgitOId or %NULL.
290  *
291  **/
292 GgitOId *
ggit_blob_output_stream_get_id(GgitBlobOutputStream * stream,GError ** error)293 ggit_blob_output_stream_get_id (GgitBlobOutputStream  *stream,
294                                 GError               **error)
295 {
296 	GgitBlobOutputStreamPrivate *priv;
297 
298 	g_return_val_if_fail (GGIT_IS_BLOB_OUTPUT_STREAM (stream), NULL);
299 
300 	priv = ggit_blob_output_stream_get_instance_private (stream);
301 
302 	if (priv->ret != GIT_OK)
303 	{
304 		_ggit_error_set (error, priv->ret);
305 		return NULL;
306 	}
307 
308 	return ggit_oid_copy (priv->oid);
309 }
310 
311 /* ex:set ts=8 noet: */
312