core: always allow volume setting with single-channel pa_cvolume
[platform/upstream/pulseaudio.git] / src / pulsecore / authkey.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2004-2006 Lennart Poettering
5   Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7   PulseAudio is free software; you can redistribute it and/or modify
8   it under the terms of the GNU Lesser General Public License as
9   published by the Free Software Foundation; either version 2.1 of the
10   License, or (at your option) any later version.
11
12   PulseAudio is distributed in the hope that it will be useful, but
13   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
18   License along with PulseAudio; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20   USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <stdio.h>
32 #include <inttypes.h>
33 #include <stdlib.h>
34 #include <time.h>
35 #include <limits.h>
36 #include <sys/stat.h>
37
38 #include <pulse/util.h>
39 #include <pulse/xmalloc.h>
40 #include <pulsecore/core-error.h>
41 #include <pulsecore/core-util.h>
42 #include <pulsecore/log.h>
43 #include <pulsecore/random.h>
44 #include <pulsecore/macro.h>
45
46 #include "authkey.h"
47
48 /* Generate a new authorization key, store it in file fd and return it in *data  */
49 static int generate(int fd, void *ret_data, size_t length) {
50     ssize_t r;
51
52     pa_assert(fd >= 0);
53     pa_assert(ret_data);
54     pa_assert(length > 0);
55
56     pa_random(ret_data, length);
57
58     lseek(fd, (off_t) 0, SEEK_SET);
59     (void) ftruncate(fd, (off_t) 0);
60
61     if ((r = pa_loop_write(fd, ret_data, length, NULL)) < 0 || (size_t) r != length) {
62         pa_log("Failed to write cookie file: %s", pa_cstrerror(errno));
63         return -1;
64     }
65
66     return 0;
67 }
68
69 #ifndef O_BINARY
70 #define O_BINARY 0
71 #endif
72
73 #ifndef O_NOCTTY
74 #define O_NOCTTY 0
75 #endif
76
77 /* Load an euthorization cookie from file fn and store it in data. If
78  * the cookie file doesn't exist, create it */
79 static int load(const char *fn, void *data, size_t length) {
80     int fd = -1;
81     int writable = 1;
82     int unlock = 0, ret = -1;
83     ssize_t r;
84
85     pa_assert(fn);
86     pa_assert(data);
87     pa_assert(length > 0);
88
89     if ((fd = open(fn, O_RDWR|O_CREAT|O_BINARY|O_NOCTTY, S_IRUSR|S_IWUSR)) < 0) {
90
91         if (errno != EACCES || (fd = open(fn, O_RDONLY|O_BINARY|O_NOCTTY)) < 0) {
92             pa_log_warn("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
93             goto finish;
94         } else
95             writable = 0;
96     }
97
98     unlock = pa_lock_fd(fd, 1) >= 0;
99
100     if ((r = pa_loop_read(fd, data, length, NULL)) < 0) {
101         pa_log("Failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
102         goto finish;
103     }
104
105     if ((size_t) r != length) {
106         pa_log_debug("Got %d bytes from cookie file '%s', expected %d", (int) r, fn, (int) length);
107
108         if (!writable) {
109             pa_log_warn("Unable to write cookie to read-only file");
110             goto finish;
111         }
112
113         if (generate(fd, data, length) < 0)
114             goto finish;
115     }
116
117     ret = 0;
118
119 finish:
120
121     if (fd >= 0) {
122
123         if (unlock)
124             pa_lock_fd(fd, 0);
125
126         if (pa_close(fd) < 0) {
127             pa_log_warn("Failed to close cookie file: %s", pa_cstrerror(errno));
128             ret = -1;
129         }
130     }
131
132     return ret;
133 }
134
135 /* Load a cookie from a cookie file. If the file doesn't exist, create it. */
136 int pa_authkey_load(const char *path, void *data, size_t length) {
137     int ret;
138
139     pa_assert(path);
140     pa_assert(data);
141     pa_assert(length > 0);
142
143     if ((ret = load(path, data, length)) < 0)
144         pa_log_warn("Failed to load authorization key '%s': %s", path, (ret < 0) ? pa_cstrerror(errno) : "File corrupt");
145
146     return ret;
147 }
148
149 /* If the specified file path starts with / return it, otherwise
150  * return path prepended with home directory */
151 static char *normalize_path(const char *fn) {
152
153     pa_assert(fn);
154
155 #ifndef OS_IS_WIN32
156     if (fn[0] != '/') {
157 #else
158     if (strlen(fn) < 3 || !isalpha(fn[0]) || fn[1] != ':' || fn[2] != '\\') {
159 #endif
160         char *homedir, *s;
161
162         if (!(homedir = pa_get_home_dir_malloc()))
163             return NULL;
164
165         s = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", homedir, fn);
166         pa_xfree(homedir);
167
168         return s;
169     }
170
171     return pa_xstrdup(fn);
172 }
173
174 /* Load a cookie from a file in the home directory. If the specified
175  * path starts with /, use it as absolute path instead. */
176 int pa_authkey_load_auto(const char *fn, void *data, size_t length) {
177     char *p;
178     int ret;
179
180     pa_assert(fn);
181     pa_assert(data);
182     pa_assert(length > 0);
183
184     if (!(p = normalize_path(fn)))
185         return -2;
186
187     ret = pa_authkey_load(p, data, length);
188     pa_xfree(p);
189
190     return ret;
191 }
192
193 /* Store the specified cookie in the specified cookie file */
194 int pa_authkey_save(const char *fn, const void *data, size_t length) {
195     int fd = -1;
196     int unlock = 0, ret = -1;
197     ssize_t r;
198     char *p;
199
200     pa_assert(fn);
201     pa_assert(data);
202     pa_assert(length > 0);
203
204     if (!(p = normalize_path(fn)))
205         return -2;
206
207     if ((fd = open(p, O_RDWR|O_CREAT|O_NOCTTY, S_IRUSR|S_IWUSR)) < 0) {
208         pa_log_warn("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
209         goto finish;
210     }
211
212     unlock = pa_lock_fd(fd, 1) >= 0;
213
214     if ((r = pa_loop_write(fd, data, length, NULL)) < 0 || (size_t) r != length) {
215         pa_log("Failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
216         goto finish;
217     }
218
219     ret = 0;
220
221 finish:
222
223     if (fd >= 0) {
224
225         if (unlock)
226             pa_lock_fd(fd, 0);
227
228         if (pa_close(fd) < 0) {
229             pa_log_warn("Failed to close cookie file: %s", pa_cstrerror(errno));
230             ret = -1;
231         }
232     }
233
234     pa_xfree(p);
235
236     return ret;
237 }