respect https prefix
[k8lowj.git] / src / remote.c
blob0586f8d0f7dad868a607b42419338f5ed5c7a8b2
1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
3 */
5 /* remote code written after reading xmms remote code.
6 * Copyright (C) 1998-2002 Peter Alm, Mikael Alm, Olle Hallnas,
7 * Thomas Nilsson and 4Front Technologies
8 * Copyright (C) 1999-2002 Haavard Kvaalen
9 */
10 #include "gtk-all.h"
12 #include <errno.h>
13 #include <unistd.h>
15 #include <sys/socket.h>
16 #include <sys/types.h>
17 #include <sys/un.h>
19 #include "conf.h"
20 #include "marshalers.h"
21 #include "remote.h"
24 /*** remote protocol structure ***/
25 #define REMOTE_PROTOCOL_ACK 'A'
26 #define REMOTE_PROTOCOL_PRESENT 'P'
27 #define REMOTE_PROTOCOL_CHANGE_USER 'U'
30 typedef struct {
31 char username[50];
32 gboolean login;
33 } RemoteChangeLJUser;
36 /*** LogJamRemote object ***/
37 struct _LogJamRemote {
38 GObject parent;
39 int fd;
43 struct _LogJamRemoteClass {
44 GObjectClass parent_class;
45 void (*present) (LogJamRemote *remote);
46 gboolean (*change_user) (LogJamRemote *remote, char *username, gboolean skip, GError **err);
50 enum {
51 PRESENT,
52 CHANGE_USER,
53 LAST_SIGNAL
56 static guint signals[LAST_SIGNAL] = { 0 };
58 static void logjam_remote_finalize(GObject *object) {
59 GObjectClass *parent_class;
60 logjam_remote_stop_listening((LogJamRemote *) object, NULL);
61 parent_class = g_type_class_peek_parent(G_OBJECT_GET_CLASS(object));
62 parent_class->finalize(object);
66 static void logjam_remote_class_init (gpointer klass, gpointer class_data) {
67 GObjectClass *gclass = G_OBJECT_CLASS(klass);
68 gclass->finalize = logjam_remote_finalize;
69 signals[PRESENT] = g_signal_new("present",
70 G_OBJECT_CLASS_TYPE(gclass),
71 G_SIGNAL_RUN_LAST,
72 G_STRUCT_OFFSET(LogJamRemoteClass, present), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
73 signals[CHANGE_USER] = g_signal_new("change_user",
74 G_OBJECT_CLASS_TYPE(gclass),
75 G_SIGNAL_RUN_LAST,
76 G_STRUCT_OFFSET(LogJamRemoteClass, change_user),
77 NULL, NULL, logjam_marshal_BOOLEAN__STRING_POINTER, G_TYPE_BOOLEAN, 2, G_TYPE_STRING, G_TYPE_POINTER);
81 static void logjam_remote_init (GTypeInstance *inst, gpointer g_class) {
82 LogJamRemote *remote = (LogJamRemote *)inst;
83 remote->fd = -1;
87 GType logjam_remote_get_type (void) {
88 static GType remote_type = 0;
89 if (!remote_type) {
90 static const GTypeInfo remote_info = {
91 sizeof(LogJamRemoteClass),
92 NULL,
93 NULL,
94 logjam_remote_class_init,
95 NULL,
96 NULL,
97 sizeof(LogJamRemote),
99 logjam_remote_init
101 remote_type = g_type_register_static(G_TYPE_OBJECT, "LogJamRemote", &remote_info, 0);
103 return remote_type;
107 LogJamRemote *logjam_remote_new (void) {
108 return g_object_new(logjam_remote_get_type(), NULL);
112 GQuark remote_error_quark (void) {
113 static GQuark quark = 0;
114 if (quark == 0) quark = g_quark_from_static_string("remote-error-quark");
115 return quark;
119 static void make_remote_path (char *buf, int len) {
120 g_snprintf(buf, len, "%s/remote", app.conf_dir);
124 static int remote_connect (GError **err) {
125 int fd;
126 struct sockaddr_un saddr;
128 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
129 g_set_error(err, REMOTE_ERROR, REMOTE_ERROR_SYSTEM, "socket: %s", g_strerror(errno));
130 return -1;
133 saddr.sun_family = AF_UNIX;
134 make_remote_path(saddr.sun_path, 100);
135 if (connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
136 close(fd);
137 if (errno != ENOENT && errno != ECONNREFUSED) {
138 g_set_error(err, REMOTE_ERROR, REMOTE_ERROR_SYSTEM, "connect: %s", g_strerror(errno));
140 /* ENOENT is not a real error; there was just nobody there. */
141 return -1;
144 return fd;
148 static gboolean remote_send (char command, gpointer data, int datalen, GError **err) {
149 int fd, len;
150 char response;
152 if ((fd = remote_connect(err)) < 0) return FALSE;
154 len = write(fd, &command, 1);
155 if (len < 0) {
156 g_set_error(err, REMOTE_ERROR, REMOTE_ERROR_SYSTEM, "write: %s", g_strerror(errno));
157 return FALSE;
159 if (datalen > 0) {
160 len = write(fd, data, datalen);
161 if (len < 0) {
162 g_set_error(err, REMOTE_ERROR, REMOTE_ERROR_SYSTEM, "write: %s", g_strerror(errno));
163 return FALSE;
167 len = read(fd, &response, 1);
168 if (len < 0) {
169 g_set_error(err, REMOTE_ERROR, REMOTE_ERROR_SYSTEM, "read: %s", g_strerror(errno));
170 return FALSE;
172 if (len < 1) return FALSE;
174 if (response == REMOTE_PROTOCOL_ACK) return TRUE;
176 return FALSE;
180 gboolean remote_send_user (const char *username, GError **err) {
181 if (username) {
182 RemoteChangeLJUser user;
183 strcpy(user.username, username);
184 user.login = TRUE;
185 return remote_send(REMOTE_PROTOCOL_CHANGE_USER, &user, sizeof(RemoteChangeLJUser), err);
187 return remote_send(REMOTE_PROTOCOL_PRESENT, NULL, 0, err);
191 static gboolean remote_recieve_change_user (LogJamRemote *remote, int fd) {
192 RemoteChangeLJUser user;
193 int readret;
194 gboolean signalret;
195 GError *err = NULL;
197 readret = read(fd, &user, sizeof(RemoteChangeLJUser));
198 if (readret < 0) {
199 /*jam_warning(win, _("Accepting remote command: %s: %s."), "read", g_strerror(errno));*/
200 close(fd);
201 return FALSE;
204 g_signal_emit_by_name(remote, "change_user", user.username, &err, &signalret);
205 return TRUE;
209 static gboolean io_cb (GIOChannel *channel, GIOCondition cond, gpointer data) {
210 LogJamRemote *remote = (LogJamRemote *) data;
211 char command;
212 int fd, ret;
213 struct sockaddr_un saddr;
214 socklen_t addrlen = sizeof(struct sockaddr_un);
216 if (cond == G_IO_IN) {
217 fd = accept(remote->fd, (struct sockaddr *)&saddr, &addrlen);
218 if (fd < 0) {
219 /*jam_warning(win, _("Accepting remote command: %s: %s."), "accept", g_strerror(errno));*/
220 return TRUE;
223 ret = read(fd, &command, 1);
224 if (ret < 0) {
225 /*jam_warning(win, _("Accepting remote command: %s: %s."), "read", g_strerror(errno));*/
226 close(fd);
227 return TRUE;
230 switch (command) {
231 case REMOTE_PROTOCOL_PRESENT:
232 g_signal_emit_by_name(remote, "present");
233 break;
234 case REMOTE_PROTOCOL_CHANGE_USER:
235 if (!remote_recieve_change_user(remote, fd)) return TRUE;
236 break;
237 default:
238 //jam_warning(win, _("Unknown remote command (%c)."), command);
239 close(fd);
240 return TRUE;
243 command = REMOTE_PROTOCOL_ACK;
244 write(fd, &command, 1);
245 close(fd);
248 return TRUE;
252 gboolean logjam_remote_listen (LogJamRemote *remote, GError **err) {
253 struct sockaddr_un saddr;
254 GIOChannel *channel;
256 if (remote->fd > 0) return TRUE;
258 if ((remote->fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
259 g_set_error(err, REMOTE_ERROR, REMOTE_ERROR_SYSTEM, "socket: %s", g_strerror(errno));
260 return FALSE;
263 saddr.sun_family = AF_UNIX;
264 make_remote_path(saddr.sun_path, 100);
265 unlink(saddr.sun_path);
266 conf_verify_dir();
267 if (bind(remote->fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
268 close(remote->fd);
269 remote->fd = -1;
270 g_set_error(err, REMOTE_ERROR, REMOTE_ERROR_SYSTEM, "bind: %s", g_strerror(errno));
271 return FALSE;
274 if (listen(remote->fd, 1) < 0) {
275 close(remote->fd);
276 remote->fd = -1;
277 g_set_error(err, REMOTE_ERROR, REMOTE_ERROR_SYSTEM, "listen: %s", g_strerror(errno));
278 return FALSE;
281 channel = g_io_channel_unix_new(remote->fd);
282 g_io_channel_set_encoding(channel, NULL, NULL);
283 g_io_channel_set_buffered(channel, FALSE);
284 g_io_add_watch(channel, G_IO_IN | G_IO_ERR, io_cb, remote);
285 g_io_channel_unref(channel);
287 return TRUE;
291 gboolean logjam_remote_stop_listening (LogJamRemote *remote, GError **err) {
292 char buf[128];
293 if (remote->fd <= 0) return TRUE;
294 close(remote->fd);
295 remote->fd = -1;
296 make_remote_path(buf, 128);
297 unlink(buf);
298 return TRUE;