diff options
Diffstat (limited to 'dpi/downloads.cc')
-rw-r--r-- | dpi/downloads.cc | 1133 |
1 files changed, 1133 insertions, 0 deletions
diff --git a/dpi/downloads.cc b/dpi/downloads.cc new file mode 100644 index 00000000..b61f4914 --- /dev/null +++ b/dpi/downloads.cc @@ -0,0 +1,1133 @@ +/* + * File: downloads.cc + * + * Copyright (C) 2005 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(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(char *msg) { prBar->message(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; + char *p, *esc_url; + + if (pipe(LogPipe) < 0) { + MSG("pipe, %s\n", strerror(errno)); + return; + } + /* Set FD to background */ + fcntl(LogPipe[0], F_SETFL, + O_NONBLOCK | fcntl(LogPipe[0], F_GETFL)); + + fullname = strdup(full_filename); + p = strrchr(fullname, '/'); + shortname = (p) ? strdup(p + 1) : strdup("??"); + 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++] = "wget"; + if (action == DL_CONTINUE) { + if (stat(fullname, &ss) == 0) + init_bytesize = (int)ss.st_size; + dl_argv[i++] = "-c"; + } + dl_argv[i++] = "--load-cookies"; + dl_argv[i++] = dStrconcat(dGethomedir(), "/.dillo/cookies.txt", NULL); + dl_argv[i++] = "-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 = 470, 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->resizable(group); + 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(char *buf, ssize_t st) +{ + char *p, *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 * (double)curr_bytesize / total_bytesize; + prBar->position(pos); + } +} + +static void read_log_cb(int fd_in, void *data) +{ + DLItem *dl_item = (DLItem *)data; + 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. + */ +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", strerror(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 + */ +void raw_sigchld(int) +{ + caught_sigchld = 1; +} + +/*! Establish SIGCHLD handler */ +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. + */ +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". + */ +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 --------------------------------------------------------------------- + +/* + * Read a single line from a socket and store it in a Dstr. + */ +static ssize_t readline(int socket, Dstr ** msg) +{ + ssize_t st; + char buf[16384]; + + /* can't use fread() */ + do + st = read(socket, buf, 16384); + while (st < 0 && errno == EINTR); + + if (st == -1) + MSG("readline, %s\n", strerror(errno)); + + dStr_truncate(*msg, 0); + if (st > 0) + dStr_append_l(*msg, buf, (int)st); + + return st; +} + +/* + * 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 *) +{ + Dstr *tag; + struct sockaddr_un clnt_addr; + int new_socket; + socklen_t csz; + struct stat sb; + char *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 { + new_socket = accept(req_fd, (struct sockaddr *) &clnt_addr, &csz); + } while (new_socket == -1 && errno == EINTR); + if (new_socket == -1) { + MSG("accept, %s fd=%d\n", strerror(errno), req_fd); + return; + } + + //sigprocmask(SIG_BLOCK, &blockSC, NULL); + tag = dStr_sized_new(64); + readline(new_socket, &tag); + close(new_socket); + _MSG("Received tag={%s}\n", tag->str); + + if ((cmd = a_Dpip_get_attr(tag->str, (size_t)tag->len, "cmd")) == NULL) { + MSG("Failed to parse 'cmd' in %s\n", tag->str); + 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(tag->str,(size_t)tag->len, "url"))){ + MSG("Failed to parse 'url' in %s\n", tag->str); + goto end; + } + if (!(dl_dest = a_Dpip_get_attr(tag->str,(size_t)tag->len,"destination"))){ + MSG("Failed to parse 'destination' in %s\n", tag->str); + 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); + dStr_free(tag, TRUE); +} + +/* + * Callback for close window request (WM or EscapeKey press) + */ +static void dlwin_esc_cb(Widget *, void *) +{ + 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->add(*dl_item->get_widget()); + 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; +} + +/* + * Add a new download request to the main window and + * fork a child to do the job. + */ +void DLWin::del(int n_item) +{ + DLItem *dl_item = mDList->get(n_item); + + // Remove the widget from the scroll group + mPG->remove(dl_item->get_widget()); + // Resize the scroll group + mPG->resize(mWin->w(), 1); + + 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(); + mWin->resizable(mWin); + mWin->end(); + 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(); +} + |