Mercurial > dillo_port1.3
view dpi/downloads.cc @ 1738:7974b38cc036
use background-color on <html>-element
author | Johannes Hofmann <Johannes.Hofmann@gmx.de> |
---|---|
date | Mon, 11 Oct 2010 23:36:36 +0200 |
parents | c805b4e0b7ce |
children | e43e68b39066 |
line wrap: on
line source
/* * File: downloads.cc * * Copyright (C) 2005-2007 Jorge Arellano Cid <jcid@dillo.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ /* * A FLTK2-based GUI for the downloads dpi (dillo plugin). */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #include <ctype.h> #include <math.h> #include <time.h> #include <signal.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/un.h> #include <sys/wait.h> #include <fltk/run.h> #include <fltk/Window.h> #include <fltk/Widget.h> #include <fltk/damage.h> #include <fltk/Box.h> #include <fltk/draw.h> #include <fltk/HighlightButton.h> #include <fltk/PackedGroup.h> #include <fltk/ScrollGroup.h> #include <fltk/ask.h> #include <fltk/file_chooser.h> #include "dpiutil.h" #include "../dpip/dpip.h" using namespace fltk; /* * Debugging macros */ #define _MSG(...) #define MSG(...) printf("[downloads dpi]: " __VA_ARGS__) /* * Internal types */ typedef enum { DL_NEWFILE, DL_CONTINUE, DL_RENAME, DL_OVERWRITE, DL_ABORT } DLAction; /* * Class declarations */ // ProgressBar widget -------------------------------------------------------- // class FL_API ProgressBar : public Widget { class ProgressBar : public Widget { protected: double mMin; double mMax; double mPresent; double mStep; bool mShowPct, mShowMsg; char mMsg[64]; Color mTextColor; void draw(); public: ProgressBar(int x, int y, int w, int h, const char *lbl = 0); void range(double min, double max, double step = 1) { mMin = min; mMax = max; mStep = step; }; void step(double step) { mPresent += step; redraw(); }; void move(double step); double minimum() { return mMin; } double maximum() { return mMax; } void minimum(double nm) { mMin = nm; }; void maximum(double nm) { mMax = nm; }; double position () { return mPresent; } double step() { return mStep; } void position(double pos) { mPresent = pos; redraw(); } void showtext(bool st) { mShowPct = st; } void message(char *msg) { mShowMsg = true; strncpy(mMsg,msg,63); redraw(); } bool showtext() { return mShowPct; } void text_color(Color col) { mTextColor = col; } Color text_color() { return mTextColor; } }; // Download-item class ------------------------------------------------------- class DLItem { enum { ST_newline, ST_number, ST_discard, ST_copy }; pid_t mPid; int LogPipe[2]; char *shortname, *fullname; char *target_dir; int log_len, log_max, log_state; char *log_text; time_t init_time; char **dl_argv; time_t twosec_time, onesec_time; int twosec_bytesize, onesec_bytesize; int init_bytesize, curr_bytesize, total_bytesize; int DataDone, LogDone, ForkDone, UpdatesDone, WidgetDone; int WgetStatus; int gw, gh; Group *group; ProgressBar *prBar; HighlightButton *prButton; Widget *prTitle, *prGot, *prSize, *prRate, *pr_Rate, *prETA, *prETAt; public: DLItem(const char *full_filename, const char *url, DLAction action); ~DLItem(); void child_init(); void father_init(); void update_size(int new_sz); void log_text_add(const char *buf, ssize_t st); void log_text_show(); void abort_dl(); void prButton_cb(); pid_t pid() { return mPid; } void pid(pid_t p) { mPid = p; } void child_finished(int status); void status_msg(const char *msg) { prBar->message((char*)msg); } Widget *get_widget() { return group; } int widget_done() { return WidgetDone; } void widget_done(int val) { WidgetDone = val; } int updates_done() { return UpdatesDone; } void updates_done(int val) { UpdatesDone = val; } int fork_done() { return ForkDone; } void fork_done(int val) { ForkDone = val; } int log_done() { return LogDone; } void log_done(int val) { LogDone = val; } int wget_status() { return WgetStatus; } void wget_status(int val) { WgetStatus = val; } void update_prSize(int newsize); void update(); }; // DLItem List --------------------------------------------------------------- /// BUG: make dynamic class DLItemList { DLItem *mList[32]; int mNum, mMax; public: DLItemList() { mNum = 0; mMax = 32; } ~DLItemList() { } int num() { return mNum; } void add(DLItem *i) { if (mNum < mMax) mList[mNum++] = i; } DLItem *get(int n) { return (n >= 0 && n < mNum) ? mList[n] : NULL; } void del(int n) { if (n >= 0 && n < mNum) mList[n] = mList[--mNum]; } }; // DLWin --------------------------------------------------------------------- class DLWin { DLItemList *mDList; Window *mWin; ScrollGroup *mScroll; PackedGroup *mPG; public: DLWin(int ww, int wh); void add(const char *full_filename, const char *url, DLAction action); void del(int n_item); int num(); int num_running(); void listen(int req_fd); void show() { mWin->show(); } void hide() { mWin->hide(); } void abort_all(); DLAction check_filename(char **p_dl_dest); }; /* * Global variables */ // SIGCHLD mask sigset_t mask_sigchld; // SIGCHLD flag volatile sig_atomic_t caught_sigchld = 0; // The download window object static class DLWin *dl_win = NULL; // ProgressBar widget -------------------------------------------------------- void ProgressBar::move(double step) { mPresent += step; if (mPresent > mMax) mPresent = mMin; redraw(); } ProgressBar::ProgressBar(int x, int y, int w, int h, const char *lbl) : Widget(x, y, w, h, lbl) { mMin = mPresent = 0; mMax = 100; mShowPct = true; mShowMsg = false; box(DOWN_BOX); selection_color(BLUE); color(WHITE); textcolor(RED); } void ProgressBar::draw() { drawstyle(style(), flags()); if (damage() & DAMAGE_ALL) draw_box(); Rectangle r(w(), h()); box()->inset(r); if (mPresent > mMax) mPresent = mMax; if (mPresent < mMin) mPresent = mMin; double pct = (mPresent - mMin) / mMax; if (vertical()) { int barHeight = int (r.h() * pct + .5); r.y(r.y() + r.h() - barHeight); r.h(barHeight); } else { r.w(int (r.w() * pct + .5)); } setcolor(selection_color()); if (mShowPct) { fillrect(r); } else { Rectangle r2(int (r.w() * pct), 0, int (w() * .1), h()); push_clip(r2); fillrect(r); pop_clip(); } if (mShowMsg) { setcolor(textcolor()); setfont(this->labelfont(), this->labelsize()); drawtext(mMsg, Rectangle(w(), h()), ALIGN_CENTER); } else if (mShowPct) { char buffer[30]; sprintf(buffer, "%d%%", int (pct * 100 + .5)); setcolor(textcolor()); setfont(this->labelfont(), this->labelsize()); drawtext(buffer, Rectangle(w(), h()), ALIGN_CENTER); } } // Download-item class ------------------------------------------------------- static void prButton_scb(Widget *, void *cb_data) { DLItem *i = (DLItem *)cb_data; i->prButton_cb(); } DLItem::DLItem(const char *full_filename, const char *url, DLAction action) { struct stat ss; const char *p; char *esc_url; if (pipe(LogPipe) < 0) { MSG("pipe, %s\n", dStrerror(errno)); return; } /* Set FD to background */ fcntl(LogPipe[0], F_SETFL, O_NONBLOCK | fcntl(LogPipe[0], F_GETFL)); fullname = dStrdup(full_filename); p = strrchr(fullname, '/'); shortname = (p) ? dStrdup(p + 1) : dStrdup("??"); p = strrchr(full_filename, '/'); target_dir= p ? dStrndup(full_filename,p-full_filename+1) : dStrdup("??"); log_len = 0; log_max = 0; log_state = ST_newline; log_text = NULL; onesec_bytesize = twosec_bytesize = curr_bytesize = init_bytesize = 0; total_bytesize = -1; // Init value. Reset later, upon the first data bytes arrival init_time = time(NULL); // BUG:? test a URL with ' inside. /* escape "'" character for the shell. Is it necessary? */ esc_url = Escape_uri_str(url, "'"); /* avoid malicious SMTP relaying with FTP urls */ if (dStrncasecmp(esc_url, "ftp:/", 5) == 0) Filter_smtp_hack(esc_url); dl_argv = new char*[8]; int i = 0; dl_argv[i++] = (char*)"wget"; if (action == DL_CONTINUE) { if (stat(fullname, &ss) == 0) init_bytesize = (int)ss.st_size; dl_argv[i++] = (char*)"-c"; } dl_argv[i++] = (char*)"--load-cookies"; dl_argv[i++] = dStrconcat(dGethomedir(), "/.dillo/cookies.txt", NULL); dl_argv[i++] = (char*)"-O"; dl_argv[i++] = fullname; dl_argv[i++] = esc_url; dl_argv[i++] = NULL; DataDone = 0; LogDone = 0; UpdatesDone = 0; ForkDone = 0; WidgetDone = 0; WgetStatus = -1; gw = 400, gh = 70; group = new Group(0,0,gw,gh); group->begin(); prTitle = new Widget(24, 7, 290, 23, shortname); prTitle->box(RSHADOW_BOX); prTitle->align(ALIGN_LEFT|ALIGN_INSIDE|ALIGN_CLIP); // Attach this 'log_text' to the tooltip log_text_add("Target File: ", 13); log_text_add(fullname, strlen(fullname)); log_text_add("\n\n", 2); prBar = new ProgressBar(24, 40, 92, 20); prBar->box(BORDER_BOX); // ENGRAVED_BOX prBar->tooltip("Progress Status"); int ix = 122, iy = 36, iw = 50, ih = 14; Widget *o = new Widget(ix,iy,iw,ih, "Got"); o->box(RFLAT_BOX); o->color((Color)0xc0c0c000); o->tooltip("Downloaded Size"); prGot = new Widget(ix,iy+14,iw,ih, "0KB"); prGot->align(ALIGN_CENTER|ALIGN_INSIDE); prGot->labelcolor((Color)0x6c6cbd00); prGot->box(NO_BOX); ix += iw; o = new Widget(ix,iy,iw,ih, "Size"); o->box(RFLAT_BOX); o->color((Color)0xc0c0c000); o->tooltip("Total Size"); prSize = new Widget(ix,iy+14,iw,ih, "??"); prSize->align(ALIGN_CENTER|ALIGN_INSIDE); prSize->box(NO_BOX); ix += iw; o = new Widget(ix,iy,iw,ih, "Rate"); o->box(RFLAT_BOX); o->color((Color)0xc0c0c000); o->tooltip("Current transfer Rate (KBytes/sec)"); prRate = new Widget(ix,iy+14,iw,ih, "??"); prRate->align(ALIGN_CENTER|ALIGN_INSIDE); prRate->box(NO_BOX); ix += iw; o = new Widget(ix,iy,iw,ih, "~Rate"); o->box(RFLAT_BOX); o->color((Color)0xc0c0c000); o->tooltip("Average transfer Rate (KBytes/sec)"); pr_Rate = new Widget(ix,iy+14,iw,ih, "??"); pr_Rate->align(ALIGN_CENTER|ALIGN_INSIDE); pr_Rate->box(NO_BOX); ix += iw; prETAt = o = new Widget(ix,iy,iw,ih, "ETA"); o->box(RFLAT_BOX); o->color((Color)0xc0c0c000); o->tooltip("Estimated Time of Arrival"); prETA = new Widget(ix,iy+14,iw,ih, "??"); prETA->align(ALIGN_CENTER|ALIGN_INSIDE); prETA->box(NO_BOX); //ix += 50; //prButton = new HighlightButton(ix, 41, 38, 19, "Stop"); prButton = new HighlightButton(328, 9, 38, 19, "Stop"); prButton->tooltip("Stop this transfer"); prButton->box(UP_BOX); prButton->clear_tab_to_focus(); prButton->callback(prButton_scb, this); group->box(ROUND_UP_BOX); group->end(); } DLItem::~DLItem() { free(shortname); dFree(fullname); dFree(target_dir); free(log_text); int idx = (strcmp(dl_argv[1], "-c")) ? 2 : 3; dFree(dl_argv[idx]); dFree(dl_argv[idx+3]); delete(dl_argv); delete(group); } /* * Abort a running download */ void DLItem::abort_dl() { if (!log_done()) { close(LogPipe[0]); remove_fd(LogPipe[0]); log_done(1); // Stop wget if (!fork_done()) kill(pid(), SIGTERM); } widget_done(1); } void DLItem::prButton_cb() { prButton->deactivate(); abort_dl(); } void DLItem::child_init() { close(0); // stdin close(1); // stdout close(LogPipe[0]); dup2(LogPipe[1], 2); // stderr // set the locale to C for log parsing setenv("LC_ALL", "C", 1); // start wget execvp(dl_argv[0], dl_argv); } /* * Update displayed size */ void DLItem::update_prSize(int newsize) { char num[64]; if (newsize > 1024 * 1024) snprintf(num, 64, "%.1fMB", (float)newsize / (1024*1024)); else snprintf(num, 64, "%.0fKB", (float)newsize / 1024); prSize->copy_label(num); prSize->redraw_label(); } void DLItem::log_text_add(const char *buf, ssize_t st) { const char *p; char *q, *d, num[64]; // Make room... if (log_len + st >= log_max) { log_max = log_len + st + 1024; log_text = (char *) realloc (log_text, log_max); log_text[log_len] = 0; prTitle->tooltip(log_text); } // FSM to remove wget's "dot-progress" (i.e. "^ " || "^[0-9]+K") q = log_text + log_len; for (p = buf; (p - buf) < st; ++p) { switch (log_state) { case ST_newline: if (*p == ' ') { log_state = ST_discard; } else if (isdigit(*p)) { *q++ = *p; log_state = ST_number; } else if (*p == '\n') { *q++ = *p; } else { *q++ = *p; log_state = ST_copy; } break; case ST_number: if (isdigit(*q++ = *p)) { // keep here } else if (*p == 'K') { for (--q; isdigit(q[-1]); --q) ; log_state = ST_discard; } else { log_state = ST_copy; } break; case ST_discard: if (*p == '\n') log_state = ST_newline; break; case ST_copy: if ((*q++ = *p) == '\n') log_state = ST_newline; break; } } *q = 0; log_len = strlen(log_text); // Now scan for the length of the file if (total_bytesize == -1) { p = strstr(log_text, "\nLength: "); if (p && isdigit(p[9]) && strchr(p + 9, ' ')) { for (p += 9, d = &num[0]; *p != ' '; ++p) if (isdigit(*p)) *d++ = *p; *d = 0; total_bytesize = strtol (num, NULL, 10); // Update displayed size update_prSize(total_bytesize); } } // Show we're connecting... if (curr_bytesize == 0) { prTitle->label("Connecting..."); prTitle->redraw_label(); } } /// void DLItem::log_text_show() { MSG("\nStored Log:\n%s", log_text); } void DLItem::update_size(int new_sz) { char buf[64]; if (curr_bytesize == 0 && new_sz) { // Start the timer with the first bytes got init_time = time(NULL); // Update the title prTitle->label(shortname); prTitle->redraw_label(); } curr_bytesize = new_sz; if (curr_bytesize > 1024 * 1024) snprintf(buf, 64, "%.1fMB", (float)curr_bytesize / (1024*1024)); else snprintf(buf, 64, "%.0fKB", (float)curr_bytesize / 1024); prGot->copy_label(buf); prGot->redraw_label(); if (total_bytesize == -1) { prBar->showtext(false); prBar->move(1); } else { prBar->showtext(true); double pos = 100.0; if (total_bytesize > 0) pos *= (double)curr_bytesize / total_bytesize; prBar->position(pos); } } static void read_log_cb(int fd_in, void *data) { DLItem *dl_item = (DLItem *)data; const int BufLen = 4096; char Buf[BufLen]; ssize_t st; int ret = -1; do { st = read(fd_in, Buf, BufLen); if (st < 0) { if (errno == EAGAIN) { ret = 1; break; } perror("read, "); break; } else if (st == 0) { close(fd_in); remove_fd(fd_in, 1); dl_item->log_done(1); ret = 0; break; } else { dl_item->log_text_add(Buf, st); } } while (1); } void DLItem::father_init() { close(LogPipe[1]); add_fd(LogPipe[0], 1, read_log_cb, this); // Read // Start the timer after the child is running. // (this makes a big difference with wget) //init_time = time(NULL); } /* * Our wget exited, let's check its status and update the panel. */ void DLItem::child_finished(int status) { wget_status(status); if (status == 0) { prButton->label("Done"); prButton->tooltip("Close this information panel"); } else { prButton->label("Close"); prButton->tooltip("Close this information panel"); status_msg("ABORTED"); if (curr_bytesize == 0) { // Update the title prTitle->label(shortname); prTitle->redraw_label(); } } prButton->activate(); prButton->redraw(); MSG("wget status %d\n", status); } /* * Convert seconds into human readable [hour]:[min]:[sec] string. */ static void secs2timestr(int et, char *str) { int eh, em, es; eh = et / 3600; em = (et % 3600) / 60; es = et % 60; if (eh == 0) { if (em == 0) snprintf(str, 8, "%ds", es); else snprintf(str, 8, "%dm%ds", em, es); } else { snprintf(str, 8, "%dh%dm", eh, em); } } /* * Update Got, Rate, ~Rate and ETA */ void DLItem::update() { struct stat ss; time_t curr_time; float csec, tsec, rate, _rate = 0; char str[64]; int et; if (updates_done()) return; /* Update curr_size */ if (stat(fullname, &ss) == -1) { MSG("stat, %s\n", dStrerror(errno)); return; } update_size((int)ss.st_size); /* Get current time */ time(&curr_time); csec = (float) (curr_time - init_time); /* Rate */ if (csec >= 2) { tsec = (float) (curr_time - twosec_time); rate = ((float)(curr_bytesize-twosec_bytesize) / 1024) / tsec; snprintf(str, 64, (rate < 100) ? "%.1fK/s" : "%.0fK/s", rate); prRate->copy_label(str); prRate->redraw_label(); } /* ~Rate */ if (csec >= 1) { _rate = ((float)(curr_bytesize-init_bytesize) / 1024) / csec; snprintf(str, 64, (_rate < 100) ? "%.1fK/s" : "%.0fK/s", _rate); pr_Rate->copy_label(str); pr_Rate->redraw_label(); } /* ETA */ if (fork_done()) { updates_done(1); // Last update prETAt->label("Time"); prETAt->tooltip("Download Time"); prETAt->redraw(); secs2timestr((int)csec, str); prETA->copy_label(str); if (total_bytesize == -1) { update_prSize(curr_bytesize); if (wget_status() == 0) status_msg("Done"); } } else { if (_rate > 0 && total_bytesize > 0 && curr_bytesize > 0) { et = (int)((total_bytesize-curr_bytesize) / (_rate * 1024)); secs2timestr(et, str); prETA->copy_label(str); } } prETA->redraw_label(); /* Update one and two secs ago times and bytesizes */ twosec_time = onesec_time; onesec_time = curr_time; twosec_bytesize = onesec_bytesize; onesec_bytesize = curr_bytesize; } // SIGCHLD ------------------------------------------------------------------- /*! SIGCHLD handler */ static void raw_sigchld(int) { caught_sigchld = 1; } /*! Establish SIGCHLD handler */ static void est_sigchld(void) { struct sigaction sigact; sigset_t set; (void) sigemptyset(&set); sigact.sa_handler = raw_sigchld; sigact.sa_mask = set; sigact.sa_flags = SA_NOCLDSTOP; if (sigaction(SIGCHLD, &sigact, NULL) == -1) { perror("sigaction"); exit(1); } } /* * Timeout function to check wget's exit status. */ static void cleanup_cb(void *data) { DLItemList *list = (DLItemList *)data; sigprocmask(SIG_BLOCK, &mask_sigchld, NULL); if (caught_sigchld) { /* Handle SIGCHLD */ int i, status; for (i = 0; i < list->num(); ++i) { if (!list->get(i)->fork_done() && waitpid(list->get(i)->pid(), &status, WNOHANG) > 0) { list->get(i)->child_finished(status); list->get(i)->fork_done(1); } } caught_sigchld = 0; } sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL); repeat_timeout(1.0,cleanup_cb,data); } /* * Timeout function to update the widget indicators, * also remove widgets marked "done". */ static void update_cb(void *data) { static int cb_used = 0; DLItemList *list = (DLItemList *)data; /* Update the widgets and remove the ones marked as done */ for (int i = 0; i < list->num(); ++i) { if (!list->get(i)->widget_done()) { list->get(i)->update(); } else if (list->get(i)->fork_done()) { // widget_done and fork_done avoid a race condition. dl_win->del(i); --i; } cb_used = 1; } if (cb_used && list->num() == 0) exit(0); repeat_timeout(1.0,update_cb,data); } // DLWin --------------------------------------------------------------------- /* * Make a new name and place it in 'dl_dest'. */ static void make_new_name(char **dl_dest, const char *url) { Dstr *gstr = dStr_new(*dl_dest); int idx = gstr->len; if (gstr->str[idx - 1] != '/'){ dStr_append_c(gstr, '/'); ++idx; } /* Use a mangled url as name */ dStr_append(gstr, url); for ( ; idx < gstr->len; ++idx) if (!isalnum(gstr->str[idx])) gstr->str[idx] = '_'; /* free memory */ dFree(*dl_dest); *dl_dest = gstr->str; dStr_free(gstr, FALSE); } /* * Callback function for the request socket. * Read the request, parse and start a new download. */ static void read_req_cb(int req_fd, void *) { struct sockaddr_un clnt_addr; int sock_fd; socklen_t csz; struct stat sb; Dsh *sh = NULL; char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *dl_dest = NULL; DLAction action = DL_ABORT; /* compiler happiness */ /* Initialize the value-result parameter */ csz = sizeof(struct sockaddr_un); /* accept the request */ do { sock_fd = accept(req_fd, (struct sockaddr *) &clnt_addr, &csz); } while (sock_fd == -1 && errno == EINTR); if (sock_fd == -1) { MSG("accept, %s fd=%d\n", dStrerror(errno), req_fd); return; } /* create a sock handler */ sh = a_Dpip_dsh_new(sock_fd, sock_fd, 8*1024); /* Authenticate our client... */ if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) || a_Dpip_check_auth(dpip_tag) < 0) { MSG("can't authenticate request: %s fd=%d\n", dStrerror(errno), sock_fd); a_Dpip_dsh_close(sh); goto end; } dFree(dpip_tag); /* Read request */ if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1))) { MSG("can't read request: %s fd=%d\n", dStrerror(errno), sock_fd); a_Dpip_dsh_close(sh); goto end; } a_Dpip_dsh_close(sh); _MSG("Received tag={%s}\n", dpip_tag); if ((cmd = a_Dpip_get_attr(dpip_tag, "cmd")) == NULL) { MSG("Failed to parse 'cmd' in {%s}\n", dpip_tag); goto end; } if (strcmp(cmd, "DpiBye") == 0) { MSG("got DpiBye, ignoring...\n"); goto end; } if (strcmp(cmd, "download") != 0) { MSG("unknown command: '%s'. Aborting.\n", cmd); goto end; } if (!(url = a_Dpip_get_attr(dpip_tag, "url"))){ MSG("Failed to parse 'url' in {%s}\n", dpip_tag); goto end; } if (!(dl_dest = a_Dpip_get_attr(dpip_tag, "destination"))){ MSG("Failed to parse 'destination' in {%s}\n", dpip_tag); goto end; } /* 'dl_dest' may be a directory */ if (stat(dl_dest, &sb) == 0 && S_ISDIR(sb.st_mode)) { make_new_name(&dl_dest, url); } action = dl_win->check_filename(&dl_dest); if (action != DL_ABORT) { // Start the whole thing whithin FLTK. dl_win->add(dl_dest, url, action); } else if (dl_win->num() == 0) { exit(0); } end: dFree(cmd); dFree(url); dFree(dl_dest); dFree(dpip_tag); a_Dpip_dsh_free(sh); } /* * Callback for close window request (WM or EscapeKey press) */ static void dlwin_esc_cb(Widget *, void *) { const char *msg = "There are running downloads.\n" "ABORT them and EXIT anyway?"; if (dl_win && dl_win->num_running() > 0) { int ch = fltk::choice(msg, "Yes", "*No", "Cancel"); if (ch != 0) return; } /* abort each download properly */ dl_win->abort_all(); } /* * Add a new download request to the main window and * fork a child to do the job. */ void DLWin::add(const char *full_filename, const char *url, DLAction action) { DLItem *dl_item = new DLItem(full_filename, url, action); mDList->add(dl_item); mPG->insert(*dl_item->get_widget(), 0); _MSG("Child index = %d\n", mPG->find(dl_item->get_widget())); // Start the child process pid_t f_pid = fork(); if (f_pid == 0) { /* child */ dl_item->child_init(); _exit(EXIT_FAILURE); } else if (f_pid < 0) { perror("fork, "); exit(1); } else { /* father */ dl_win->show(); dl_item->pid(f_pid); dl_item->father_init(); } } /* * Decide what to do when the filename already exists. * (renaming takes place here when necessary) */ DLAction DLWin::check_filename(char **p_fullname) { struct stat ss; Dstr *ds; int ch; DLAction ret = DL_ABORT; if (stat(*p_fullname, &ss) == -1) return DL_NEWFILE; ds = dStr_sized_new(128); dStr_sprintf(ds, "The file:\n %s (%d Bytes)\nalready exists. What do we do?", *p_fullname, (int)ss.st_size); ch = fltk::choice(ds->str, "Rename", "Continue", "Abort"); dStr_free(ds, 1); MSG("Choice %d\n", ch); if (ch == 0) { const char *p; p = fltk::file_chooser("Enter a new name:", NULL, *p_fullname); if (p) { dFree(*p_fullname); *p_fullname = dStrdup(p); ret = check_filename(p_fullname); } } else if (ch == 1) { ret = DL_CONTINUE; } return ret; } /* * Delete a download request from the main window. */ void DLWin::del(int n_item) { DLItem *dl_item = mDList->get(n_item); // Remove the widget from the packed group mPG->remove(dl_item->get_widget()); mDList->del(n_item); delete(dl_item); } /* * Return number of entries */ int DLWin::num() { return mDList->num(); } /* * Return number of running downloads */ int DLWin::num_running() { int i, nr; for (i = nr = 0; i < mDList->num(); ++i) if (!mDList->get(i)->fork_done()) ++nr; return nr; } /* * Set a callback function for the request socket */ void DLWin::listen(int req_fd) { add_fd(req_fd, 1, read_req_cb, NULL); // Read } /* * Abort each download properly, and let the main cycle exit */ void DLWin::abort_all() { for (int i = 0; i < mDList->num(); ++i) mDList->get(i)->abort_dl(); } /* * Create the main window and an empty list of requests. */ DLWin::DLWin(int ww, int wh) { // Init an empty list for the download requests mDList = new DLItemList(); // Create the empty main window mWin = new Window(ww, wh, "Downloads:"); mWin->begin(); mScroll = new ScrollGroup(0,0,ww,wh); mScroll->begin(); mPG = new PackedGroup(0,0,ww,wh); mPG->end(); //mPG->spacing(10); mScroll->end(); mScroll->type(ScrollGroup::VERTICAL); mWin->end(); mWin->resizable(mScroll); mWin->callback(dlwin_esc_cb, NULL); mWin->show(); // Set SIGCHLD handlers sigemptyset(&mask_sigchld); sigaddset(&mask_sigchld, SIGCHLD); est_sigchld(); // Set the cleanup timeout add_timeout(1.0, cleanup_cb, mDList); // Set the update timeout add_timeout(1.0, update_cb, mDList); } // --------------------------------------------------------------------------- //int main(int argc, char **argv) int main() { int ww = 420, wh = 85; lock(); // Create the download window dl_win = new DLWin(ww, wh); // Start listening to the request socket dl_win->listen(STDIN_FILENO); MSG("started...\n"); return run(); }