No issues found
1 /*
2 *
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with the program; if not, see <http://www.gnu.org/licenses/>
15 *
16 *
17 * Authors:
18 * Michael Zucchi <notzed@ximian.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 *
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31
32 /* This isn't as portable as, say, the stuff in GNU coreutils.
33 * But I care not for OSF1. */
34 #ifdef HAVE_STATVFS
35 # ifdef HAVE_SYS_STATVFS_H
36 # include <sys/statvfs.h>
37 # endif
38 #else
39 #ifdef HAVE_STATFS
40 # ifdef HAVE_SYS_PARAM_H
41 # include <sys/param.h> /* bsd interface */
42 # endif
43 # ifdef HAVE_SYS_MOUNT_H
44 # include <sys/mount.h>
45 # endif
46 #endif
47 #endif
48
49 #include <errno.h>
50 #include <string.h>
51
52 #include <glib/gstdio.h>
53 #include <glib/gi18n-lib.h>
54
55 #include "e-activity.h"
56 #include "e-file-utils.h"
57
58 typedef struct _AsyncContext AsyncContext;
59
60 struct _AsyncContext {
61 EActivity *activity;
62 gchar *new_etag;
63 };
64
65 static void
66 async_context_free (AsyncContext *context)
67 {
68 if (context->activity != NULL)
69 g_object_unref (context->activity);
70
71 g_free (context->new_etag);
72
73 g_slice_free (AsyncContext, context);
74 }
75
76 static void
77 file_replace_contents_cb (GFile *file,
78 GAsyncResult *result,
79 GSimpleAsyncResult *simple)
80 {
81 AsyncContext *context;
82 gchar *new_etag = NULL;
83 GError *error = NULL;
84
85 context = g_simple_async_result_get_op_res_gpointer (simple);
86
87 g_file_replace_contents_finish (file, result, &new_etag, &error);
88
89 if (!e_activity_handle_cancellation (context->activity, error))
90 e_activity_set_state (context->activity, E_ACTIVITY_COMPLETED);
91
92 if (error == NULL)
93 context->new_etag = new_etag;
94 else {
95 g_warn_if_fail (new_etag == NULL);
96 g_simple_async_result_take_error (simple, error);
97 }
98
99 g_simple_async_result_complete (simple);
100
101 g_object_unref (simple);
102 }
103
104 /**
105 * e_file_replace_contents_async:
106 * @file: input #GFile
107 * @contents: string of contents to replace the file with
108 * @length: the length of @contents in bytes
109 * @etag: a new entity tag for the @file, or %NULL
110 * @make_backup: %TRUE if a backup should be created
111 * @flags: a set of #GFileCreateFlags
112 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
113 * @user_data: the data to pass to the callback function
114 *
115 * This is a wrapper for g_file_replace_contents_async() that also returns
116 * an #EActivity to track the file operation. Cancelling the activity will
117 * cancel the file operation. See g_file_replace_contents_async() for more
118 * details.
119 *
120 * Returns: an #EActivity for the file operation
121 **/
122 EActivity *
123 e_file_replace_contents_async (GFile *file,
124 const gchar *contents,
125 gsize length,
126 const gchar *etag,
127 gboolean make_backup,
128 GFileCreateFlags flags,
129 GAsyncReadyCallback callback,
130 gpointer user_data)
131 {
132 GSimpleAsyncResult *simple;
133 GCancellable *cancellable;
134 AsyncContext *context;
135 const gchar *format;
136 gchar *description;
137 gchar *basename;
138 gchar *filename;
139 gchar *hostname;
140 gchar *uri;
141
142 g_return_val_if_fail (G_IS_FILE (file), NULL);
143 g_return_val_if_fail (contents != NULL, NULL);
144
145 uri = g_file_get_uri (file);
146 filename = g_filename_from_uri (uri, &hostname, NULL);
147 if (filename != NULL)
148 basename = g_filename_display_basename (filename);
149 else
150 basename = g_strdup (_("(Unknown Filename)"));
151
152 if (hostname == NULL) {
153 /* Translators: The string value is the basename of a file. */
154 format = _("Writing \"%s\"");
155 description = g_strdup_printf (format, basename);
156 } else {
157 /* Translators: The first string value is the basename of a
158 * remote file, the second string value is the hostname. */
159 format = _("Writing \"%s\" to %s");
160 description = g_strdup_printf (format, basename, hostname);
161 }
162
163 cancellable = g_cancellable_new ();
164
165 context = g_slice_new0 (AsyncContext);
166 context->activity = e_activity_new ();
167
168 e_activity_set_text (context->activity, description);
169 e_activity_set_cancellable (context->activity, cancellable);
170
171 simple = g_simple_async_result_new (
172 G_OBJECT (file), callback, user_data,
173 e_file_replace_contents_async);
174
175 g_simple_async_result_set_check_cancellable (simple, cancellable);
176
177 g_simple_async_result_set_op_res_gpointer (
178 simple, context, (GDestroyNotify) async_context_free);
179
180 g_file_replace_contents_async (
181 file, contents, length, etag,
182 make_backup, flags, cancellable,
183 (GAsyncReadyCallback) file_replace_contents_cb,
184 simple);
185
186 g_object_unref (cancellable);
187
188 g_free (description);
189 g_free (basename);
190 g_free (filename);
191 g_free (hostname);
192 g_free (uri);
193
194 return context->activity;
195 }
196
197 /**
198 * e_file_replace_contents_finish:
199 * @file: input #GFile
200 * @result: a #GAsyncResult
201 * @new_etag: return location for a new entity tag
202 * @error: return location for a #GError, or %NULL
203 *
204 * Finishes an asynchronous replace of the given @file. See
205 * e_file_replace_contents_async(). Sets @new_etag to the new entity
206 * tag for the document, if present. Free it with g_free() when it is
207 * no longer needed.
208 *
209 * Returns: %TRUE on success, %FALSE on failure
210 **/
211 gboolean
212 e_file_replace_contents_finish (GFile *file,
213 GAsyncResult *result,
214 gchar **new_etag,
215 GError **error)
216 {
217 GSimpleAsyncResult *simple;
218 AsyncContext *context;
219
220 g_return_val_if_fail (G_IS_FILE (file), FALSE);
221 g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
222
223 simple = G_SIMPLE_ASYNC_RESULT (result);
224 context = g_simple_async_result_get_op_res_gpointer (simple);
225
226 if (g_simple_async_result_propagate_error (simple, error))
227 return FALSE;
228
229 if (new_etag != NULL)
230 *new_etag = g_strdup (context->new_etag);
231
232 return TRUE;
233 }