2 * Dolda Connect - Modular multiuser Direct Connect-style client
3 * Copyright (C) 2007 Fredrik Tolf <fredrik@dolda2000.com>
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.
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.
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
31 #include <gdk/gdkkeysyms.h>
33 #include <doldaconnect/uilib.h>
34 #include <doldaconnect/uimisc.h>
35 #include <doldaconnect/utils.h>
42 #include <libnotify/notify.h>
45 #define _(text) gettext(text)
49 int opos, spos, speed;
55 void updatewrite(void);
60 pid_t dpid = 0, dcpid = 0;
65 size_t derrbufsize = 0, derrbufdata = 0;
66 int dcfd, dcfdrtag, dcfdwtag = -1;
69 NotifyNotification *trnote = NULL;
72 #include "dsh-start.gtkh"
73 #include "dsh-menu.gtkh"
81 if((pfs = fopen(pf, "r")) == NULL) {
85 fgets(buf, sizeof(buf), pfs);
87 if((pid = atoi(buf)) == 0)
89 return(!kill(pid, 0));
92 void derrcb(gpointer data, gint source, GdkInputCondition cond)
96 sizebuf2(derrbuf, derrbufdata + 1024, 1);
97 ret = read(derrfd, derrbuf + derrbufdata, derrbufsize - derrbufdata);
100 bprintf(derrbuf, "\ncould not read from daemon: %s\n", strerror(errno));
101 gdk_input_remove(derrtag);
109 int msgbox(int type, int buttons, char *format, ...)
116 va_start(args, format);
117 buf = vsprintf2(format, args);
119 swnd = gtk_message_dialog_new(NULL, 0, type, buttons, "%s", buf);
120 resp = gtk_dialog_run(GTK_DIALOG(swnd));
121 gtk_widget_destroy(swnd);
126 void destroytr(struct dc_transfer *tr)
134 void inittr(struct dc_transfer *tr)
138 tr->udata = tri = memset(smalloc(sizeof(*tri)), 0, sizeof(*tri));
139 tr->destroycb = destroytr;
140 tri->ostate = tr->state;
141 tri->spos = tri->opos = tr->curpos;
143 tri->lastprog = time(NULL);
144 tri->sprog = ntime();
148 void notify(NotifyNotification **n, char *cat, char *title, char *body, ...)
153 va_start(args, body);
154 bbuf = vsprintf2(body, args);
157 *n = notify_notification_new_with_status_icon(title, bbuf, NULL, tray);
158 notify_notification_set_icon_from_pixbuf(*n, dcicon);
160 notify_notification_update(*n, title, bbuf, NULL);
162 notify_notification_show(*n, NULL);
166 /* XXX: Achtung! Too DC-specific! */
167 wchar_t *getfilename(wchar_t *path)
171 if((p = wcsrchr(path, L'\\')) == NULL)
177 char *bytes2si(long long bytes)
185 for(i = 0; (b >= 1024) && (i < 4); i++)
197 snprintf(ret, 64, "%.1f %s", b, sd);
201 void updatetooltip(void)
203 struct dc_transfer *tr;
205 int t, i, a, st, bc, bt;
207 size_t bufsize, bufdata;
211 for(tr = dc_transfers; tr != NULL; tr = tr->next) {
212 if(tr->dir != DC_TRNSD_DOWN)
216 if(tr->state == DC_TRNS_WAITING)
218 else if((tr->state == DC_TRNS_HS) || (tr->state == DC_TRNS_MAIN))
220 if((tr->state == DC_TRNS_MAIN)) {
225 if(tri->speed != -1) {
233 bufsize = bufdata = 0;
234 bprintf(buf, "%s: %i", _("Transfers"), t);
236 bprintf(buf, " (%i/%i)", i, a);
238 bprintf(buf, ", %.1f%%", ((double)bc / (double)bt) * 100.0);
240 bprintf(buf, ", %s/s", bytes2si(st));
242 gtk_status_icon_set_tooltip(tray, buf);
246 void trstatechange(struct dc_transfer *tr, int ostate)
251 if((ostate == DC_TRNS_MAIN) && (tr->dir == DC_TRNSD_DOWN)) {
252 if(tr->state == DC_TRNS_DONE) {
255 notify(&trnote, "transfer.complete", _("Transfer complete"), _("Finished downloading %ls from %ls"), getfilename(tr->path), tr->peernick);
260 notify(&trnote, "transfer.error", _("Transfer interrupted"), _("The transfer of %ls from %ls was interrupted from the other side"), getfilename(tr->path), tr->peernick);
264 if(tr->state == DC_TRNS_MAIN) {
266 tri->spos = tr->curpos;
267 tri->sprog = ntime();
271 void updatetrinfo(void)
273 struct dc_transfer *tr;
280 for(tr = dc_transfers; tr != NULL; tr = tr->next) {
281 if(tr->udata == NULL) {
285 if(tri->ostate != tr->state) {
286 trstatechange(tr, tri->ostate);
287 tri->ostate = tr->state;
289 if(tri->opos != tr->curpos) {
290 tri->opos = tr->curpos;
295 if((tr->state = DC_TRNS_MAIN) && (now - tri->lastprog > 600) && !tri->warned) {
297 notify(&trnote, "transfer.error", _("Transfer stalled"), _("The transfer of %ls from %ls has not made progress for 10 minutes"), getfilename(tr->path), tr->peernick);
302 if((tr->state == DC_TRNS_MAIN) && (dnow - tri->sprog > 10)) {
303 tri->speed = ((double)(tr->curpos - tri->spos) / (dnow - tri->sprog));
304 tri->spos = tr->curpos;
312 void trlscb(int resp, void *data)
317 gint trupdatecb(gpointer data)
323 void logincb(int err, wchar_t *reason, void *data)
325 if(err != DC_LOGIN_ERR_SUCCESS) {
326 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server"));
329 dc_queuecmd(NULL, NULL, L"notify", L"trans:act", L"on", L"trans:prog", L"on", NULL);
330 dc_gettrlistasync(trlscb, NULL);
335 void dcfdcb(gpointer data, gint source, GdkInputCondition cond)
337 struct dc_response *resp;
339 if(((cond & GDK_INPUT_READ) && dc_handleread()) || ((cond & GDK_INPUT_WRITE) && dc_handlewrite())) {
343 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server: %s"), strerror(errno));
348 while((resp = dc_getresp()) != NULL) {
349 if(!wcscmp(resp->cmdname, L".connect")) {
350 if(resp->code != 201) {
351 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("The server refused the connection"));
353 } else if(dc_checkprotocol(resp, DC_LATEST)) {
354 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Server protocol revision mismatch"));
357 gtk_status_icon_set_tooltip(tray, _("Authenticating..."));
358 dc_loginasync(NULL, 1, dc_convnone, logincb, NULL);
360 } else if(!wcscmp(resp->cmdname, L".notify")) {
361 dc_uimisc_handlenotify(resp);
369 void updatewrite(void)
375 dcfdwtag = gdk_input_add(dcfd, GDK_INPUT_WRITE, dcfdcb, NULL);
378 gdk_input_remove(dcfdwtag);
386 if((dcfd = dc_connect(server)) < 0) {
387 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server: %s"), strerror(errno));
390 dcfdrtag = gdk_input_add(dcfd, GDK_INPUT_READ, dcfdcb, NULL);
392 gtk_status_icon_set_tooltip(tray, _("Connecting to server..."));
395 void startdaemon(void)
401 if(getenv("HOME") != NULL)
402 snprintf(pf, sizeof(pf), "%s/.doldacond.pid", getenv("HOME"));
404 snprintf(pf, sizeof(pf), "%s/.doldacond.pid", getpwuid(getuid())->pw_dir);
405 if(access(pf, F_OK) || !running(pf)) {
408 sigaddset(&ss, SIGCHLD);
409 sigprocmask(SIG_BLOCK, &ss, NULL);
410 if((dpid = fork()) == 0) {
411 sigprocmask(SIG_UNBLOCK, &ss, NULL);
413 for(i = 3; i < FD_SETSIZE; i++)
415 execlp("doldacond", "doldacond", "-p", pf, NULL);
423 derrtag = gdk_input_add(derrfd, GDK_INPUT_READ, derrcb, NULL);
425 gtk_widget_show(start_wnd);
426 gtk_status_icon_set_tooltip(tray, _("Starting..."));
427 sigprocmask(SIG_UNBLOCK, &ss, NULL);
433 gboolean daemonized(gpointer uu)
435 gtk_widget_hide(start_wnd);
438 gdk_input_remove(derrtag);
442 gtk_status_icon_set_visible(tray, FALSE);
443 gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(start_log)), derrbuf, derrbufdata);
444 gtk_widget_show(start_errwnd);
451 void sighandler(int sig)
457 while((p = waitpid(-1, &status, WNOHANG)) > 0) {
460 gtk_timeout_add(1, daemonized, NULL);
461 } else if(p == dcpid) {
472 if((dcpid = fork()) == 0) {
473 for(i = 3; i < FD_SETSIZE; i++)
476 execlp("dolcon", "dolcon", NULL);
478 execlp("dolcon", "dolcon", "-l", NULL);
484 void cb_shm_dolconf_activate(GtkWidget *uu1, gpointer uu2)
488 if((dcpid = fork()) == 0) {
489 for(i = 3; i < FD_SETSIZE; i++)
491 execlp("dolconf", "dolconf", NULL);
497 void cb_shm_dolcon_activate(GtkWidget *uu1, gpointer uu2)
502 void cb_shm_quit_activate(GtkWidget *uu1, gpointer uu2)
504 dc_queuecmd(NULL, NULL, L"shutdown", NULL);
508 void tray_activate(GtkStatusIcon *uu1, gpointer uu2)
511 gtk_widget_show(start_wnd);
512 } else if(connected) {
517 void tray_popup(GtkStatusIcon *uu1, guint button, guint time, gpointer uu2)
519 gtk_menu_popup(GTK_MENU(shm_menu), NULL, NULL, NULL, NULL, button, time);
522 void cb_start_hide_clicked(GtkWidget *uu1, gpointer uu2)
524 gtk_widget_hide(start_wnd);
527 void cb_start_abort_clicked(GtkWidget *uu1, gpointer uu2)
533 void cb_start_errok_clicked(GtkWidget *uu1, gpointer uu2)
538 #include "../dolda-icon.xpm"
542 tray = gtk_status_icon_new_from_pixbuf(gdk_pixbuf_scale_simple(dcicon, 24, 24, GDK_INTERP_BILINEAR));
543 gtk_status_icon_set_tooltip(tray, "");
544 g_signal_connect(G_OBJECT(tray), "activate", G_CALLBACK(tray_activate), (gpointer)NULL);
545 g_signal_connect(G_OBJECT(tray), "popup-menu", G_CALLBACK(tray_popup), (gpointer)NULL);
548 int main(int argc, char **argv)
552 setlocale(LC_ALL, "");
553 bindtextdomain(PACKAGE, LOCALEDIR);
555 signal(SIGCHLD, sighandler);
557 server = dc_srv_local;
558 gtk_init(&argc, &argv);
560 notify_init("Dolda Connect");
562 while((c = getopt(argc, argv, "Vrhs:")) != -1) {
573 printf("usage: doldacond-shell [-hr]\n");
574 printf("\t-h\tDisplay this help message\n");
575 printf("\t-r\tConnect to a remote host\n");
576 printf("\t-V\tDisplay version info and exit\n");
579 printf("%s", RELEASEINFO);
582 fprintf(stderr, "usage: doldacond-shell [-hr]\n");
588 dcicon = gdk_pixbuf_new_from_xpm_data((const char **)dolda_icon_xpm);
589 gtk_window_set_default_icon(dcicon);
596 g_timeout_add(10000, trupdatecb, NULL);
602 #include "dsh-start.gtk"
603 #include "dsh-menu.gtk"