Initial SVN import
[maemo-recorder] / src / maemo-recorder-file.c
1 /* vim: set ts=4 sw=4 et: */
2 /*
3  * maemo-recorder-file.c
4  * File-related operations
5  *
6  * Copyright (C) 2006 Nokia Corporation
7  *
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as
11  * published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16  * for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  *
22  */
23
24
25 #include <libgnomevfs/gnome-vfs.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "maemo-recorder.h"
30 #include "maemo-recorder-file.h"
31 #include "maemo-recorder-au.h"
32
33 static gboolean moveFile(const gchar *src, const gchar *dst);
34
35 #if 0 
36 /* DEPRECATED */
37 gboolean 
38 doSave(const gchar *current, const gchar *target, AudioFormat format)
39 {
40     if (!current|| !target)
41         return FALSE;
42
43     switch (format)
44     {
45         case FORMAT_PCMA:
46             break;
47
48         case FORMAT_PCMU:
49             break;
50
51         case FORMAT_PCM:
52             break;
53             
54         case FORMAT_ILBC:
55         default:
56             return moveFile(current, target);
57     }
58
59     return moveFile(current, target);
60 }
61 #endif
62
63 static gboolean 
64 moveFile(const gchar *src, const gchar *dst)
65 {
66     gchar *tmpcmd = NULL;
67     gint ret;
68
69     /* TODO: escape */
70     tmpcmd = g_strdup_printf("mv '%s' '%s'", src, dst);
71     ret = system(tmpcmd);
72     g_free(tmpcmd);
73
74     if (ret == -1)
75         return FALSE;
76
77     return TRUE;
78 }
79
80 const gchar * 
81 getExtension(gint format)
82 {
83     switch (format)
84     {
85         case FORMAT_ILBC:
86             return EXTENSION_ILBC;
87         case FORMAT_PCMA:
88             /* return EXTENSION_PCMA; */
89         case FORMAT_PCMU:
90             return EXTENSION_AU;
91             /* return EXTENSION_PCMU; */
92         case FORMAT_PCM:
93         default:
94             return EXTENSION_RAW;
95     }
96     return EXTENSION_RAW;
97 }
98
99 /* saveFile
100  *
101  * @param filename the filename from save dialog
102  * @param tmpfile the tmpfile where the actual data is
103  * @param format format of the tmpfile
104  * @param newfile the file (filename + extension) where the data was saved is returned here
105  */
106 gboolean 
107 saveFile(const gchar *filename, const gchar *tmpfile, AudioFormat format, gchar **newfile)
108 {
109     const gchar *ext;
110     GnomeVFSHandle *tmp_handle = NULL;
111     GnomeVFSHandle *to_handle = NULL;
112     gchar *newfile_tmp;
113
114     if (!filename || !tmpfile)
115         return FALSE;
116
117     ext = getExtension(format);
118
119     /* don't append extension if the filename already has it */
120     if (g_str_has_suffix(filename, ext))
121         newfile_tmp = g_strdup(filename);
122     else 
123         newfile_tmp = g_strconcat(filename, ext, NULL);
124
125     g_assert(newfile_tmp);
126
127     if (strcmp(ext, EXTENSION_AU) == 0)
128     {
129         GnomeVFSFileSize len = 0;
130         guint32 encoding = 0;
131         gint written = -1;
132         GnomeVFSURI *uri = NULL;
133         gchar *text_uri;
134         GnomeVFSResult res;
135
136         len = getFileLength(tmpfile);
137         if (len == 0)
138         {
139             ULOG_WARN("%s: file length was zero", G_STRFUNC);
140             goto save_error;
141         }
142
143         encoding = au_get_encoding(format);
144         if (encoding == 0)
145             goto save_error;
146
147         /* open tmpfile(r) */
148         text_uri = g_strconcat("file://", tmpfile, NULL);
149         
150         if (gnome_vfs_open(&tmp_handle, text_uri, GNOME_VFS_OPEN_READ) != GNOME_VFS_OK)
151         {
152             ULOG_WARN("%s: gnome_vfs_open() failed", G_STRFUNC);
153             g_free(text_uri);
154             goto save_error;
155         }
156         
157         g_free(text_uri);
158         text_uri = NULL;
159
160         /* open/create newfile for writing */
161         text_uri = g_strconcat("file://", newfile_tmp, NULL);
162         uri = gnome_vfs_uri_new(text_uri);
163         g_free(text_uri);
164         text_uri = NULL;
165
166         /* TODO: check for symlink and other dangerous stuff */
167         res = gnome_vfs_create_uri(&to_handle, uri, 
168                     GNOME_VFS_OPEN_WRITE, 
169                     0 /* exclusive */,
170                     S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH  /* perm */);
171         if (res != GNOME_VFS_OK)
172         {
173             ULOG_WARN("%s: gnome_vfs_create_uri() failed for '%s': %s", G_STRFUNC, gnome_vfs_uri_to_string(uri, GNOME_VFS_URI_HIDE_NONE), gnome_vfs_result_to_string(res));
174             gnome_vfs_uri_unref(uri);
175             goto save_error;
176         }
177
178         /* TODO: future work: get rate and channels from AudioFormatSpec */
179         written = au_write_copy(to_handle, encoding, DEFAULT_RATE, DEFAULT_CHANNELS, tmp_handle, len);
180
181         gnome_vfs_uri_unref(uri);
182         /* not very clever check but should catch most errors */
183         if (written < len)
184         {
185             ULOG_WARN("%s: au_write_copy() failed", G_STRFUNC);
186             goto save_error;
187         }
188
189         /* todo esta bien, close things and exit */
190         gnome_vfs_close(tmp_handle);
191         gnome_vfs_close(to_handle);
192
193         /* finally, place the filename into newfile */
194         *newfile = newfile_tmp;
195
196         return TRUE;
197     }
198     else
199     {
200         /* everything but AU files are RAW, just rename */
201         if (moveFile(tmpfile, newfile_tmp))
202         {
203             *newfile = newfile_tmp;
204             return TRUE;
205         }
206         goto save_error;
207     }
208
209 save_error:
210
211     if (tmp_handle)
212         gnome_vfs_close(tmp_handle);
213     
214     if (to_handle)
215         gnome_vfs_close(to_handle);
216
217     if (newfile_tmp)
218     {
219         g_free(newfile_tmp);
220         /* don't touch newfile if we're returning false */
221         /* *newfile = NULL; */
222     }
223     return FALSE;
224 }
225     
226 /* openFile takes in the filename and returns:
227  * - whether it is supported format (the boolean return value)
228  * - audio format (FORMAT_XXX)
229  * - the tmpfile where the raw data is (or the same file name if the file was raw)
230  * TODO: add parameter (GError?) to report back an error string
231  */
232 gboolean openFile(const gchar *filename, AudioFormat *format, gchar **tmpfile)
233 {
234     if (g_strrstr(filename, EXTENSION_PCMA))
235     {
236         ULOG_INFO("%s() - file was %s, assuming raw %s, %d Hz, 1 ch", G_STRFUNC, EXTENSION_PCMA, GST_TYPE_PCMA, PCM_RATE);
237         *format = FORMAT_PCMA;
238         *tmpfile = g_strdup(filename);
239     }
240     else if (g_strrstr(filename, EXTENSION_PCMU))
241     {
242         ULOG_INFO("%s() - file was %s, assuming raw %s, %d Hz, 1 ch", G_STRFUNC, EXTENSION_PCMU, GST_TYPE_PCMU, PCM_RATE);
243         *format = FORMAT_PCMU;
244         *tmpfile = g_strdup(filename);
245     }
246     else if (g_strrstr(filename, EXTENSION_RAW))
247     {
248         ULOG_INFO("%s() - file was %s, assuming raw %s, %d Hz, 1 ch, 16-bit", G_STRFUNC, EXTENSION_RAW, GST_TYPE_PCM, PCM_RATE);
249         *format = FORMAT_PCM;
250         *tmpfile = g_strdup(filename);
251     }
252     else if (g_strrstr(filename, EXTENSION_MP3))
253     {
254         ULOG_INFO("%s() - file was %s", G_STRFUNC, EXTENSION_MP3);
255         *format = FORMAT_MP3;
256         *tmpfile = g_strdup(filename);
257     }
258     else if (g_strrstr(filename, EXTENSION_WAV))
259     {
260         ULOG_INFO("%s() - file was %s", G_STRFUNC, EXTENSION_WAV);
261         *format = FORMAT_WAV;
262         *tmpfile = g_strdup(filename);
263     }
264     else if (g_strrstr(filename, EXTENSION_ILBC))
265     {
266         ULOG_INFO("%s() - file was %s", G_STRFUNC, EXTENSION_ILBC);
267         *format = FORMAT_ILBC;
268         *tmpfile = g_strdup(filename);
269     }
270     else if (g_strrstr(filename, EXTENSION_AU) || g_strrstr(filename, EXTENSION_SND))
271     {
272         gchar *text_uri;
273         GnomeVFSHandle *from_handle, *tmp_handle;
274         GnomeVFSResult res;
275         guint32 fmt, rate, channels, data_size, data_offset;
276         GnomeVFSURI *uri;
277
278         gint ret = 0;
279
280         ULOG_INFO("%s() - file was AU/SND", G_STRFUNC);
281         /* decode and extract raw AU data */
282         text_uri = g_strdup_printf("file://%s", filename);
283         if (gnome_vfs_open(&from_handle, text_uri, GNOME_VFS_OPEN_READ) != GNOME_VFS_OK)
284         {
285             ULOG_WARN("%s() -  gnome_vfs_open() failed", G_STRFUNC);
286             g_free(text_uri);
287             return FALSE;
288         }
289         g_free(text_uri);
290         text_uri = NULL;
291         if ((ret = au_get_info(from_handle, &fmt, &rate, &channels, &data_size, &data_offset)) <= 0)
292         {
293             ULOG_WARN("%s() - au_get_info() failed", G_STRFUNC);
294             gnome_vfs_close(from_handle);
295             return FALSE;
296         }
297
298         ULOG_DEBUG("%s() - format: %u, rate: %u, channels: %u, data_size: %u, data_offset: %u", G_STRFUNC, fmt, rate, channels, data_size, data_offset);
299         if (rate != DEFAULT_RATE || channels != DEFAULT_CHANNELS || data_size == 0)
300         {
301             ULOG_WARN("%s() - unsupported format", G_STRFUNC);
302             gnome_vfs_close(from_handle);
303             return FALSE;
304         }
305
306         switch (fmt)
307         {
308             case FORMAT_PCMA:
309                 text_uri = g_strdup_printf("file://%s", DEFAULT_TMP_PCMA_FILE);
310                 break;
311             case FORMAT_PCMU:
312                 text_uri = g_strdup_printf("file://%s", DEFAULT_TMP_PCMU_FILE);
313                 break;
314             case FORMAT_PCM:
315             default: 
316                 text_uri = g_strdup_printf("file://%s", DEFAULT_TMP_FILE);
317                 break;
318         }
319
320         uri = gnome_vfs_uri_new(text_uri);
321         g_free(text_uri);
322         text_uri = NULL;
323
324         /* TODO: check for symlink and other dangerous stuff */
325         res = gnome_vfs_create_uri(&tmp_handle, uri, 
326                     GNOME_VFS_OPEN_WRITE, 
327                     0 /* exclusive */,
328                     S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH  /* perm */);
329         if (res != GNOME_VFS_OK)
330         {
331             ULOG_WARN("%s() - gnome_vfs_create() failed for '%s': %s", G_STRFUNC, gnome_vfs_uri_to_string(uri, GNOME_VFS_URI_HIDE_NONE), gnome_vfs_result_to_string(res));
332             gnome_vfs_uri_unref(uri);
333             return FALSE;
334         }
335
336         ret = au_copy_data(tmp_handle, from_handle, data_offset /* from_offset */);
337         gnome_vfs_close(from_handle);
338         if (ret <= 0)
339         {
340             ULOG_WARN("%s() - couldn't copy file data to %s", G_STRFUNC, gnome_vfs_uri_to_string(uri, GNOME_VFS_URI_HIDE_NONE));
341             gnome_vfs_uri_unref(uri);
342             return FALSE;
343         }
344
345         /* everything ok, tmpfile created */
346
347         *format = fmt;
348         *tmpfile = g_strdup(gnome_vfs_uri_get_path(uri));
349         ULOG_DEBUG("%s() - created tmpfile '%s'", G_STRFUNC, *tmpfile);
350
351         gnome_vfs_uri_unref(uri);
352         uri = NULL;
353     }
354     else
355     {
356         ULOG_WARN("%s() - non-matching file name", G_STRFUNC); 
357         return FALSE;
358     }
359     return TRUE;
360 }
361
362 GnomeVFSFileSize getFileLength(const gchar *file)
363 {
364     GnomeVFSFileInfo *info;
365     GnomeVFSResult res;
366     gchar *text_uri;
367     GnomeVFSFileSize ret = 0;
368
369     if (NULL == file)
370         return ret;
371
372     text_uri = g_strdup_printf("file://%s", file);
373     info = gnome_vfs_file_info_new();
374     res = gnome_vfs_get_file_info(text_uri,
375                 info,
376                 GNOME_VFS_FILE_INFO_DEFAULT);
377     g_free(text_uri);
378
379     if (res == GNOME_VFS_OK && (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE))
380     {
381         ret = info->size;
382     }
383     else 
384     {
385         ULOG_WARN("%s: couldn't get file size", G_STRFUNC);
386         ret = 0;
387     }
388
389     gnome_vfs_file_info_unref(info);
390
391     return ret;
392 }