Fix memory leak in dsh.
[doldaconnect.git] / clients / gui-shell / dsh.c
CommitLineData
34d45a15
FT
1/*
2 * Dolda Connect - Modular multiuser Direct Connect-style client
3 * Copyright (C) 2007 Fredrik Tolf <fredrik@dolda2000.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18*/
19
20#include <stdlib.h>
21#include <stdio.h>
22#include <unistd.h>
23#include <string.h>
24#include <errno.h>
25#include <pwd.h>
26#include <locale.h>
27#include <libintl.h>
28#include <signal.h>
29#include <sys/wait.h>
30#include <gtk/gtk.h>
31#include <gdk/gdkkeysyms.h>
32#include <stdarg.h>
33#include <doldaconnect/uilib.h>
34#include <doldaconnect/uimisc.h>
35#include <doldaconnect/utils.h>
36
37#ifdef HAVE_CONFIG_H
38#include <config.h>
39#endif
40
41#ifdef HAVE_NOTIFY
42#include <libnotify/notify.h>
43#endif
44
45#define _(text) gettext(text)
46
47struct trinfo {
48 int ostate;
fa9a8ac1
FT
49 int opos;
50 time_t lastprog;
34d45a15
FT
51};
52
53void updatewrite(void);
54
71aeadfc 55int remote = 0;
34d45a15
FT
56GtkStatusIcon *tray;
57pid_t dpid = 0, dcpid = 0;
58int connected = 0;
59int dstat;
60int derrfd, derrtag;
61char *derrbuf = NULL;
62size_t derrbufsize = 0, derrbufdata = 0;
63int dcfd, dcfdrtag, dcfdwtag = -1;
64GdkPixbuf *dcicon;
65#ifdef HAVE_NOTIFY
66NotifyNotification *trnote = NULL;
67#endif
68
69#include "dsh-start.gtkh"
70#include "dsh-menu.gtkh"
71
72int running(char *pf)
73{
74 FILE *pfs;
75 char buf[1024];
76 int pid;
77
78 if((pfs = fopen(pf, "r")) == NULL) {
79 perror(pf);
80 return(0);
81 }
82 fgets(buf, sizeof(buf), pfs);
83 fclose(pfs);
84 if((pid = atoi(buf)) == 0)
85 return(0);
86 return(!kill(pid, 0));
87}
88
89void derrcb(gpointer data, gint source, GdkInputCondition cond)
90{
91 int ret = 0;
92
93 sizebuf2(derrbuf, derrbufdata + 1024, 1);
94 ret = read(derrfd, derrbuf + derrbufdata, derrbufsize - derrbufdata);
95 if(ret <= 0) {
96 if(ret < 0)
97 bprintf(derrbuf, "\ncould not read from daemon: %s\n", strerror(errno));
98 gdk_input_remove(derrtag);
99 close(derrfd);
100 derrfd = -1;
101 } else {
102 derrbufdata += ret;
103 }
104}
105
106int msgbox(int type, int buttons, char *format, ...)
107{
108 GtkWidget *swnd;
109 va_list args;
110 char *buf;
111 int resp;
112
113 va_start(args, format);
114 buf = vsprintf2(format, args);
115 va_end(args);
116 swnd = gtk_message_dialog_new(NULL, 0, type, buttons, "%s", buf);
117 resp = gtk_dialog_run(GTK_DIALOG(swnd));
118 gtk_widget_destroy(swnd);
119 free(buf);
120 return(resp);
121}
122
123void destroytr(struct dc_transfer *tr)
124{
125 struct trinfo *tri;
126
127 tri = tr->udata;
128 free(tri);
129}
130
131void inittr(struct dc_transfer *tr)
132{
133 struct trinfo *tri;
134
135 tr->udata = tri = memset(smalloc(sizeof(*tri)), 0, sizeof(*tri));
136 tr->destroycb = destroytr;
137 tri->ostate = tr->state;
fa9a8ac1
FT
138 tri->opos = tr->curpos;
139 tri->lastprog = time(NULL);
34d45a15
FT
140}
141
142#ifdef HAVE_NOTIFY
143void notify(NotifyNotification **n, char *cat, char *title, char *body, ...)
144{
145 va_list args;
146 char *bbuf;
147
148 va_start(args, body);
149 bbuf = vsprintf2(body, args);
150 va_end(args);
151 if(*n == NULL) {
152 *n = notify_notification_new_with_status_icon(title, bbuf, NULL, tray);
153 notify_notification_set_icon_from_pixbuf(*n, dcicon);
154 } else {
155 notify_notification_update(*n, title, bbuf, NULL);
156 }
157 notify_notification_show(*n, NULL);
158}
159#endif
160
fa9a8ac1
FT
161/* XXX: Achtung! Too DC-specific! */
162wchar_t *getfilename(wchar_t *path)
163{
164 wchar_t *p;
165
166 if((p = wcsrchr(path, L'\\')) == NULL)
167 return(path);
168 else
169 return(p + 1);
170}
171
34d45a15
FT
172void trstatechange(struct dc_transfer *tr, int ostate)
173{
fa9a8ac1 174 if((ostate == DC_TRNS_MAIN) && (tr->dir == DC_TRNSD_DOWN)) {
34d45a15
FT
175 if(tr->state == DC_TRNS_DONE) {
176#ifdef HAVE_NOTIFY
43edea91
FT
177 if(dcpid == 0)
178 notify(&trnote, "transfer.complete", _("Transfer complete"), _("Finished downloading %ls from %ls"), getfilename(tr->path), tr->peernick);
34d45a15
FT
179#endif
180 } else {
181#ifdef HAVE_NOTIFY
43edea91
FT
182 if(dcpid == 0)
183 notify(&trnote, "transfer.error", _("Transfer interrupted"), _("The transfer of %ls from %ls was interrupted from the other side"), getfilename(tr->path), tr->peernick);
34d45a15
FT
184#endif
185 }
186 }
187}
188
189void updatetrinfo(void)
190{
191 struct dc_transfer *tr;
192 struct trinfo *tri;
fa9a8ac1 193 time_t now;
34d45a15 194
fa9a8ac1 195 now = time(NULL);
34d45a15
FT
196 for(tr = dc_transfers; tr != NULL; tr = tr->next) {
197 if(tr->udata == NULL) {
198 inittr(tr);
199 } else {
200 tri = tr->udata;
201 if(tri->ostate != tr->state) {
202 trstatechange(tr, tri->ostate);
203 tri->ostate = tr->state;
204 }
fa9a8ac1
FT
205 if(tri->opos != tr->curpos) {
206 tri->opos = tr->curpos;
207 tri->lastprog = now;
208 }
209#ifdef NOTIFY
210 if((tr->state = DC_TRNS_MAIN) && (now - tri->lastprog > 600)) {
43edea91
FT
211 if(dcpid == 0)
212 notify(&trnote, "transfer.error", _("Transfer stalled"), _("The transfer of %ls from %ls has not made progress for 10 minutes"), getfilename(tr->path), tr->peernick);
fa9a8ac1
FT
213 }
214#endif
34d45a15
FT
215 }
216 }
217}
218
219void trlscb(int resp, void *data)
220{
221 updatetrinfo();
222}
223
224void logincb(int err, wchar_t *reason, void *data)
225{
226 if(err != DC_LOGIN_ERR_SUCCESS) {
227 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server"));
228 exit(1);
229 }
230 gtk_status_icon_set_tooltip(tray, "Dolda Connect");
231 dc_queuecmd(NULL, NULL, L"notify", L"trans:act", L"on", L"trans:prog", L"on", NULL);
232 dc_gettrlistasync(trlscb, NULL);
233 connected = 1;
234 updatewrite();
235}
236
237void dcfdcb(gpointer data, gint source, GdkInputCondition cond)
238{
239 struct dc_response *resp;
240
241 if(((cond & GDK_INPUT_READ) && dc_handleread()) || ((cond & GDK_INPUT_WRITE) && dc_handlewrite())) {
242 if(errno == 0) {
243 gtk_main_quit();
244 } else {
245 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server: %s"), strerror(errno));
246 exit(1);
247 }
248 return;
249 }
250 while((resp = dc_getresp()) != NULL) {
251 if(!wcscmp(resp->cmdname, L".connect")) {
252 if(resp->code != 201) {
253 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("The server refused the connection"));
254 exit(1);
255 } else if(dc_checkprotocol(resp, DC_LATEST)) {
256 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Server protocol revision mismatch"));
257 exit(1);
258 } else {
259 gtk_status_icon_set_tooltip(tray, _("Authenticating..."));
260 dc_loginasync(NULL, 1, dc_convnone, logincb, NULL);
261 }
262 } else if(!wcscmp(resp->cmdname, L".notify")) {
263 dc_uimisc_handlenotify(resp);
264 updatetrinfo();
265 }
d20e3861 266 dc_freeresp(resp);
34d45a15
FT
267 }
268 updatewrite();
269}
270
271void updatewrite(void)
272{
273 if(dcfd < 0)
274 return;
275 if(dc_wantwrite()) {
276 if(dcfdwtag == -1)
277 dcfdwtag = gdk_input_add(dcfd, GDK_INPUT_WRITE, dcfdcb, NULL);
278 } else {
279 if(dcfdwtag != -1) {
280 gdk_input_remove(dcfdwtag);
281 dcfdwtag = -1;
282 }
283 }
284}
285
286void connectdc(void)
287{
71aeadfc 288 if((dcfd = dc_connect(remote?NULL:dc_srv_local)) < 0) {
34d45a15
FT
289 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server: %s"), strerror(errno));
290 exit(1);
291 }
292 dcfdrtag = gdk_input_add(dcfd, GDK_INPUT_READ, dcfdcb, NULL);
293 updatewrite();
294 gtk_status_icon_set_tooltip(tray, _("Connecting to server..."));
295}
296
297void startdaemon(void)
298{
299 char pf[1024];
300 int pfd[2], i;
301
302 if(getenv("HOME") != NULL)
303 snprintf(pf, sizeof(pf), "%s/.doldacond.pid", getenv("HOME"));
304 else
305 snprintf(pf, sizeof(pf), "%s/.doldacond.pid", getpwuid(getuid())->pw_dir);
306 if(access(pf, F_OK) || !running(pf)) {
307 pipe(pfd);
308 if((dpid = fork()) == 0) {
309 dup2(pfd[1], 2);
310 for(i = 3; i < FD_SETSIZE; i++)
311 close(i);
312 execlp("doldacond", "doldacond", "-p", pf, NULL);
313 perror("doldacond");
314 exit(127);
315 }
316 if(dpid == -1)
317 abort();
318 close(pfd[1]);
319 derrfd = pfd[0];
320 derrtag = gdk_input_add(derrfd, GDK_INPUT_READ, derrcb, NULL);
321 create_start_wnd();
322 gtk_widget_show(start_wnd);
323 gtk_status_icon_set_tooltip(tray, _("Starting..."));
324 } else {
325 connectdc();
326 }
327}
328
329gboolean daemonized(gpointer uu)
330{
331 gtk_widget_hide(start_wnd);
332 dpid = 0;
333 if(derrfd != -1) {
334 gdk_input_remove(derrtag);
335 close(derrfd);
336 }
337 if(dstat != 0) {
338 gtk_status_icon_set_visible(tray, FALSE);
339 gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(start_log)), derrbuf, derrbufdata);
340 gtk_widget_show(start_errwnd);
341 } else {
342 connectdc();
343 }
344 return(FALSE);
345}
346
347void sighandler(int sig)
348{
349 pid_t p;
350 int status;
351
352 if(sig == SIGCHLD) {
353 while((p = waitpid(-1, &status, WNOHANG)) > 0) {
354 if(p == dpid) {
355 dstat = status;
356 gtk_timeout_add(1, daemonized, NULL);
357 } else if(p == dcpid) {
358 dcpid = 0;
359 }
360 }
361 }
362}
363
364void dolcon(void)
365{
366 int i;
367
368 if((dcpid = fork()) == 0) {
369 for(i = 3; i < FD_SETSIZE; i++)
370 close(i);
71aeadfc
FT
371 if(remote)
372 execlp("dolcon", "dolcon", NULL);
373 else
374 execlp("dolcon", "dolcon", "-l", NULL);
34d45a15
FT
375 perror("dolcon");
376 exit(127);
377 }
378}
379
31a01fdd
FT
380void cb_shm_dolconf_activate(GtkWidget *uu1, gpointer uu2)
381{
382 int i;
383
384 if((dcpid = fork()) == 0) {
385 for(i = 3; i < FD_SETSIZE; i++)
386 close(i);
387 execlp("dolconf", "dolconf", NULL);
388 perror("dolconf");
389 exit(127);
390 }
391}
392
34d45a15
FT
393void cb_shm_dolcon_activate(GtkWidget *uu1, gpointer uu2)
394{
395 dolcon();
396}
397
398void cb_shm_quit_activate(GtkWidget *uu1, gpointer uu2)
399{
400 dc_queuecmd(NULL, NULL, L"shutdown", NULL);
401 updatewrite();
402}
403
404void tray_activate(GtkStatusIcon *uu1, gpointer uu2)
405{
406 if(dpid != 0) {
407 gtk_widget_show(start_wnd);
408 } else if(connected) {
409 dolcon();
410 }
411}
412
413void tray_popup(GtkStatusIcon *uu1, guint button, guint time, gpointer uu2)
414{
415 gtk_menu_popup(GTK_MENU(shm_menu), NULL, NULL, NULL, NULL, button, time);
416}
417
418void cb_start_hide_clicked(GtkWidget *uu1, gpointer uu2)
419{
420 gtk_widget_hide(start_wnd);
421}
422
423void cb_start_abort_clicked(GtkWidget *uu1, gpointer uu2)
424{
425 kill(dpid, SIGINT);
426 exit(0);
427}
428
429void cb_start_errok_clicked(GtkWidget *uu1, gpointer uu2)
430{
431 gtk_main_quit();
432}
433
434#include "../dolda-icon.xpm"
435
436void inittray(void)
437{
438 tray = gtk_status_icon_new_from_pixbuf(gdk_pixbuf_scale_simple(dcicon, 24, 24, GDK_INTERP_BILINEAR));
439 gtk_status_icon_set_tooltip(tray, "");
440 g_signal_connect(G_OBJECT(tray), "activate", G_CALLBACK(tray_activate), (gpointer)NULL);
441 g_signal_connect(G_OBJECT(tray), "popup-menu", G_CALLBACK(tray_popup), (gpointer)NULL);
442}
443
444int main(int argc, char **argv)
445{
71aeadfc
FT
446 int c;
447
34d45a15
FT
448 setlocale(LC_ALL, "");
449 bindtextdomain(PACKAGE, LOCALEDIR);
450 textdomain(PACKAGE);
451 signal(SIGCHLD, sighandler);
452 dc_init();
453 gtk_init(&argc, &argv);
454#ifdef HAVE_NOTIFY
455 notify_init("Dolda Connect");
456#endif
71aeadfc
FT
457 while((c = getopt(argc, argv, "rh")) != -1) {
458 switch(c) {
459 case 'r':
460 remote = 1;
461 break;
462 case 'h':
463 printf("usage: doldacond-shell [-hr]\n");
464 printf("\t-h\tDisplay this help message\n");
465 printf("\t-r\tConnect to a remote host\n");
466 exit(0);
467 default:
468 fprintf(stderr, "usage: doldacond-shell [-hr]\n");
469 exit(1);
470 }
471 }
34d45a15
FT
472
473 create_shm_wnd();
474 dcicon = gdk_pixbuf_new_from_xpm_data((const char **)dolda_icon_xpm);
475 gtk_window_set_default_icon(dcicon);
476 inittray();
71aeadfc
FT
477 if(remote)
478 connectdc();
479 else
480 startdaemon();
34d45a15
FT
481
482 gtk_main();
483
484 return(0);
485}
486
487#include "dsh-start.gtk"
488#include "dsh-menu.gtk"