]> git.0d.be Git - empathy.git/blob - src/bacon-message-connection.c
Add en_GB in gitignore
[empathy.git] / src / bacon-message-connection.c
1 /*
2  * Copyright (C) 2003 Bastien Nocera <hadess@hadess.net>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  *
18  * The Totem project hereby grant permission for non-gpl compatible GStreamer
19  * plugins to be used and distributed together with GStreamer and Totem. This
20  * permission are above and beyond the permissions granted by the GPL license
21  * Totem is covered by.
22  *
23  * Monday 7th February 2005: Christian Schaller: Add excemption clause.
24  * See license_change file for details.
25  *
26  */
27
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/un.h>
37 #include <errno.h>
38
39 #include "bacon-message-connection.h"
40
41 #ifndef UNIX_PATH_MAX
42 #define UNIX_PATH_MAX 108
43 #endif
44
45 struct BaconMessageConnection {
46         /* A server accepts connections */
47         gboolean is_server;
48
49         /* The socket path itself */
50         char *path;
51
52         /* File descriptor of the socket */
53         int fd;
54         /* Channel to watch */
55         GIOChannel *chan;
56         /* Event id returned by g_io_add_watch() */
57         int conn_id;
58
59         /* Connections accepted by this connection */
60         GSList *accepted_connections;
61
62         /* callback */
63         void (*func) (const char *message, gpointer user_data);
64         gpointer data;
65 };
66
67 static gboolean
68 test_is_socket (const char *path)
69 {
70         struct stat s;
71
72         if (stat (path, &s) == -1)
73                 return FALSE;
74
75         if (S_ISSOCK (s.st_mode))
76                 return TRUE;
77
78         return FALSE;
79 }
80
81 static gboolean
82 is_owned_by_user_and_socket (const char *path)
83 {
84         struct stat s;
85
86         if (stat (path, &s) == -1)
87                 return FALSE;
88
89         if (s.st_uid != geteuid ())
90                 return FALSE;
91
92         if ((s.st_mode & S_IFSOCK) != S_IFSOCK)
93                 return FALSE;
94         
95         return TRUE;
96 }
97
98 static gboolean server_cb (GIOChannel *source,
99                            GIOCondition condition, gpointer data);
100
101 static gboolean
102 setup_connection (BaconMessageConnection *conn)
103 {
104         int fdflags;
105
106         g_return_val_if_fail (conn->chan == NULL, FALSE);
107
108         /* Add CLOEXEC flag on the fd to make sure the socket get closed
109          * if exec is called. */
110         fdflags = fcntl (conn->fd, F_GETFD, 0);
111         if (fdflags >= 0) {
112                 fdflags |= FD_CLOEXEC;
113                 fcntl (conn->fd, F_SETFD, fdflags);
114         }
115
116         conn->chan = g_io_channel_unix_new (conn->fd);
117         if (!conn->chan) {
118                 return FALSE;
119         }
120         g_io_channel_set_line_term (conn->chan, "\n", 1);
121         conn->conn_id = g_io_add_watch (conn->chan, G_IO_IN, server_cb, conn);
122
123         return TRUE;
124 }
125
126 static void
127 accept_new_connection (BaconMessageConnection *server_conn)
128 {
129         BaconMessageConnection *conn;
130         int alen;
131
132         g_return_if_fail (server_conn->is_server);
133
134         conn = g_new0 (BaconMessageConnection, 1);
135         conn->is_server = FALSE;
136         conn->func = server_conn->func;
137         conn->data = server_conn->data;
138
139         conn->fd = accept (server_conn->fd, NULL, (guint *)&alen);
140
141         server_conn->accepted_connections =
142                 g_slist_prepend (server_conn->accepted_connections, conn);
143
144         setup_connection (conn);
145 }
146
147 static gboolean
148 server_cb (GIOChannel *source, GIOCondition condition, gpointer data)
149 {
150         BaconMessageConnection *conn = (BaconMessageConnection *)data;
151         char *message, *subs, buf;
152         int cd, rc, offset;
153         gboolean finished;
154
155         offset = 0;
156         if (conn->is_server && conn->fd == g_io_channel_unix_get_fd (source)) {
157                 accept_new_connection (conn);
158                 return TRUE;
159         }
160         message = g_malloc (1);
161         cd = conn->fd;
162         rc = read (cd, &buf, 1);
163         while (rc > 0 && buf != '\n')
164         {
165                 message = g_realloc (message, rc + offset + 1);
166                 message[offset] = buf;
167                 offset = offset + rc;
168                 rc = read (cd, &buf, 1);
169         }
170         if (rc <= 0) {
171                 g_io_channel_shutdown (conn->chan, FALSE, NULL);
172                 g_io_channel_unref (conn->chan);
173                 conn->chan = NULL;
174                 close (conn->fd);
175                 conn->fd = -1;
176                 g_free (message);
177                 conn->conn_id = 0;
178
179                 return FALSE;
180         }
181         message[offset] = '\0';
182
183         subs = message;
184         finished = FALSE;
185
186         while (finished == FALSE && *subs != '\0')
187         {
188                 if (conn->func != NULL)
189                         (*conn->func) (subs, conn->data);
190
191                 subs += strlen (subs) + 1;
192                 if (subs - message >= offset)
193                         finished = TRUE;
194         }
195
196         g_free (message);
197
198         return TRUE;
199 }
200
201 static char *
202 find_file_with_pattern (const char *dir, const char *pattern)
203 {
204         GDir *filedir;
205         char *found_filename;
206         const char *filename;
207         GPatternSpec *pat;
208
209         filedir = g_dir_open (dir, 0, NULL);
210         if (filedir == NULL)
211                 return NULL;
212
213         pat = g_pattern_spec_new (pattern);
214         if (pat == NULL)
215         {
216                 g_dir_close (filedir);
217                 return NULL;
218         }
219
220         found_filename = NULL;
221
222         while ((filename = g_dir_read_name (filedir)))
223         {
224                 if (g_pattern_match_string (pat, filename))
225                 {
226                         char *tmp = g_build_filename (dir, filename, NULL);
227                         if (is_owned_by_user_and_socket (tmp))
228                                 found_filename = g_strdup (filename);
229                         g_free (tmp);
230                 }
231
232                 if (found_filename != NULL)
233                         break;
234         }
235
236         g_pattern_spec_free (pat);
237         g_dir_close (filedir);
238
239         return found_filename;
240 }
241
242 static char *
243 socket_filename (const char *prefix)
244 {
245         char *pattern, *newfile, *path, *filename;
246         const char *tmpdir;
247
248         pattern = g_strdup_printf ("%s.%s.*", prefix, g_get_user_name ());
249         tmpdir = g_get_tmp_dir ();
250         filename = find_file_with_pattern (tmpdir, pattern);
251         if (filename == NULL)
252         {
253                 newfile = g_strdup_printf ("%s.%s.%u", prefix,
254                                 g_get_user_name (), g_random_int ());
255                 path = g_build_filename (tmpdir, newfile, NULL);
256                 g_free (newfile);
257         } else {
258                 path = g_build_filename (tmpdir, filename, NULL);
259                 g_free (filename);
260         }
261
262         g_free (pattern);
263         return path;
264 }
265
266 static gboolean
267 try_server (BaconMessageConnection *conn)
268 {
269         struct sockaddr_un uaddr;
270
271         uaddr.sun_family = AF_UNIX;
272         strncpy (uaddr.sun_path, conn->path,
273                         MIN (strlen(conn->path)+1, UNIX_PATH_MAX));
274         conn->fd = socket (PF_UNIX, SOCK_STREAM, 0);
275         if (bind (conn->fd, (struct sockaddr *) &uaddr, sizeof (uaddr)) == -1)
276         {
277                 conn->fd = -1;
278                 return FALSE;
279         }
280         listen (conn->fd, 5);
281
282         return setup_connection (conn);
283 }
284
285 static gboolean
286 try_client (BaconMessageConnection *conn)
287 {
288         struct sockaddr_un uaddr;
289
290         uaddr.sun_family = AF_UNIX;
291         strncpy (uaddr.sun_path, conn->path,
292                         MIN(strlen(conn->path)+1, UNIX_PATH_MAX));
293         conn->fd = socket (PF_UNIX, SOCK_STREAM, 0);
294         if (connect (conn->fd, (struct sockaddr *) &uaddr,
295                                 sizeof (uaddr)) == -1)
296         {
297                 conn->fd = -1;
298                 return FALSE;
299         }
300
301         return setup_connection (conn);
302 }
303
304 BaconMessageConnection *
305 bacon_message_connection_new (const char *prefix)
306 {
307         BaconMessageConnection *conn;
308
309         g_return_val_if_fail (prefix != NULL, NULL);
310
311         conn = g_new0 (BaconMessageConnection, 1);
312         conn->path = socket_filename (prefix);
313
314         if (test_is_socket (conn->path) == FALSE)
315         {
316                 if (!try_server (conn))
317                 {
318                         bacon_message_connection_free (conn);
319                         return NULL;
320                 }
321
322                 conn->is_server = TRUE;
323                 return conn;
324         }
325
326         if (try_client (conn) == FALSE)
327         {
328                 unlink (conn->path);
329                 try_server (conn);
330                 if (conn->fd == -1)
331                 {
332                         bacon_message_connection_free (conn);
333                         return NULL;
334                 }
335
336                 conn->is_server = TRUE;
337                 return conn;
338         }
339
340         conn->is_server = FALSE;
341         return conn;
342 }
343
344 void
345 bacon_message_connection_free (BaconMessageConnection *conn)
346 {
347         GSList *child_conn;
348
349         g_return_if_fail (conn != NULL);
350         /* Only servers can accept other connections */
351         g_return_if_fail (conn->is_server != FALSE ||
352                           conn->accepted_connections == NULL);
353
354         child_conn = conn->accepted_connections;
355         while (child_conn != NULL) {
356                 bacon_message_connection_free (child_conn->data);
357                 child_conn = g_slist_next (child_conn);
358         }
359         g_slist_free (conn->accepted_connections);
360
361         if (conn->conn_id) {
362                 g_source_remove (conn->conn_id);
363                 conn->conn_id = 0;
364         }
365         if (conn->chan) {
366                 g_io_channel_shutdown (conn->chan, FALSE, NULL);
367                 g_io_channel_unref (conn->chan);
368         }
369
370         if (conn->is_server != FALSE) {
371                 unlink (conn->path);
372         }
373         if (conn->fd != -1) {
374                 close (conn->fd);
375         }
376
377         g_free (conn->path);
378         g_free (conn);
379 }
380
381 void
382 bacon_message_connection_set_callback (BaconMessageConnection *conn,
383                                        BaconMessageReceivedFunc func,
384                                        gpointer user_data)
385 {
386         g_return_if_fail (conn != NULL);
387
388         conn->func = func;
389         conn->data = user_data;
390 }
391
392 void
393 bacon_message_connection_send (BaconMessageConnection *conn,
394                                const char *message)
395 {
396         g_return_if_fail (conn != NULL);
397         g_return_if_fail (message != NULL);
398
399         g_io_channel_write_chars (conn->chan, message, strlen (message),
400                                   NULL, NULL);
401         g_io_channel_write_chars (conn->chan, "\n", 1, NULL, NULL);
402         g_io_channel_flush (conn->chan, NULL);
403 }
404
405 gboolean
406 bacon_message_connection_get_is_server (BaconMessageConnection *conn)
407 {
408         g_return_val_if_fail (conn != NULL, FALSE);
409
410         return conn->is_server;
411 }
412