Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow actions to be defined to handle URLs #199

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ dillo_SOURCES = \
bw.c \
cookies.c \
cookies.h \
rules.c \
rules.h \
hsts.c \
hsts.h \
auth.c \
Expand Down
3 changes: 3 additions & 0 deletions src/dillo.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Dillo web browser
*
* Copyright 1999-2007 Jorge Arellano Cid <[email protected]>
* Copyright (C) 2024 Rodrigo Arias Mallo <[email protected]>
*
* 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
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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();
Expand Down
95 changes: 87 additions & 8 deletions src/menu.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
#include <FL/Fl.H>
#include <FL/Fl_Menu_Item.H>

#include <unistd.h>
#include "lout/misc.hh" /* SimpleVector */
#include "msg.h"
#include "menu.hh"
#include "rules.h"
#include "uicmd.hh"
#include "history.h"
#include "html.hh"
Expand Down Expand Up @@ -430,37 +432,114 @@ 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;
}
}
}

/**
* Link popup menu (construction & popup)
*/
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();
popup_bw = bw;
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);
Expand All @@ -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};
Expand Down
185 changes: 185 additions & 0 deletions src/rules.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/*
* File: rules.c
*
* Copyright (C) 2024 Rodrigo Arias Mallo <[email protected]>
*
* 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 <stdio.h>
#include <errno.h>

#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;
}
36 changes: 36 additions & 0 deletions src/rules.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* File: rules.c
*
* Copyright (C) 2024 Rodrigo Arias Mallo <[email protected]>
*
* 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__ */
Loading