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