diff --git a/src/Makefile.am b/src/Makefile.am index e26c246c..e192c1d3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -37,6 +37,8 @@ dillo_SOURCES = \ bw.c \ cookies.c \ cookies.h \ + rules.c \ + rules.h \ hsts.c \ hsts.h \ auth.c \ diff --git a/src/dillo.cc b/src/dillo.cc index 889b5256..b475e3e8 100644 --- a/src/dillo.cc +++ b/src/dillo.cc @@ -2,6 +2,7 @@ * Dillo web browser * * Copyright 1999-2007 Jorge Arellano Cid + * Copyright (C) 2024 Rodrigo Arias Mallo * * 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 @@ -55,6 +56,7 @@ #include "capi.h" #include "dicache.h" #include "cookies.h" +#include "rules.h" #include "hsts.h" #include "domain.h" #include "auth.h" @@ -474,6 +476,7 @@ int main(int argc, char **argv) a_Dicache_init(); a_Bw_init(); a_Cookies_init(); + a_Rules_init(); a_Hsts_init(Paths::getPrefsFP(PATHS_HSTS_PRELOAD)); a_Auth_init(); a_UIcmd_init(); diff --git a/src/menu.cc b/src/menu.cc index 865b843b..f2bf3c8e 100644 --- a/src/menu.cc +++ b/src/menu.cc @@ -18,9 +18,11 @@ #include #include +#include #include "lout/misc.hh" /* SimpleVector */ #include "msg.h" #include "menu.hh" +#include "rules.h" #include "uicmd.hh" #include "history.h" #include "html.hh" @@ -430,22 +432,96 @@ void a_Menu_page_popup(BrowserWindow *bw, const DilloUrl *url, a_Timeout_add(0.0, Menu_popup_cb, (void*)&page_data); } -static Fl_Menu_Item link_menu[] = { +static Fl_Menu_Item link_menu_[] = { {"Open link in new tab", 0, Menu_open_url_nt_cb,0,0,0,0,0,0}, {"Open link in new window", 0, Menu_open_url_nw_cb,0,FL_MENU_DIVIDER,0,0, 0,0}, {"Bookmark this link", 0, Menu_add_bookmark_cb,0,0,0,0,0,0}, {"Copy link location", 0, Menu_copy_urlstr_cb,0,FL_MENU_DIVIDER,0,0,0,0}, - {"Save link as...", 0, Menu_save_link_cb,0,0,0,0,0,0}, + {"Save link as...", 0, Menu_save_link_cb,0,FL_MENU_DIVIDER,0,0,0,0}, {0,0,0,0,0,0,0,0,0} }; -static void Menu_set_link_menu_user_data(void *user_data) +/* As we can only provide a pointer to the link meny items, we need to + * create an auxiliary structure to hold the current URL and the program + * that should run on each item. */ +struct link_menu_item { + DilloUrl *url; + struct rule *rule; +}; + +/** + * Open URL following a custom action rule + */ +static void Menu_open_url_action_cb(Fl_Widget*, void *user_data) { - int i; + struct link_menu_item *mitem = (struct link_menu_item *) user_data; + DilloUrl *url = mitem->url; + struct rule *rule = mitem->rule; - for (i = 0; link_menu[i].label(); i++) - link_menu[i].user_data(user_data); + /* Set the url */ + setenv("url", URL_STR(url), 1); + MSG("Running: %s\n", rule->command); + + if (fork() == 0) { + /* Child */ + system(rule->command); + exit(0); + } +} + +static Fl_Menu_Item *get_link_menu(void) +{ + static Fl_Menu_Item *link_menu = NULL; + static struct link_menu_item *link_menu_item = NULL; + + /* Already initialized */ + if (link_menu != NULL) + return link_menu; + + struct rules *rules = a_Rules_get(); + + /* Count static menu entries */ + int nstatic = 0; + while (link_menu_[nstatic].text) + nstatic++; + + int ntotal = nstatic + rules->n; + link_menu = (Fl_Menu_Item *) calloc(ntotal + 1, sizeof(Fl_Menu_Item)); + link_menu_item = (struct link_menu_item *) calloc(rules->n, sizeof(struct link_menu_item)); + + /* Just copy the static entries */ + for (int i = 0; i < nstatic; i++) { + memcpy(&link_menu[i], &link_menu_[i], sizeof(Fl_Menu_Item)); + } + + /* And append the dynamic ones */ + for (int i = 0; i < rules->n; i++) { + struct rule *rule = rules->rule[i]; + struct link_menu_item *mitem = &link_menu_item[i]; + mitem->url = NULL; /* Not known yet */ + mitem->rule = rule; + + Fl_Menu_Item *item = &link_menu[nstatic + i]; + item->text = rule->action; + item->callback_ = Menu_open_url_action_cb; + item->user_data_ = mitem; + } + + return link_menu; +} + +static void Menu_set_link_menu_user_data(DilloUrl *url) +{ + Fl_Menu_Item *link_menu = get_link_menu(); + for (int i = 0; link_menu[i].label(); i++) { + if (link_menu[i].callback_ == Menu_open_url_action_cb) { + struct link_menu_item *mitem = (struct link_menu_item *) link_menu[i].user_data_; + mitem->url = url; + } else { + link_menu[i].user_data_ = url; + } + } } /** @@ -453,7 +529,7 @@ static void Menu_set_link_menu_user_data(void *user_data) */ void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url) { - static Menu_popup_data_t link_data = {"Link menu", NULL, link_menu}; + static Menu_popup_data_t link_data = {"Link menu", NULL, NULL}; popup_x = Fl::event_x(); popup_y = Fl::event_y(); @@ -461,6 +537,9 @@ void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url) a_Url_free(popup_url); popup_url = a_Url_dup(url); + Fl_Menu_Item *link_menu = get_link_menu(); + link_data.menu = link_menu; + Menu_set_link_menu_user_data(popup_url); a_Timeout_add(0.0, Menu_popup_cb, (void*)&link_data); @@ -484,7 +563,7 @@ void a_Menu_image_popup(BrowserWindow *bw, const DilloUrl *url, {"Bookmark this image", 0, Menu_add_bookmark_cb,0,0,0,0,0,0}, {"Copy image location", 0,Menu_copy_urlstr_cb,0,FL_MENU_DIVIDER,0,0,0,0}, {"Save image as...", 0, Menu_save_link_cb, 0, FL_MENU_DIVIDER,0,0,0,0}, - {"Link menu", 0, Menu_nop_cb, link_menu, FL_SUBMENU_POINTER,0,0,0,0}, + {"Link menu", 0, Menu_nop_cb, get_link_menu(), FL_SUBMENU_POINTER,0,0,0,0}, {0,0,0,0,0,0,0,0,0} }; static Menu_popup_data_t image_data = {"Image menu", NULL, pm}; diff --git a/src/rules.c b/src/rules.c new file mode 100644 index 00000000..10ce9310 --- /dev/null +++ b/src/rules.c @@ -0,0 +1,185 @@ +/* + * File: rules.c + * + * Copyright (C) 2024 Rodrigo Arias Mallo + * + * 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. + */ + +#include "rules.h" +#include "msg.h" +#include "../dlib/dlib.h" +#include +#include + +#define LINE_MAXLEN 4096 + +static struct rules rules; + +int +parse_string(char *line, char *buf, int n, char **next) +{ + char *p = line; + if (*p != '"') { + MSG("expecting string\n"); + return -1; + } + + p++; /* Skip quote */ + + int i = 0; + while (1) { + if (p[0] == '\\' && p[1] == '"') { + if (i >= n) { + MSG("too big\n"); + return -1; + } + buf[i++] = '"'; + p += 2; + continue; + } + + if (*p == '\0') { + MSG("premature end\n"); + return -1; + } + + if (p[0] == '"') { + p++; + break; + } + + if (i >= n) { + MSG("too big\n"); + return -1; + } + buf[i++] = *p; + p++; + } + + if (i >= n) { + MSG("too big\n"); + return -1; + } + buf[i] = '\0'; + + *next = p; + + return 0; +} + +int +parse_keyword(char *p, char *keyword, char **next) +{ + while (*p == ' ') + p++; + + int n = strlen(keyword); + + if (strncmp(p, keyword, n) != 0) { + MSG("doesn't match keyword '%s' at p=%s\n", keyword, p); + return -1; + } + + p += n; + + /* Skip spaces after action */ + while (*p == ' ') + p++; + + *next = p; + return 0; +} + +struct rule * +parse_rule(char *line) +{ + char *p; + if (parse_keyword(line, "action", &p) != 0) { + MSG("cannot find keyword action\n"); + return NULL; + } + + char name[LINE_MAXLEN]; + if (parse_string(p, name, LINE_MAXLEN, &p) != 0) { + MSG("failed parsing action name\n"); + return NULL; + } + + line = p; + if (parse_keyword(line, "shell", &p) != 0) { + MSG("cannot find keyword shell\n"); + return NULL; + } + + char cmd[LINE_MAXLEN]; + if (parse_string(p, cmd, LINE_MAXLEN, &p) != 0) { + MSG("failed parsing shell command\n"); + return NULL; + } + + MSG("name = '%s', command = '%s'\n", name, cmd); + + struct rule *rule = dNew(struct rule, 1); + rule->action = dStrdup(name); + rule->command = dStrdup(cmd); + + return rule; +} + +int +a_Rules_init(void) +{ + memset(&rules, 0, sizeof(rules)); + + char *filename = dStrconcat(dGethomedir(), "/.dillo/rulesrc", NULL); + if (!filename) { + MSG("dStrconcat failed\n"); + return -1; + } + + FILE *f = fopen(filename, "r"); + if (!f) { + /* No rules to parse */ + return 0; + } + + char line[LINE_MAXLEN]; + while (!feof(f)) { + line[0] = '\0'; + char *rc = fgets(line, LINE_MAXLEN, f); + if (!rc && ferror(f)) { + MSG("Error while reading rule from rulesrc: %s\n", + dStrerror(errno)); + break; /* bail out */ + } + + /* Remove leading and trailing whitespaces */ + dStrstrip(line); + + if ((line[0] == '\0') || (line[0] == '#')) + continue; + + MSG("RULE: %s\n", line); + struct rule *rule = parse_rule(line); + if (rule == NULL) { + MSG("Cannot parse rule: %s\n", line); + break; + } + + if (rules.n < 256) + rules.rule[rules.n++] = rule; + } + + fclose(f); + return 0; +} + +struct rules * +a_Rules_get(void) +{ + return &rules; +} diff --git a/src/rules.h b/src/rules.h new file mode 100644 index 00000000..0c828ea2 --- /dev/null +++ b/src/rules.h @@ -0,0 +1,36 @@ +/* + * File: rules.c + * + * Copyright (C) 2024 Rodrigo Arias Mallo + * + * 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. + */ + +#ifndef __RULES_H__ +#define __RULES_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Only one type of rule for now */ +struct rule { + char *action; + char *command; +}; + +struct rules { + struct rule *rule[256]; + int n; +}; + +int a_Rules_init(void); +struct rules *a_Rules_get(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* !__RULES_H__ */