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