Initial import of Dillo
This commit is contained in:
471
src/IO/IO.c
Normal file
471
src/IO/IO.c
Normal file
@ -0,0 +1,471 @@
|
||||
/*
|
||||
* File: IO.c
|
||||
*
|
||||
* Copyright (C) 2000-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.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* Dillo's event driven IO engine
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include "../msg.h"
|
||||
#include "../chain.h"
|
||||
#include "../klist.h"
|
||||
#include "IO.h"
|
||||
#include "iowatch.hh"
|
||||
#include "tls.h"
|
||||
|
||||
/*
|
||||
* Symbolic defines for shutdown() function
|
||||
* (Not defined in the same header file, for all distros --Jcid)
|
||||
*/
|
||||
#define IO_StopRd 1
|
||||
#define IO_StopWr 2
|
||||
#define IO_StopRdWr (IO_StopRd | IO_StopWr)
|
||||
|
||||
|
||||
typedef struct {
|
||||
int Key; /* Primary Key (for klist) */
|
||||
int Op; /* IORead | IOWrite */
|
||||
int FD; /* Current File Descriptor */
|
||||
int Status; /* nonzero upon IO failure */
|
||||
Dstr *Buf; /* Internal buffer */
|
||||
|
||||
void *Info; /* CCC Info structure for this IO */
|
||||
} IOData_t;
|
||||
|
||||
|
||||
/*
|
||||
* Local data
|
||||
*/
|
||||
static Klist_t *ValidIOs = NULL; /* Active IOs list. It holds pointers to
|
||||
* IOData_t structures. */
|
||||
|
||||
/*
|
||||
* Forward declarations
|
||||
*/
|
||||
void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
|
||||
void *Data1, void *Data2);
|
||||
|
||||
|
||||
/* IO API - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
/**
|
||||
* Return a new, initialized, 'io' struct
|
||||
*/
|
||||
static IOData_t *IO_new(int op)
|
||||
{
|
||||
IOData_t *io = dNew0(IOData_t, 1);
|
||||
io->Op = op;
|
||||
io->FD = -1;
|
||||
io->Key = 0;
|
||||
io->Buf = dStr_sized_new(IOBufLen);
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
/**
|
||||
* Register an IO in ValidIOs
|
||||
*/
|
||||
static void IO_ins(IOData_t *io)
|
||||
{
|
||||
if (io->Key == 0) {
|
||||
io->Key = a_Klist_insert(&ValidIOs, io);
|
||||
}
|
||||
_MSG("IO_ins: io->Key=%d, Klist_length=%d\n",
|
||||
io->Key, a_Klist_length(ValidIOs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an IO from ValidIOs
|
||||
*/
|
||||
static void IO_del(IOData_t *io)
|
||||
{
|
||||
if (io->Key != 0) {
|
||||
a_Klist_remove(ValidIOs, io->Key);
|
||||
}
|
||||
io->Key = 0;
|
||||
_MSG(" -->ValidIOs: %d\n", a_Klist_length(ValidIOs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a io by its Key (NULL if not found)
|
||||
*/
|
||||
static IOData_t *IO_get(int Key)
|
||||
{
|
||||
return (IOData_t *)a_Klist_get_data(ValidIOs, Key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free an 'io' struct
|
||||
*/
|
||||
static void IO_free(IOData_t *io)
|
||||
{
|
||||
dStr_free(io->Buf, 1);
|
||||
dFree(io);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close an open FD, and remove io controls.
|
||||
* (This function can be used for Close and Abort operations)
|
||||
* BUG: there's a race condition for Abort. The file descriptor is closed
|
||||
* twice, and it could be reused for something else in between. It's simple
|
||||
* to fix, but it'd be better to design a canonical way to Abort the CCC.
|
||||
*/
|
||||
static void IO_close_fd(IOData_t *io, int CloseCode)
|
||||
{
|
||||
int events = 0;
|
||||
|
||||
_MSG("====> begin IO_close_fd (%d) Key=%d CloseCode=%d ",
|
||||
io->FD, io->Key, CloseCode);
|
||||
|
||||
/* With HTTP, if we close the writing part, the reading one also gets
|
||||
* closed! */
|
||||
if ((CloseCode == IO_StopRdWr) && io->FD != -1) {
|
||||
dClose(io->FD);
|
||||
} else {
|
||||
_MSG(" NOT CLOSING ");
|
||||
}
|
||||
/* Remove this IOData_t reference, from our ValidIOs list
|
||||
* We don't deallocate it here, just remove from the list.*/
|
||||
IO_del(io);
|
||||
|
||||
/* Stop the polling on this FD */
|
||||
if (CloseCode & IO_StopRd) {
|
||||
events |= DIO_READ;
|
||||
}
|
||||
if (CloseCode & IO_StopWr) {
|
||||
events |= DIO_WRITE;
|
||||
}
|
||||
a_IOwatch_remove_fd(io->FD, events);
|
||||
_MSG(" end IO close (%d) <=====\n", io->FD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data from a file descriptor into a specific buffer
|
||||
*/
|
||||
static bool_t IO_read(IOData_t *io)
|
||||
{
|
||||
char Buf[IOBufLen];
|
||||
ssize_t St;
|
||||
bool_t ret = FALSE;
|
||||
int io_key = io->Key;
|
||||
void *conn = a_Tls_connection(io->FD);
|
||||
|
||||
_MSG(" IO_read\n");
|
||||
|
||||
/* this is a new read-buffer */
|
||||
dStr_truncate(io->Buf, 0);
|
||||
io->Status = 0;
|
||||
|
||||
while (1) {
|
||||
St = conn ? a_Tls_read(conn, Buf, IOBufLen)
|
||||
: read(io->FD, Buf, IOBufLen);
|
||||
if (St > 0) {
|
||||
dStr_append_l(io->Buf, Buf, St);
|
||||
continue;
|
||||
} else if (St < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
} else if (errno == EAGAIN) {
|
||||
ret = TRUE;
|
||||
break;
|
||||
} else {
|
||||
if (conn) {
|
||||
io->Status = St;
|
||||
break;
|
||||
} else {
|
||||
io->Status = errno;
|
||||
MSG("READ Failed with %d: %s\n", (int)St, strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else { /* St == 0 */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (io->Buf->len > 0) {
|
||||
/* send what we've got so far */
|
||||
a_IO_ccc(OpSend, 2, FWD, io->Info, io, NULL);
|
||||
}
|
||||
if (St == 0) {
|
||||
/* TODO: design a general way to avoid reentrancy problems with CCC. */
|
||||
|
||||
/* The following check is necessary because the above CCC operation
|
||||
* may abort the whole Chain. */
|
||||
if ((io = IO_get(io_key))) {
|
||||
/* All data read (EOF) */
|
||||
_MSG("IO_read: io->Key=%d io_key=%d\n", io->Key, io_key);
|
||||
a_IO_ccc(OpEnd, 2, FWD, io->Info, io, NULL);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data, from a specific buffer, into a file descriptor
|
||||
*/
|
||||
static bool_t IO_write(IOData_t *io)
|
||||
{
|
||||
ssize_t St;
|
||||
bool_t ret = FALSE;
|
||||
void *conn = a_Tls_connection(io->FD);
|
||||
|
||||
_MSG(" IO_write\n");
|
||||
io->Status = 0;
|
||||
|
||||
while (1) {
|
||||
St = conn ? a_Tls_write(conn, io->Buf->str, io->Buf->len)
|
||||
: write(io->FD, io->Buf->str, io->Buf->len);
|
||||
if (St < 0) {
|
||||
/* Error */
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
} else if (errno == EAGAIN) {
|
||||
ret = TRUE;
|
||||
break;
|
||||
} else {
|
||||
if (conn) {
|
||||
io->Status = St;
|
||||
break;
|
||||
} else {
|
||||
io->Status = errno;
|
||||
MSG("WRITE Failed with %d: %s\n", (int)St, strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (St < io->Buf->len) {
|
||||
/* Not all data written */
|
||||
dStr_erase (io->Buf, 0, St);
|
||||
} else {
|
||||
/* All data in buffer written */
|
||||
dStr_truncate(io->Buf, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle background IO for a given FD (reads | writes).
|
||||
* (This function gets called when there's activity in the FD)
|
||||
*/
|
||||
static int IO_callback(IOData_t *io)
|
||||
{
|
||||
bool_t ret = FALSE;
|
||||
|
||||
_MSG("IO_callback:: (%s) FD = %d\n",
|
||||
(io->Op == IORead) ? "IORead" : "IOWrite", io->FD);
|
||||
|
||||
if (io->Op == IORead) { /* Read */
|
||||
ret = IO_read(io);
|
||||
} else if (io->Op == IOWrite) { /* Write */
|
||||
ret = IO_write(io);
|
||||
}
|
||||
return (ret) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the READ event of a FD.
|
||||
*/
|
||||
static void IO_fd_read_cb(int fd, void *data)
|
||||
{
|
||||
int io_key = VOIDP2INT(data);
|
||||
IOData_t *io = IO_get(io_key);
|
||||
|
||||
/* There should be no more events on already closed FDs --Jcid */
|
||||
if (io == NULL) {
|
||||
MSG_ERR("IO_fd_read_cb: call on already closed io!\n");
|
||||
a_IOwatch_remove_fd(fd, DIO_READ);
|
||||
|
||||
} else {
|
||||
if (IO_callback(io) == 0)
|
||||
a_IOwatch_remove_fd(fd, DIO_READ);
|
||||
if ((io = IO_get(io_key)) && io->Status) {
|
||||
/* check io because IO_read OpSend could trigger abort */
|
||||
a_IO_ccc(OpAbort, 2, FWD, io->Info, io, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the WRITE event of a FD.
|
||||
*/
|
||||
static void IO_fd_write_cb(int fd, void *data)
|
||||
{
|
||||
int io_key = VOIDP2INT(data);
|
||||
IOData_t *io = IO_get(io_key);
|
||||
|
||||
if (io == NULL) {
|
||||
/* There must be no more events on already closed FDs --Jcid */
|
||||
MSG_ERR("IO_fd_write_cb: call on already closed io!\n");
|
||||
a_IOwatch_remove_fd(fd, DIO_WRITE);
|
||||
|
||||
} else {
|
||||
if (IO_callback(io) == 0)
|
||||
a_IOwatch_remove_fd(fd, DIO_WRITE);
|
||||
if (io->Status)
|
||||
a_IO_ccc(OpAbort, 1, FWD, io->Info, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive an IO request (IORead | IOWrite),
|
||||
* Set a watch for it, and let it flow!
|
||||
*/
|
||||
static void IO_submit(IOData_t *r_io)
|
||||
{
|
||||
if (r_io->FD < 0) {
|
||||
MSG_ERR("IO_submit: FD not initialized\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Insert this IO in ValidIOs */
|
||||
IO_ins(r_io);
|
||||
|
||||
_MSG("IO_submit:: (%s) FD = %d\n",
|
||||
(r_io->Op == IORead) ? "IORead" : "IOWrite", r_io->FD);
|
||||
|
||||
/* Set FD to background and to close on exec. */
|
||||
fcntl(r_io->FD, F_SETFL, O_NONBLOCK | fcntl(r_io->FD, F_GETFL));
|
||||
fcntl(r_io->FD, F_SETFD, FD_CLOEXEC | fcntl(r_io->FD, F_GETFD));
|
||||
|
||||
if (r_io->Op == IORead) {
|
||||
a_IOwatch_add_fd(r_io->FD, DIO_READ,
|
||||
IO_fd_read_cb, INT2VOIDP(r_io->Key));
|
||||
|
||||
} else if (r_io->Op == IOWrite) {
|
||||
a_IOwatch_add_fd(r_io->FD, DIO_WRITE,
|
||||
IO_fd_write_cb, INT2VOIDP(r_io->Key));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CCC function for the IO module.
|
||||
* ( Data1 = IOData_t* ; Data2 = NULL )
|
||||
*/
|
||||
void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
|
||||
void *Data1, void *Data2)
|
||||
{
|
||||
IOData_t *io;
|
||||
DataBuf *dbuf;
|
||||
|
||||
dReturn_if_fail( a_Chain_check("a_IO_ccc", Op, Branch, Dir, Info) );
|
||||
|
||||
if (Branch == 1) {
|
||||
if (Dir == BCK) {
|
||||
/* Write data using select */
|
||||
switch (Op) {
|
||||
case OpStart:
|
||||
io = IO_new(IOWrite);
|
||||
io->Info = Info;
|
||||
Info->LocalKey = io;
|
||||
break;
|
||||
case OpSend:
|
||||
io = Info->LocalKey;
|
||||
if (Data2 && !strcmp(Data2, "FD")) {
|
||||
io->FD = *(int*)Data1; /* SockFD */
|
||||
} else {
|
||||
dbuf = Data1;
|
||||
dStr_append_l(io->Buf, dbuf->Buf, dbuf->Size);
|
||||
IO_submit(io);
|
||||
}
|
||||
break;
|
||||
case OpEnd:
|
||||
case OpAbort:
|
||||
io = Info->LocalKey;
|
||||
if (io->Buf->len > 0) {
|
||||
char *newline = memchr(io->Buf->str, '\n', io->Buf->len);
|
||||
int msglen = newline ? newline - io->Buf->str : 2048;
|
||||
|
||||
MSG("IO_write, closing with pending data not sent: \"%s\"\n",
|
||||
dStr_printable(io->Buf, msglen));
|
||||
}
|
||||
/* close FD, remove from ValidIOs and remove its watch */
|
||||
IO_close_fd(io, Op == OpEnd ? IO_StopWr : IO_StopRdWr);
|
||||
IO_free(io);
|
||||
dFree(Info);
|
||||
break;
|
||||
default:
|
||||
MSG_WARN("Unused CCC IO 1B\n");
|
||||
break;
|
||||
}
|
||||
} else { /* 1 FWD */
|
||||
/* Write-data status */
|
||||
switch (Op) {
|
||||
case OpAbort:
|
||||
io = Info->LocalKey;
|
||||
IO_close_fd(io, IO_StopRdWr);
|
||||
IO_free(io);
|
||||
a_Chain_fcb(OpAbort, Info, NULL, NULL);
|
||||
dFree(Info);
|
||||
break;
|
||||
default:
|
||||
MSG_WARN("Unused CCC IO 1F\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (Branch == 2) {
|
||||
if (Dir == BCK) {
|
||||
/* This part catches the reader's messages */
|
||||
switch (Op) {
|
||||
case OpStart:
|
||||
io = IO_new(IORead);
|
||||
Info->LocalKey = io;
|
||||
io->Info = Info;
|
||||
break;
|
||||
case OpSend:
|
||||
io = Info->LocalKey;
|
||||
if (Data2 && !strcmp(Data2, "FD")) {
|
||||
io->FD = *(int*)Data1; /* SockFD */
|
||||
IO_submit(io);
|
||||
}
|
||||
break;
|
||||
case OpEnd:
|
||||
case OpAbort:
|
||||
io = Info->LocalKey;
|
||||
IO_close_fd(io, Op == OpEnd ? IO_StopRd : IO_StopRdWr);
|
||||
IO_free(io);
|
||||
dFree(Info);
|
||||
break;
|
||||
default:
|
||||
MSG_WARN("Unused CCC IO 2B\n");
|
||||
break;
|
||||
}
|
||||
} else { /* 2 FWD */
|
||||
/* Send read-data */
|
||||
io = Data1;
|
||||
switch (Op) {
|
||||
case OpSend:
|
||||
dbuf = a_Chain_dbuf_new(io->Buf->str, io->Buf->len, 0);
|
||||
a_Chain_fcb(OpSend, Info, dbuf, NULL);
|
||||
dFree(dbuf);
|
||||
break;
|
||||
case OpEnd:
|
||||
case OpAbort:
|
||||
a_Chain_fcb(Op, Info, NULL, NULL);
|
||||
IO_close_fd(io, IO_StopRdWr); /* IO_StopRd would leak FDs */
|
||||
IO_free(io);
|
||||
dFree(Info);
|
||||
break;
|
||||
default:
|
||||
MSG_WARN("Unused CCC IO 2F\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
35
src/IO/IO.h
Normal file
35
src/IO/IO.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef __IO_H__
|
||||
#define __IO_H__
|
||||
|
||||
#include "d_size.h"
|
||||
#include "../../dlib/dlib.h"
|
||||
#include "../chain.h"
|
||||
|
||||
/*
|
||||
* IO Operations
|
||||
*/
|
||||
#define IORead 0
|
||||
#define IOWrite 1
|
||||
#define IOClose 2
|
||||
#define IOAbort 3
|
||||
|
||||
/*
|
||||
* IO constants
|
||||
*/
|
||||
#define IOBufLen 8192
|
||||
|
||||
|
||||
/*
|
||||
* Exported functions
|
||||
*/
|
||||
/* Note: a_IO_ccc() is defined in Url.h together with the *_ccc() set */
|
||||
|
||||
|
||||
/*
|
||||
* Exported data
|
||||
*/
|
||||
extern const char *AboutSplash;
|
||||
|
||||
|
||||
#endif /* __IO_H__ */
|
||||
|
||||
39
src/IO/Makefile.am
Normal file
39
src/IO/Makefile.am
Normal file
@ -0,0 +1,39 @@
|
||||
AM_CPPFLAGS = \
|
||||
-I$(top_srcdir) \
|
||||
-DDILLO_BINDIR='"$(bindir)/"' \
|
||||
-DCA_CERTS_FILE='"@CA_CERTS_FILE@"' \
|
||||
-DCA_CERTS_DIR='"@CA_CERTS_DIR@"'
|
||||
|
||||
AM_CFLAGS = @LIBFLTK_CFLAGS@
|
||||
AM_CXXFLAGS = @LIBFLTK_CXXFLAGS@
|
||||
|
||||
noinst_LIBRARIES = libDiof.a
|
||||
|
||||
if USE_OPENSSL
|
||||
TLS_OPENSSL = tls_openssl.c tls_openssl.h
|
||||
else
|
||||
TLS_OPENSSL =
|
||||
endif
|
||||
|
||||
if USE_MBEDTLS
|
||||
TLS_MBEDTLS = tls_mbedtls.c tls_mbedtls.h
|
||||
else
|
||||
TLS_MBEDTLS =
|
||||
endif
|
||||
|
||||
|
||||
libDiof_a_SOURCES = \
|
||||
mime.c \
|
||||
mime.h \
|
||||
about.c \
|
||||
Url.h \
|
||||
http.c \
|
||||
tls.h \
|
||||
tls.c \
|
||||
$(TLS_OPENSSL) \
|
||||
$(TLS_MBEDTLS) \
|
||||
dpi.c \
|
||||
IO.c \
|
||||
iowatch.cc \
|
||||
iowatch.hh \
|
||||
IO.h
|
||||
38
src/IO/Url.h
Normal file
38
src/IO/Url.h
Normal file
@ -0,0 +1,38 @@
|
||||
#ifndef __IO_URL_H__
|
||||
#define __IO_URL_H__
|
||||
|
||||
#include "../chain.h"
|
||||
#include "../url.h"
|
||||
#include "../../dlib/dlib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* External functions
|
||||
*/
|
||||
extern void a_Http_freeall(void);
|
||||
int a_Http_init(void);
|
||||
int a_Http_proxy_auth(void);
|
||||
void a_Http_set_proxy_passwd(const char *str);
|
||||
void a_Http_connect_done(int fd, bool_t success);
|
||||
|
||||
void a_Http_ccc (int Op, int Branch, int Dir, ChainLink *Info,
|
||||
void *Data1, void *Data2);
|
||||
void a_IO_ccc (int Op, int Branch, int Dir, ChainLink *Info,
|
||||
void *Data1, void *Data2);
|
||||
void a_Dpi_ccc (int Op, int Branch, int Dir, ChainLink *Info,
|
||||
void *Data1, void *Data2);
|
||||
|
||||
char *a_Dpi_send_blocking_cmd(const char *server_name, const char *cmd);
|
||||
void a_Dpi_dillo_exit(void);
|
||||
void a_Dpi_init(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __IO_URL_H__ */
|
||||
|
||||
59
src/IO/about.c
Normal file
59
src/IO/about.c
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* File: about.c
|
||||
*
|
||||
* Copyright (C) 1999-2007 Jorge Arellano Cid <jcid@dillo.org>
|
||||
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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 <config.h>
|
||||
|
||||
/**
|
||||
* HTML text for startup screen
|
||||
*/
|
||||
const char *const AboutSplash=
|
||||
"<!DOCTYPE HTML>\n"
|
||||
"<html>\n"
|
||||
"<head>\n"
|
||||
" <title>Quickstart</title>\n"
|
||||
" <style>\n"
|
||||
" body {\n"
|
||||
" background: white;\n"
|
||||
" margin: 3em;\n"
|
||||
" font-size: 16px;\n"
|
||||
" font-family: sans-serif;\n"
|
||||
" line-height: 1.4em;\n"
|
||||
" }\n"
|
||||
" .main { max-width: 40em; }\n"
|
||||
" p { margin-top: 1em; }\n"
|
||||
" ul { margin-left: 1em; }\n"
|
||||
" li { margin-top: 0.5em; }\n"
|
||||
" </style>\n"
|
||||
"</head>\n"
|
||||
"<body>\n"
|
||||
"<div class=\"main\">\n"
|
||||
"\n"
|
||||
"<h1>Quickstart</h1>\n"
|
||||
"\n"
|
||||
"<p>Welcome to Dillo " VERSION ", a small and fast graphical web browser. To\n"
|
||||
"access the help click the question mark button <code>?</code> in the top\n"
|
||||
"right corner at any time. Here are some tips to get you started:</p>\n"
|
||||
"\n"
|
||||
"<ul>\n"
|
||||
" <li>The main configuration file is at <code>~/.dillo/dillorc</code>.</li>\n"
|
||||
" <li>Most actions can also be done by using the <em>keyboard</em>.</li>\n"
|
||||
" <li>Cookies are <em>disabled by default</em>.</li>\n"
|
||||
" <li>Several Dillo plugins are available.</li>\n"
|
||||
"</ul>\n"
|
||||
"\n"
|
||||
"<p>See more details in the\n"
|
||||
"<a href=\"https://dillo-browser.github.io/\">Dillo website</a>.</p>\n"
|
||||
"\n"
|
||||
"</div>\n"
|
||||
"</body>\n"
|
||||
"</html>\n";
|
||||
|
||||
791
src/IO/dpi.c
Normal file
791
src/IO/dpi.c
Normal file
@ -0,0 +1,791 @@
|
||||
/*
|
||||
* File: dpi.c
|
||||
*
|
||||
* Copyright (C) 2002-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.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* Dillo plugins (small programs that interact with dillo).
|
||||
*
|
||||
* Dillo plugins are designed to handle:
|
||||
* bookmarks, cookies, FTP, downloads, files, preferences, https,
|
||||
* datauri and a lot of any-to-html filters.
|
||||
*/
|
||||
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h> /* for errno */
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h> /* isxdigit */
|
||||
#include <stdint.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include "../msg.h"
|
||||
#include "../klist.h"
|
||||
#include "IO.h"
|
||||
#include "Url.h"
|
||||
#include "../../dpip/dpip.h"
|
||||
#include "dlib/dlib.h"
|
||||
|
||||
/* This one is tricky, some sources state it should include the byte
|
||||
* for the terminating NULL, and others say it shouldn't. */
|
||||
# define D_SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
|
||||
+ strlen ((ptr)->sun_path))
|
||||
|
||||
/* Solaris may not have this one... */
|
||||
#ifndef AF_LOCAL
|
||||
#define AF_LOCAL AF_UNIX
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int InTag;
|
||||
int Send2EOF;
|
||||
|
||||
int DataTotalSize;
|
||||
int DataRecvSize;
|
||||
|
||||
Dstr *Buf;
|
||||
|
||||
int BufIdx;
|
||||
int TokIdx;
|
||||
int TokSize;
|
||||
int TokIsTag;
|
||||
|
||||
ChainLink *InfoRecv;
|
||||
int Key;
|
||||
} dpi_conn_t;
|
||||
|
||||
|
||||
/*
|
||||
* Local data
|
||||
*/
|
||||
static Klist_t *ValidConns = NULL; /* Active connections list. It holds
|
||||
* pointers to dpi_conn_t structures. */
|
||||
static char SharedKey[32];
|
||||
|
||||
/*
|
||||
* Initialize local data
|
||||
*/
|
||||
void a_Dpi_init(void)
|
||||
{
|
||||
/* empty */
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new connection data structure
|
||||
*/
|
||||
static dpi_conn_t *Dpi_conn_new(ChainLink *Info)
|
||||
{
|
||||
dpi_conn_t *conn = dNew0(dpi_conn_t, 1);
|
||||
|
||||
conn->Buf = dStr_sized_new(8*1024);
|
||||
conn->InfoRecv = Info;
|
||||
conn->Key = a_Klist_insert(&ValidConns, conn);
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a connection data structure
|
||||
*/
|
||||
static void Dpi_conn_free(dpi_conn_t *conn)
|
||||
{
|
||||
a_Klist_remove(ValidConns, conn->Key);
|
||||
dStr_free(conn->Buf, 1);
|
||||
dFree(conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a conn is still valid.
|
||||
* Return: 1 if found, 0 otherwise
|
||||
*/
|
||||
static int Dpi_conn_valid(int key)
|
||||
{
|
||||
return (a_Klist_get_data(ValidConns, key)) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the new buffer in 'dbuf' to Buf in 'conn'
|
||||
*/
|
||||
static void Dpi_append_dbuf(dpi_conn_t *conn, DataBuf *dbuf)
|
||||
{
|
||||
if (dbuf->Code == 0 && dbuf->Size > 0) {
|
||||
dStr_append_l(conn->Buf, dbuf->Buf, dbuf->Size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the data stream into tokens.
|
||||
* Here, a token is either:
|
||||
* a) a dpi tag
|
||||
* b) a raw data chunk
|
||||
*
|
||||
* Return Value: 0 upon a new token, -1 on not enough data.
|
||||
*
|
||||
* TODO: define an API and move this function into libDpip.a.
|
||||
*/
|
||||
static int Dpi_get_token(dpi_conn_t *conn)
|
||||
{
|
||||
int i, resp = -1;
|
||||
char *buf = conn->Buf->str;
|
||||
|
||||
if (conn->BufIdx == conn->Buf->len) {
|
||||
dStr_truncate(conn->Buf, 0);
|
||||
conn->BufIdx = 0;
|
||||
return resp;
|
||||
}
|
||||
|
||||
if (conn->Send2EOF) {
|
||||
conn->TokIdx = conn->BufIdx;
|
||||
conn->TokSize = conn->Buf->len - conn->BufIdx;
|
||||
conn->BufIdx = conn->Buf->len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_MSG("conn->BufIdx = %d; conn->Buf->len = %d\nbuf: [%s]\n",
|
||||
conn->BufIdx,conn->Buf->len, conn->Buf->str + conn->BufIdx);
|
||||
|
||||
if (!conn->InTag) {
|
||||
/* search for start of tag */
|
||||
while (conn->BufIdx < conn->Buf->len && buf[conn->BufIdx] != '<')
|
||||
++conn->BufIdx;
|
||||
if (conn->BufIdx < conn->Buf->len) {
|
||||
/* found */
|
||||
conn->InTag = 1;
|
||||
conn->TokIdx = conn->BufIdx;
|
||||
} else {
|
||||
MSG_ERR("[Dpi_get_token] Can't find token start\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (conn->InTag) {
|
||||
/* search for end of tag (EOT=" '>") */
|
||||
for (i = conn->BufIdx; i < conn->Buf->len; ++i)
|
||||
if (buf[i] == '>' && i >= 2 && buf[i-1] == '\'' && buf[i-2] == ' ')
|
||||
break;
|
||||
conn->BufIdx = i;
|
||||
|
||||
if (conn->BufIdx < conn->Buf->len) {
|
||||
/* found EOT */
|
||||
conn->TokIsTag = 1;
|
||||
conn->TokSize = conn->BufIdx - conn->TokIdx + 1;
|
||||
++conn->BufIdx;
|
||||
conn->InTag = 0;
|
||||
resp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a dpi tag and take the appropriate actions
|
||||
*/
|
||||
static void Dpi_parse_token(dpi_conn_t *conn)
|
||||
{
|
||||
char *tag, *cmd, *msg, *urlstr;
|
||||
DataBuf *dbuf;
|
||||
char *Tok = conn->Buf->str + conn->TokIdx;
|
||||
|
||||
if (conn->Send2EOF) {
|
||||
/* we're receiving data chunks from a HTML page */
|
||||
dbuf = a_Chain_dbuf_new(Tok, conn->TokSize, 0);
|
||||
a_Chain_fcb(OpSend, conn->InfoRecv, dbuf, "send_page_2eof");
|
||||
dFree(dbuf);
|
||||
return;
|
||||
}
|
||||
|
||||
tag = dStrndup(Tok, (size_t)conn->TokSize);
|
||||
_MSG("Dpi_parse_token: {%s}\n", tag);
|
||||
|
||||
cmd = a_Dpip_get_attr_l(Tok, conn->TokSize, "cmd");
|
||||
if (strcmp(cmd, "send_status_message") == 0) {
|
||||
msg = a_Dpip_get_attr_l(Tok, conn->TokSize, "msg");
|
||||
a_Chain_fcb(OpSend, conn->InfoRecv, msg, cmd);
|
||||
dFree(msg);
|
||||
|
||||
} else if (strcmp(cmd, "chat") == 0) {
|
||||
msg = a_Dpip_get_attr_l(Tok, conn->TokSize, "msg");
|
||||
a_Chain_fcb(OpSend, conn->InfoRecv, msg, cmd);
|
||||
dFree(msg);
|
||||
|
||||
} else if (strcmp(cmd, "dialog") == 0) {
|
||||
/* For now will send the dpip tag... */
|
||||
a_Chain_fcb(OpSend, conn->InfoRecv, tag, cmd);
|
||||
|
||||
} else if (strcmp(cmd, "start_send_page") == 0) {
|
||||
conn->Send2EOF = 1;
|
||||
urlstr = a_Dpip_get_attr_l(Tok, conn->TokSize, "url");
|
||||
a_Chain_fcb(OpSend, conn->InfoRecv, urlstr, cmd);
|
||||
dFree(urlstr);
|
||||
/* TODO: a_Dpip_get_attr_l(Tok, conn->TokSize, "send_mode") */
|
||||
|
||||
} else if (strcmp(cmd, "reload_request") == 0) {
|
||||
urlstr = a_Dpip_get_attr_l(Tok, conn->TokSize, "url");
|
||||
a_Chain_fcb(OpSend, conn->InfoRecv, urlstr, cmd);
|
||||
dFree(urlstr);
|
||||
}
|
||||
dFree(cmd);
|
||||
|
||||
dFree(tag);
|
||||
}
|
||||
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
/**
|
||||
* Write data into a file descriptor taking care of EINTR
|
||||
* and possible data splits.
|
||||
* Return value: 1 on success, -1 on error.
|
||||
*/
|
||||
static int Dpi_blocking_write(int fd, const char *msg, int msg_len)
|
||||
{
|
||||
int st, sent = 0;
|
||||
|
||||
while (sent < msg_len) {
|
||||
st = write(fd, msg + sent, msg_len - sent);
|
||||
if (st < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
} else {
|
||||
MSG_ERR("[Dpi_blocking_write] %s\n", dStrerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
sent += st;
|
||||
}
|
||||
|
||||
return (sent == msg_len) ? 1 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read all the available data from a filedescriptor.
|
||||
* This is intended for short answers, i.e. when we know the server
|
||||
* will write it all before being preempted. For answers that may come
|
||||
* as an stream with delays, non-blocking is better.
|
||||
* Return value: read data, or NULL on error and no data.
|
||||
*/
|
||||
static char *Dpi_blocking_read(int fd)
|
||||
{
|
||||
int st;
|
||||
const int buf_sz = 8*1024;
|
||||
char buf[buf_sz], *msg = NULL;
|
||||
Dstr *dstr = dStr_sized_new(buf_sz);
|
||||
|
||||
do {
|
||||
st = read(fd, buf, buf_sz);
|
||||
if (st < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
} else {
|
||||
MSG_ERR("[Dpi_blocking_read] %s\n", dStrerror(errno));
|
||||
break;
|
||||
}
|
||||
} else if (st > 0) {
|
||||
dStr_append_l(dstr, buf, st);
|
||||
}
|
||||
} while (st == buf_sz);
|
||||
|
||||
msg = (dstr->len > 0) ? dstr->str : NULL;
|
||||
dStr_free(dstr, (dstr->len > 0) ? FALSE : TRUE);
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
/**
|
||||
* Get a new data buffer (within a 'dbuf'), save it into local data,
|
||||
* split in tokens and parse the contents.
|
||||
*/
|
||||
static void Dpi_process_dbuf(int Op, void *Data1, dpi_conn_t *conn)
|
||||
{
|
||||
DataBuf *dbuf = Data1;
|
||||
int key = conn->Key;
|
||||
|
||||
/* Very useful for debugging: show the data stream as received. */
|
||||
/* fwrite(dbuf->Buf, dbuf->Size, 1, stdout); */
|
||||
|
||||
if (Op == IORead) {
|
||||
Dpi_append_dbuf(conn, dbuf);
|
||||
/* 'conn' has to be validated because Dpi_parse_token() MAY call abort */
|
||||
while (Dpi_conn_valid(key) && Dpi_get_token(conn) != -1) {
|
||||
Dpi_parse_token(conn);
|
||||
}
|
||||
|
||||
} else if (Op == IOClose) {
|
||||
/* unused */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start dpid.
|
||||
* Return: 0 starting now, 1 Error.
|
||||
*/
|
||||
static int Dpi_start_dpid(void)
|
||||
{
|
||||
pid_t pid;
|
||||
int st_pipe[2], ret = 1;
|
||||
char *answer;
|
||||
|
||||
/* create a pipe to track our child's status */
|
||||
if (pipe(st_pipe))
|
||||
return 1;
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
/* This is the child process. Execute the command. */
|
||||
char *path1 = dStrconcat(dGethomedir(), "/.dillo/dpid", NULL);
|
||||
dClose(st_pipe[0]);
|
||||
if (execl(path1, "dpid", (char*)NULL) == -1) {
|
||||
dFree(path1);
|
||||
path1 = dStrconcat(DILLO_BINDIR, "dpid", NULL);
|
||||
if (execl(path1, "dpid", (char*)NULL) == -1) {
|
||||
dFree(path1);
|
||||
if (execlp("dpid", "dpid", (char*)NULL) == -1) {
|
||||
MSG("Dpi_start_dpid (child): %s\n", dStrerror(errno));
|
||||
if (Dpi_blocking_write(st_pipe[1], "ERROR", 5) == -1) {
|
||||
MSG("Dpi_start_dpid (child): can't write to pipe.\n");
|
||||
}
|
||||
dClose(st_pipe[1]);
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (pid < 0) {
|
||||
/* The fork failed. Report failure. */
|
||||
MSG("Dpi_start_dpid: %s\n", dStrerror(errno));
|
||||
/* close the unused pipe */
|
||||
dClose(st_pipe[0]);
|
||||
dClose(st_pipe[1]);
|
||||
} else {
|
||||
/* This is the parent process, check our child status... */
|
||||
dClose(st_pipe[1]);
|
||||
if ((answer = Dpi_blocking_read(st_pipe[0])) != NULL) {
|
||||
MSG("Dpi_start_dpid: can't start dpid\n");
|
||||
dFree(answer);
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
dClose(st_pipe[0]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read dpid's communication keys from its saved file.
|
||||
* Return value: 1 on success, -1 on error.
|
||||
*/
|
||||
static int Dpi_read_comm_keys(int *port)
|
||||
{
|
||||
FILE *In;
|
||||
char *fname, *rcline = NULL, *tail;
|
||||
int i, ret = -1;
|
||||
|
||||
fname = dStrconcat(dGethomedir(), "/.dillo/dpid_comm_keys", NULL);
|
||||
if ((In = fopen(fname, "r")) == NULL) {
|
||||
MSG_ERR("[Dpi_read_comm_keys] %s\n", dStrerror(errno));
|
||||
} else if ((rcline = dGetline(In)) == NULL) {
|
||||
MSG_ERR("[Dpi_read_comm_keys] empty file: %s\n", fname);
|
||||
} else {
|
||||
*port = strtol(rcline, &tail, 10);
|
||||
for (i = 0; *tail && isxdigit(tail[i+1]); ++i)
|
||||
SharedKey[i] = tail[i+1];
|
||||
SharedKey[i] = 0;
|
||||
ret = 1;
|
||||
}
|
||||
if (In)
|
||||
fclose(In);
|
||||
dFree(rcline);
|
||||
dFree(fname);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a socket file descriptor
|
||||
*/
|
||||
static int Dpi_make_socket_fd(void)
|
||||
{
|
||||
int fd, one = 1, ret = -1;
|
||||
|
||||
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
|
||||
/* avoid delays when sending small pieces of data */
|
||||
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
|
||||
ret = fd;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a connection test for a IDS.
|
||||
* Return: 1 OK, -1 Not working.
|
||||
*/
|
||||
static int Dpi_check_dpid_ids(void)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
const socklen_t sin_sz = sizeof(sin);
|
||||
int sock_fd, dpid_port, ret = -1;
|
||||
|
||||
/* socket connection test */
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
|
||||
if (Dpi_read_comm_keys(&dpid_port) != -1) {
|
||||
sin.sin_port = htons(dpid_port);
|
||||
if ((sock_fd = Dpi_make_socket_fd()) == -1) {
|
||||
MSG("Dpi_check_dpid_ids: sock_fd=%d %s\n", sock_fd, dStrerror(errno));
|
||||
} else if (connect(sock_fd, (struct sockaddr *)&sin, sin_sz) == -1) {
|
||||
MSG("Dpi_check_dpid_ids: %s\n", dStrerror(errno));
|
||||
} else {
|
||||
dClose(sock_fd);
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm that the dpid is running. If not, start it.
|
||||
* Return: 0 running OK, 1 starting (EAGAIN), 2 Error.
|
||||
*/
|
||||
static int Dpi_check_dpid(int num_tries)
|
||||
{
|
||||
static int starting = 0;
|
||||
int check_st = 1, ret = 2;
|
||||
|
||||
check_st = Dpi_check_dpid_ids();
|
||||
_MSG("Dpi_check_dpid: check_st=%d\n", check_st);
|
||||
|
||||
if (check_st == 1) {
|
||||
/* connection test with dpi server passed */
|
||||
starting = 0;
|
||||
ret = 0;
|
||||
} else {
|
||||
if (!starting) {
|
||||
/* start dpid */
|
||||
if (Dpi_start_dpid() == 0) {
|
||||
starting = 1;
|
||||
ret = 1;
|
||||
}
|
||||
} else if (++starting < num_tries) {
|
||||
/* starting */
|
||||
ret = 1;
|
||||
} else {
|
||||
/* we waited too much, report an error... */
|
||||
starting = 0;
|
||||
}
|
||||
}
|
||||
|
||||
_MSG("Dpi_check_dpid: %s\n",
|
||||
(ret == 0) ? "OK" : (ret == 1 ? "EAGAIN" : "ERROR"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm that the dpid is running. If not, start it.
|
||||
* Return: 0 running OK, 2 Error.
|
||||
*/
|
||||
static int Dpi_blocking_start_dpid(void)
|
||||
{
|
||||
int cst, try = 0,
|
||||
n_tries = 12; /* 3 seconds */
|
||||
|
||||
/* test the dpid, and wait a bit for it to start if necessary */
|
||||
while ((cst = Dpi_check_dpid(n_tries)) == 1) {
|
||||
MSG("Dpi_blocking_start_dpid: try %d\n", ++try);
|
||||
dUsleep(250000UL);
|
||||
}
|
||||
return cst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the dpi server's port number, or -1 on error.
|
||||
* (A query is sent to dpid and then its answer parsed)
|
||||
* note: as the available servers and/or the dpi socket directory can
|
||||
* change at any time, we'll ask each time. If someday we find
|
||||
* that connecting each time significantly degrades performance,
|
||||
* an optimized approach can be tried.
|
||||
*/
|
||||
static int Dpi_get_server_port(const char *server_name)
|
||||
{
|
||||
int sock_fd = -1, dpi_port = -1;
|
||||
int dpid_port, ok = 0;
|
||||
struct sockaddr_in sin;
|
||||
char *cmd, *request, *rply = NULL, *port_str;
|
||||
socklen_t sin_sz;
|
||||
|
||||
dReturn_val_if_fail (server_name != NULL, dpi_port);
|
||||
_MSG("Dpi_get_server_port: server_name = [%s]\n", server_name);
|
||||
|
||||
/* Read dpid's port from saved file */
|
||||
if (Dpi_read_comm_keys(&dpid_port) != -1) {
|
||||
ok = 1;
|
||||
}
|
||||
if (ok) {
|
||||
/* Connect a socket with dpid */
|
||||
ok = 0;
|
||||
sin_sz = sizeof(sin);
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
sin.sin_port = htons(dpid_port);
|
||||
if ((sock_fd = Dpi_make_socket_fd()) == -1 ||
|
||||
connect(sock_fd, (struct sockaddr *)&sin, sin_sz) == -1) {
|
||||
MSG("Dpi_get_server_port: %s\n", dStrerror(errno));
|
||||
} else {
|
||||
ok = 1;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
/* ask dpid to check the dpi and send its port number back */
|
||||
ok = 0;
|
||||
request = a_Dpip_build_cmd("cmd=%s msg=%s", "check_server", server_name);
|
||||
_MSG("[%s]\n", request);
|
||||
|
||||
if (Dpi_blocking_write(sock_fd, request, strlen(request)) == -1) {
|
||||
MSG("Dpi_get_server_port: %s\n", dStrerror(errno));
|
||||
} else {
|
||||
ok = 1;
|
||||
}
|
||||
dFree(request);
|
||||
}
|
||||
if (ok) {
|
||||
/* Get the reply */
|
||||
ok = 0;
|
||||
if ((rply = Dpi_blocking_read(sock_fd)) == NULL) {
|
||||
MSG("Dpi_get_server_port: can't read server port from dpid.\n");
|
||||
} else {
|
||||
ok = 1;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
/* Parse reply */
|
||||
ok = 0;
|
||||
cmd = a_Dpip_get_attr(rply, "cmd");
|
||||
if (strcmp(cmd, "send_data") == 0) {
|
||||
port_str = a_Dpip_get_attr(rply, "msg");
|
||||
_MSG("Dpi_get_server_port: rply=%s\n", rply);
|
||||
_MSG("Dpi_get_server_port: port_str=%s\n", port_str);
|
||||
dpi_port = strtol(port_str, NULL, 10);
|
||||
dFree(port_str);
|
||||
ok = 1;
|
||||
}
|
||||
dFree(cmd);
|
||||
}
|
||||
dFree(rply);
|
||||
dClose(sock_fd);
|
||||
|
||||
return ok ? dpi_port : -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connect a socket to a dpi server and return the socket's FD.
|
||||
* We have to ask 'dpid' (dpi daemon) for the port of the target dpi server.
|
||||
* Once we have it, then the proper file descriptor is returned (-1 on error).
|
||||
*/
|
||||
static int Dpi_connect_socket(const char *server_name)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
int sock_fd, dpi_port, ret = -1;
|
||||
char *cmd = NULL;
|
||||
|
||||
/* Query dpid for the port number for this server */
|
||||
if ((dpi_port = Dpi_get_server_port(server_name)) == -1) {
|
||||
_MSG("Dpi_connect_socket: can't get port number for %s\n", server_name);
|
||||
return -1;
|
||||
}
|
||||
_MSG("Dpi_connect_socket: server=%s port=%d\n", server_name, dpi_port);
|
||||
|
||||
/* connect with this server's socket */
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
sin.sin_port = htons(dpi_port);
|
||||
|
||||
if ((sock_fd = Dpi_make_socket_fd()) == -1) {
|
||||
MSG_ERR("[Dpi_connect_socket] %s\n", dStrerror(errno));
|
||||
} else if (connect(sock_fd, (void*)&sin, sizeof(sin)) == -1) {
|
||||
MSG_ERR("[Dpi_connect_socket] errno:%d %s\n", errno, dStrerror(errno));
|
||||
|
||||
/* send authentication Key (the server closes sock_fd on auth error) */
|
||||
} else if (!(cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "auth", SharedKey))) {
|
||||
MSG_ERR("[Dpi_connect_socket] Can't make auth message.\n");
|
||||
} else if (Dpi_blocking_write(sock_fd, cmd, strlen(cmd)) == -1) {
|
||||
MSG_ERR("[Dpi_connect_socket] Can't send auth message.\n");
|
||||
} else {
|
||||
ret = sock_fd;
|
||||
}
|
||||
dFree(cmd);
|
||||
if (sock_fd != -1 && ret == -1) /* can't send cmd? */
|
||||
dClose(sock_fd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* CCC function for the Dpi module.
|
||||
*/
|
||||
void a_Dpi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
|
||||
void *Data1, void *Data2)
|
||||
{
|
||||
dpi_conn_t *conn;
|
||||
int SockFD = -1, st;
|
||||
|
||||
dReturn_if_fail( a_Chain_check("a_Dpi_ccc", Op, Branch, Dir, Info) );
|
||||
|
||||
if (Branch == 1) {
|
||||
if (Dir == BCK) {
|
||||
/* Send commands to dpi-server */
|
||||
switch (Op) {
|
||||
case OpStart:
|
||||
if ((st = Dpi_blocking_start_dpid()) == 0) {
|
||||
if ((SockFD = Dpi_connect_socket(Data1)) != -1) {
|
||||
int *fd = dNew(int, 1);
|
||||
*fd = SockFD;
|
||||
Info->LocalKey = fd;
|
||||
a_Chain_link_new(Info, a_Dpi_ccc, BCK, a_IO_ccc, 1, 1);
|
||||
a_Chain_bcb(OpStart, Info, NULL, NULL);
|
||||
/* Let the FD known and tracked */
|
||||
a_Chain_bcb(OpSend, Info, &SockFD, "FD");
|
||||
a_Chain_fcb(OpSend, Info, &SockFD, "FD");
|
||||
a_Chain_fcb(OpSend, Info, NULL, "DpidOK");
|
||||
} else {
|
||||
a_Dpi_ccc(OpAbort, 1, FWD, Info, NULL, NULL);
|
||||
}
|
||||
} else {
|
||||
MSG_ERR("dpi.c: can't start dpi daemon\n");
|
||||
a_Dpi_ccc(OpAbort, 1, FWD, Info, NULL, "DpidERROR");
|
||||
}
|
||||
break;
|
||||
case OpSend:
|
||||
a_Chain_bcb(OpSend, Info, Data1, NULL);
|
||||
break;
|
||||
case OpEnd:
|
||||
a_Chain_bcb(OpEnd, Info, NULL, NULL);
|
||||
dFree(Info->LocalKey);
|
||||
dFree(Info);
|
||||
break;
|
||||
case OpAbort:
|
||||
a_Chain_bcb(OpAbort, Info, NULL, NULL);
|
||||
dFree(Info->LocalKey);
|
||||
dFree(Info);
|
||||
break;
|
||||
default:
|
||||
MSG_WARN("Unused CCC\n");
|
||||
break;
|
||||
}
|
||||
} else { /* 1 FWD */
|
||||
/* Send commands to dpi-server (status) */
|
||||
switch (Op) {
|
||||
case OpAbort:
|
||||
a_Chain_fcb(OpAbort, Info, NULL, Data2);
|
||||
dFree(Info);
|
||||
break;
|
||||
default:
|
||||
MSG_WARN("Unused CCC\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (Branch == 2) {
|
||||
if (Dir == FWD) {
|
||||
/* Receiving from server */
|
||||
switch (Op) {
|
||||
case OpSend:
|
||||
/* Data1 = dbuf */
|
||||
Dpi_process_dbuf(IORead, Data1, Info->LocalKey);
|
||||
break;
|
||||
case OpEnd:
|
||||
a_Chain_fcb(OpEnd, Info, NULL, NULL);
|
||||
Dpi_conn_free(Info->LocalKey);
|
||||
dFree(Info);
|
||||
break;
|
||||
default:
|
||||
MSG_WARN("Unused CCC\n");
|
||||
break;
|
||||
}
|
||||
} else { /* 2 BCK */
|
||||
switch (Op) {
|
||||
case OpStart:
|
||||
conn = Dpi_conn_new(Info);
|
||||
Info->LocalKey = conn;
|
||||
|
||||
/* Hack: for receiving HTTP through the DPI framework */
|
||||
if (strcmp(Data2, "http") == 0) {
|
||||
conn->Send2EOF = 1;
|
||||
}
|
||||
|
||||
a_Chain_link_new(Info, a_Dpi_ccc, BCK, a_IO_ccc, 2, 2);
|
||||
a_Chain_bcb(OpStart, Info, NULL, NULL); /* IORead */
|
||||
break;
|
||||
case OpSend:
|
||||
if (Data2 && !strcmp(Data2, "FD")) {
|
||||
a_Chain_bcb(OpSend, Info, Data1, Data2);
|
||||
}
|
||||
break;
|
||||
case OpAbort:
|
||||
a_Chain_bcb(OpAbort, Info, NULL, NULL);
|
||||
Dpi_conn_free(Info->LocalKey);
|
||||
dFree(Info);
|
||||
break;
|
||||
default:
|
||||
MSG_WARN("Unused CCC\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Let dpid know dillo is no longer running.
|
||||
* Note: currently disabled. It may serve to let the cookies dpi know
|
||||
* when to expire session cookies.
|
||||
*/
|
||||
void a_Dpi_dillo_exit(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a command to a dpi server, and block until the answer is got.
|
||||
* Return value: the dpip tag answer as an string, NULL on error.
|
||||
*/
|
||||
char *a_Dpi_send_blocking_cmd(const char *server_name, const char *cmd)
|
||||
{
|
||||
int cst, sock_fd;
|
||||
char *ret = NULL;
|
||||
|
||||
/* test the dpid, and wait a bit for it to start if necessary */
|
||||
if ((cst = Dpi_blocking_start_dpid()) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((sock_fd = Dpi_connect_socket(server_name)) == -1) {
|
||||
MSG_ERR("[a_Dpi_send_blocking_cmd] Can't connect to server.\n");
|
||||
} else if (Dpi_blocking_write(sock_fd, cmd, strlen(cmd)) == -1) {
|
||||
MSG_ERR("[a_Dpi_send_blocking_cmd] Can't send message.\n");
|
||||
} else if ((ret = Dpi_blocking_read(sock_fd)) == NULL) {
|
||||
MSG_ERR("[a_Dpi_send_blocking_cmd] Can't read message.\n");
|
||||
}
|
||||
dClose(sock_fd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
1135
src/IO/http.c
Normal file
1135
src/IO/http.c
Normal file
File diff suppressed because it is too large
Load Diff
37
src/IO/iowatch.cc
Normal file
37
src/IO/iowatch.cc
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* File: iowatch.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.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* Simple ADT for watching file descriptor activity
|
||||
*/
|
||||
|
||||
#include <FL/Fl.H>
|
||||
#include "iowatch.hh"
|
||||
|
||||
/**
|
||||
* Hook a Callback for a certain activities in a FD
|
||||
*/
|
||||
void a_IOwatch_add_fd(int fd, int when, Fl_FD_Handler Callback,
|
||||
void *usr_data = 0)
|
||||
{
|
||||
if (fd >= 0)
|
||||
Fl::add_fd(fd, when, Callback, usr_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a Callback for a given FD (or just remove some events)
|
||||
*/
|
||||
void a_IOwatch_remove_fd(int fd, int when)
|
||||
{
|
||||
if (fd >= 0)
|
||||
Fl::remove_fd(fd, when);
|
||||
}
|
||||
|
||||
25
src/IO/iowatch.hh
Normal file
25
src/IO/iowatch.hh
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef __IO_WATCH_H__
|
||||
#define __IO_WATCH_H__
|
||||
|
||||
/*
|
||||
* BUG: enum {READ = 1, WRITE = 4, EXCEPT = 8} borrowed from FL/Enumerations.H
|
||||
*/
|
||||
#define DIO_READ 1
|
||||
#define DIO_WRITE 4
|
||||
#define DIO_EXCEPT 8
|
||||
|
||||
typedef void (*CbFunction_t)(int fd, void *data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void a_IOwatch_add_fd(int fd,int when,CbFunction_t Callback,void *usr_data);
|
||||
void a_IOwatch_remove_fd(int fd,int when);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __IO_WATCH_H__ */
|
||||
|
||||
151
src/IO/mime.c
Normal file
151
src/IO/mime.c
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* File: mime.c
|
||||
*
|
||||
* Copyright (C) 2000-2007 Jorge Arellano Cid <jcid@dillo.org>
|
||||
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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 "mime.h"
|
||||
#include "../list.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
const char *Name; /* MIME type name */
|
||||
Viewer_t Data; /* Pointer to a function */
|
||||
} MimeItem_t;
|
||||
|
||||
|
||||
/*
|
||||
* Local data
|
||||
*/
|
||||
static int MimeMinItemsSize = 0, MimeMinItemsMax = 8;
|
||||
static MimeItem_t *MimeMinItems = NULL;
|
||||
|
||||
static int MimeMajItemsSize = 0, MimeMajItemsMax = 8;
|
||||
static MimeItem_t *MimeMajItems = NULL;
|
||||
|
||||
|
||||
/**
|
||||
* Add a specific MIME type (as "image/png") to our viewer list.
|
||||
* 'Key' is the content-type string that identifies the MIME type
|
||||
* 'Method' is the function that handles it
|
||||
*/
|
||||
static int Mime_add_minor_type(const char *Key, Viewer_t Method)
|
||||
{
|
||||
a_List_add(MimeMinItems, MimeMinItemsSize, MimeMinItemsMax);
|
||||
MimeMinItems[MimeMinItemsSize].Name = Key;
|
||||
MimeMinItems[MimeMinItemsSize].Data = Method;
|
||||
MimeMinItemsSize++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a major MIME type (as "text") to our viewer list.
|
||||
* 'Key' is the content-type string that identifies the MIME type
|
||||
* 'Method' is the function that handles it
|
||||
*/
|
||||
static int Mime_add_major_type(const char *Key, Viewer_t Method)
|
||||
{
|
||||
a_List_add(MimeMajItems, MimeMajItemsSize, MimeMajItemsMax);
|
||||
MimeMajItems[MimeMajItemsSize].Name = Key;
|
||||
MimeMajItems[MimeMajItemsSize].Data = Method;
|
||||
MimeMajItemsSize++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the list of specific MIME viewers for a Method that matches 'Key'.
|
||||
* 'Key' is the content-type string that identifies the MIME type
|
||||
*/
|
||||
static Viewer_t Mime_minor_type_fetch(const char *Key, uint_t Size)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (Size) {
|
||||
for ( i = 0; i < MimeMinItemsSize; ++i )
|
||||
if (dStrnAsciiCasecmp(Key, MimeMinItems[i].Name, Size) == 0)
|
||||
return MimeMinItems[i].Data;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the list of major MIME viewers for a Method that matches 'Key'.
|
||||
* 'Key' is the content-type string that identifies the MIME type
|
||||
*/
|
||||
static Viewer_t Mime_major_type_fetch(const char *Key, uint_t Size)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (Size) {
|
||||
for ( i = 0; i < MimeMajItemsSize; ++i )
|
||||
if (dStrnAsciiCasecmp(Key, MimeMajItems[i].Name, Size) == 0)
|
||||
return MimeMajItems[i].Data;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes Mime module and, sets the supported Mime types.
|
||||
*/
|
||||
void a_Mime_init(void)
|
||||
{
|
||||
#ifdef ENABLE_GIF
|
||||
Mime_add_minor_type("image/gif", a_Dicache_gif_image);
|
||||
#endif
|
||||
#ifdef ENABLE_JPEG
|
||||
Mime_add_minor_type("image/jpeg", a_Dicache_jpeg_image);
|
||||
Mime_add_minor_type("image/pjpeg", a_Dicache_jpeg_image);
|
||||
Mime_add_minor_type("image/jpg", a_Dicache_jpeg_image);
|
||||
#endif
|
||||
#ifdef ENABLE_PNG
|
||||
Mime_add_minor_type("image/png", a_Dicache_png_image);
|
||||
Mime_add_minor_type("image/x-png", a_Dicache_png_image); /* deprecated */
|
||||
#endif
|
||||
#ifdef ENABLE_WEBP
|
||||
Mime_add_minor_type("image/webp", a_Dicache_webp_image);
|
||||
#endif
|
||||
#ifdef ENABLE_SVG
|
||||
Mime_add_minor_type("image/svg+xml", a_Dicache_svg_image);
|
||||
#endif
|
||||
Mime_add_minor_type("text/html", a_Html_text);
|
||||
Mime_add_minor_type("application/xhtml+xml", a_Html_text);
|
||||
Mime_add_minor_type("application/json", a_Plain_text);
|
||||
|
||||
/* Add a major type to handle all the text stuff */
|
||||
Mime_add_major_type("text", a_Plain_text);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the handler for the MIME type.
|
||||
*
|
||||
* Return Value:
|
||||
* On success: viewer
|
||||
* On failure: NULL
|
||||
*/
|
||||
Viewer_t a_Mime_get_viewer(const char *content_type)
|
||||
{
|
||||
Viewer_t viewer;
|
||||
uint_t MinSize, MajSize, i;
|
||||
const char *str = content_type;
|
||||
|
||||
MajSize = 0;
|
||||
for (i = 0; str[i] && str[i] != ' ' && str[i] != ';'; ++i) {
|
||||
if (str[i] == '/' && !MajSize)
|
||||
MajSize = i;
|
||||
}
|
||||
MinSize = i;
|
||||
|
||||
viewer = Mime_minor_type_fetch(content_type, MinSize);
|
||||
if (!viewer)
|
||||
viewer = Mime_major_type_fetch(content_type, MajSize);
|
||||
|
||||
return viewer;
|
||||
}
|
||||
54
src/IO/mime.h
Normal file
54
src/IO/mime.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* File: mime.h
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __MIME_H__
|
||||
#define __MIME_H__
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#include "../cache.h"
|
||||
|
||||
typedef void* (*Viewer_t) (const char*, void*, CA_Callback_t*, void**);
|
||||
|
||||
/**
|
||||
* Function prototypes defined elsewhere.
|
||||
*/
|
||||
void *a_Html_text (const char *Type,void *web, CA_Callback_t *Call,
|
||||
void **Data);
|
||||
void *a_Plain_text(const char *Type,void *web, CA_Callback_t *Call,
|
||||
void **Data);
|
||||
void *a_Dicache_png_image (const char *Type,void *web, CA_Callback_t *Call,
|
||||
void **Data);
|
||||
void *a_Dicache_webp_image (const char *Type,void *web, CA_Callback_t *Call,
|
||||
void **Data);
|
||||
void *a_Dicache_gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
|
||||
void **Data);
|
||||
void *a_Dicache_jpeg_image(const char *Type, void *Ptr, CA_Callback_t *Call,
|
||||
void **Data);
|
||||
void *a_Dicache_svg_image(const char *Type, void *Ptr, CA_Callback_t *Call,
|
||||
void **Data);
|
||||
|
||||
/**
|
||||
* Functions defined inside Mime module
|
||||
*/
|
||||
void a_Mime_init(void);
|
||||
Viewer_t a_Mime_get_viewer(const char *content_type);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __MIME_H__ */
|
||||
13
src/IO/proto.c
Normal file
13
src/IO/proto.c
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* File: proto.c
|
||||
*
|
||||
* Copyright (C) 2003-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.
|
||||
*/
|
||||
|
||||
/* This module may be programmed to manage dpi-programs. */
|
||||
|
||||
194
src/IO/tls.c
Normal file
194
src/IO/tls.c
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* File: tls.c
|
||||
*
|
||||
* Copyright (C) 2011 Benjamin Johnson <obeythepenguin@users.sourceforge.net>
|
||||
* (for the https code offered from dplus browser that formed the basis...)
|
||||
* Copyright 2016 corvid
|
||||
* Copyright (C) 2023-2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* As a special exception, permission is granted to link Dillo with the OpenSSL
|
||||
* or LibreSSL library, and distribute the linked executables without
|
||||
* including the source code for OpenSSL or LibreSSL in the source
|
||||
* distribution. You must obey the GNU General Public License, version 3, in
|
||||
* all respects for all of the code used other than OpenSSL or LibreSSL.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "../msg.h"
|
||||
|
||||
#include "tls.h"
|
||||
#include "tls_openssl.h"
|
||||
#include "tls_mbedtls.h"
|
||||
|
||||
/**
|
||||
* Get the version of the TLS library.
|
||||
*/
|
||||
const char *a_Tls_version(char *buf, int n)
|
||||
{
|
||||
#if ! defined(ENABLE_TLS)
|
||||
return NULL;
|
||||
#elif defined(HAVE_OPENSSL)
|
||||
return a_Tls_openssl_version(buf, n);
|
||||
#elif defined(HAVE_MBEDTLS)
|
||||
return a_Tls_mbedtls_version(buf, n);
|
||||
#else
|
||||
# error "no TLS library found but ENABLE_TLS set"
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize TLS library.
|
||||
*/
|
||||
void a_Tls_init(void)
|
||||
{
|
||||
#if ! defined(ENABLE_TLS)
|
||||
MSG("TLS: Disabled at compilation time.\n");
|
||||
#elif defined(HAVE_OPENSSL)
|
||||
a_Tls_openssl_init();
|
||||
#elif defined(HAVE_MBEDTLS)
|
||||
a_Tls_mbedtls_init();
|
||||
#else
|
||||
# error "no TLS library found but ENABLE_TLS set"
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Return TLS connection information for a given file
|
||||
* descriptor, or NULL if no TLS connection was found.
|
||||
*/
|
||||
void *a_Tls_connection(int fd)
|
||||
{
|
||||
#if ! defined(ENABLE_TLS)
|
||||
return NULL;
|
||||
#elif defined(HAVE_OPENSSL)
|
||||
return a_Tls_openssl_connection(fd);
|
||||
#elif defined(HAVE_MBEDTLS)
|
||||
return a_Tls_mbedtls_connection(fd);
|
||||
#else
|
||||
# error "no TLS library found but ENABLE_TLS set"
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* The purpose here is to permit a single initial connection to a server.
|
||||
* Once we have the certificate, know whether we like it -- and whether the
|
||||
* user accepts it -- HTTP can run through queued sockets as normal.
|
||||
*
|
||||
* Return: TLS_CONNECT_READY or TLS_CONNECT_NOT_YET or TLS_CONNECT_NEVER.
|
||||
*/
|
||||
int a_Tls_connect_ready(const DilloUrl *url)
|
||||
{
|
||||
#if ! defined(ENABLE_TLS)
|
||||
return TLS_CONNECT_NEVER;
|
||||
#elif defined(HAVE_OPENSSL)
|
||||
return a_Tls_openssl_connect_ready(url);
|
||||
#elif defined(HAVE_MBEDTLS)
|
||||
return a_Tls_mbedtls_connect_ready(url);
|
||||
#else
|
||||
# error "no TLS library found but ENABLE_TLS set"
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Did everything seem proper with the certificate -- no warnings to
|
||||
* click through?.
|
||||
*/
|
||||
int a_Tls_certificate_is_clean(const DilloUrl *url)
|
||||
{
|
||||
#if ! defined(ENABLE_TLS)
|
||||
return 0;
|
||||
#elif defined(HAVE_OPENSSL)
|
||||
return a_Tls_openssl_certificate_is_clean(url);
|
||||
#elif defined(HAVE_MBEDTLS)
|
||||
return a_Tls_mbedtls_certificate_is_clean(url);
|
||||
#else
|
||||
# error "no TLS library found but ENABLE_TLS set"
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up the TLS library.
|
||||
*/
|
||||
void a_Tls_freeall(void)
|
||||
{
|
||||
#if ! defined(ENABLE_TLS)
|
||||
return;
|
||||
#elif defined(HAVE_OPENSSL)
|
||||
a_Tls_openssl_freeall();
|
||||
#elif defined(HAVE_MBEDTLS)
|
||||
a_Tls_mbedtls_freeall();
|
||||
#else
|
||||
# error "no TLS library found but ENABLE_TLS set"
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void a_Tls_reset_server_state(const DilloUrl *url)
|
||||
{
|
||||
#if ! defined(ENABLE_TLS)
|
||||
return;
|
||||
#elif defined(HAVE_OPENSSL)
|
||||
a_Tls_openssl_reset_server_state(url);
|
||||
#elif defined(HAVE_MBEDTLS)
|
||||
a_Tls_mbedtls_reset_server_state(url);
|
||||
#else
|
||||
# error "no TLS library found but ENABLE_TLS set"
|
||||
#endif
|
||||
}
|
||||
|
||||
void a_Tls_connect(int fd, const DilloUrl *url)
|
||||
{
|
||||
#if ! defined(ENABLE_TLS)
|
||||
return;
|
||||
#elif defined(HAVE_OPENSSL)
|
||||
a_Tls_openssl_connect(fd, url);
|
||||
#elif defined(HAVE_MBEDTLS)
|
||||
a_Tls_mbedtls_connect(fd, url);
|
||||
#else
|
||||
# error "no TLS library found but ENABLE_TLS set"
|
||||
#endif
|
||||
}
|
||||
|
||||
void a_Tls_close_by_fd(int fd)
|
||||
{
|
||||
#if ! defined(ENABLE_TLS)
|
||||
return;
|
||||
#elif defined(HAVE_OPENSSL)
|
||||
a_Tls_openssl_close_by_fd(fd);
|
||||
#elif defined(HAVE_MBEDTLS)
|
||||
a_Tls_mbedtls_close_by_fd(fd);
|
||||
#else
|
||||
# error "no TLS library found but ENABLE_TLS set"
|
||||
#endif
|
||||
}
|
||||
|
||||
int a_Tls_read(void *conn, void *buf, size_t len)
|
||||
{
|
||||
#if ! defined(ENABLE_TLS)
|
||||
return 0;
|
||||
#elif defined(HAVE_OPENSSL)
|
||||
return a_Tls_openssl_read(conn, buf, len);
|
||||
#elif defined(HAVE_MBEDTLS)
|
||||
return a_Tls_mbedtls_read(conn, buf, len);
|
||||
#else
|
||||
# error "no TLS library found but ENABLE_TLS set"
|
||||
#endif
|
||||
}
|
||||
|
||||
int a_Tls_write(void *conn, void *buf, size_t len)
|
||||
{
|
||||
#if ! defined(ENABLE_TLS)
|
||||
return 0;
|
||||
#elif defined(HAVE_OPENSSL)
|
||||
return a_Tls_openssl_write(conn, buf, len);
|
||||
#elif defined(HAVE_MBEDTLS)
|
||||
return a_Tls_mbedtls_write(conn, buf, len);
|
||||
#else
|
||||
# error "no TLS library found but ENABLE_TLS set"
|
||||
#endif
|
||||
}
|
||||
51
src/IO/tls.h
Normal file
51
src/IO/tls.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* File: tls.h
|
||||
*
|
||||
* Copyright (C) 2011 Benjamin Johnson <obeythepenguin@users.sourceforge.net>
|
||||
* (for the https code offered from dplus browser that formed the basis...)
|
||||
* Copyright 2016 corvid
|
||||
* Copyright (C) 2023-2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* As a special exception, permission is granted to link Dillo with the OpenSSL
|
||||
* or LibreSSL library, and distribute the linked executables without
|
||||
* including the source code for OpenSSL or LibreSSL in the source
|
||||
* distribution. You must obey the GNU General Public License, version 3, in
|
||||
* all respects for all of the code used other than OpenSSL or LibreSSL.
|
||||
*/
|
||||
|
||||
#ifndef __TLS_H__
|
||||
#define __TLS_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "../url.h"
|
||||
|
||||
#define TLS_CONNECT_NEVER -1
|
||||
#define TLS_CONNECT_NOT_YET 0
|
||||
#define TLS_CONNECT_READY 1
|
||||
|
||||
const char *a_Tls_version(char *buf, int n);
|
||||
void a_Tls_init(void);
|
||||
int a_Tls_certificate_is_clean(const DilloUrl *url);
|
||||
int a_Tls_connect_ready(const DilloUrl *url);
|
||||
void a_Tls_reset_server_state(const DilloUrl *url);
|
||||
void a_Tls_connect(int fd, const DilloUrl *url);
|
||||
void *a_Tls_connection(int fd);
|
||||
void a_Tls_freeall(void);
|
||||
void a_Tls_close_by_fd(int fd);
|
||||
int a_Tls_read(void *conn, void *buf, size_t len);
|
||||
int a_Tls_write(void *conn, void *buf, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __TLS_H__ */
|
||||
|
||||
1263
src/IO/tls_mbedtls.c
Normal file
1263
src/IO/tls_mbedtls.c
Normal file
File diff suppressed because it is too large
Load Diff
40
src/IO/tls_mbedtls.h
Normal file
40
src/IO/tls_mbedtls.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* File: tls_mbedtls.h
|
||||
*
|
||||
* Copyright (C) 2011 Benjamin Johnson <obeythepenguin@users.sourceforge.net>
|
||||
* (for the https code offered from dplus browser that formed the basis...)
|
||||
* Copyright 2016 corvid
|
||||
* Copyright (C) 2023-2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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 __TLS_MBEDTLS_H__
|
||||
#define __TLS_MBEDTLS_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "../url.h"
|
||||
|
||||
const char *a_Tls_mbedtls_version(char *buf, int n);
|
||||
void a_Tls_mbedtls_init(void);
|
||||
int a_Tls_mbedtls_certificate_is_clean(const DilloUrl *url);
|
||||
int a_Tls_mbedtls_connect_ready(const DilloUrl *url);
|
||||
void a_Tls_mbedtls_reset_server_state(const DilloUrl *url);
|
||||
void a_Tls_mbedtls_connect(int fd, const DilloUrl *url);
|
||||
void *a_Tls_mbedtls_connection(int fd);
|
||||
void a_Tls_mbedtls_freeall(void);
|
||||
void a_Tls_mbedtls_close_by_fd(int fd);
|
||||
int a_Tls_mbedtls_read(void *conn, void *buf, size_t len);
|
||||
int a_Tls_mbedtls_write(void *conn, void *buf, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __TLS_MBEDTLS_H__ */
|
||||
1404
src/IO/tls_openssl.c
Normal file
1404
src/IO/tls_openssl.c
Normal file
File diff suppressed because it is too large
Load Diff
50
src/IO/tls_openssl.h
Normal file
50
src/IO/tls_openssl.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* File: tls_openssl.h
|
||||
*
|
||||
* Copyright 2004 Garrett Kajmowicz <gkajmowi@tbaytel.net>
|
||||
* (for some bits derived from the https dpi, e.g., certificate handling)
|
||||
* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
|
||||
* 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
|
||||
* (for the certificate hostname checking from wget)
|
||||
* Copyright (C) 2011 Benjamin Johnson <obeythepenguin@users.sourceforge.net>
|
||||
* (for the https code offered from dplus browser that formed the basis...)
|
||||
* Copyright (C) 2023-2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* As a special exception, permission is granted to link Dillo with the OpenSSL
|
||||
* or LibreSSL library, and distribute the linked executables without
|
||||
* including the source code for OpenSSL or LibreSSL in the source
|
||||
* distribution. You must obey the GNU General Public License, version 3, in
|
||||
* all respects for all of the code used other than OpenSSL or LibreSSL.
|
||||
*/
|
||||
|
||||
#ifndef __TLS_OPENSSL_H__
|
||||
#define __TLS_OPENSSL_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "../url.h"
|
||||
|
||||
const char *a_Tls_openssl_version(char *buf, int n);
|
||||
void a_Tls_openssl_init(void);
|
||||
int a_Tls_openssl_certificate_is_clean(const DilloUrl *url);
|
||||
int a_Tls_openssl_connect_ready(const DilloUrl *url);
|
||||
void a_Tls_openssl_reset_server_state(const DilloUrl *url);
|
||||
void a_Tls_openssl_connect(int fd, const DilloUrl *url);
|
||||
void *a_Tls_openssl_connection(int fd);
|
||||
void a_Tls_openssl_freeall(void);
|
||||
void a_Tls_openssl_close_by_fd(int fd);
|
||||
int a_Tls_openssl_read(void *conn, void *buf, size_t len);
|
||||
int a_Tls_openssl_write(void *conn, void *buf, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __TLS_OPENSSL_H__ */
|
||||
164
src/Makefile.am
Normal file
164
src/Makefile.am
Normal file
@ -0,0 +1,164 @@
|
||||
AM_CPPFLAGS= \
|
||||
-I$(top_srcdir) \
|
||||
-DDILLO_SYSCONF='"$(sysconfdir)/"' \
|
||||
-DDILLO_DOCDIR='"$(docdir)/"' \
|
||||
-DCUR_WORKING_DIR='"@BASE_CUR_WORKING_DIR@/src"' \
|
||||
@LIBJPEG_CPPFLAGS@
|
||||
|
||||
AM_CFLAGS = @LIBPNG_CFLAGS@
|
||||
AM_CXXFLAGS = @LIBPNG_CFLAGS@ @LIBFLTK_CXXFLAGS@
|
||||
|
||||
SUBDIRS = IO
|
||||
|
||||
bin_PROGRAMS = dillo
|
||||
|
||||
dillo_LDADD = \
|
||||
$(top_builddir)/dlib/libDlib.a \
|
||||
$(top_builddir)/dpip/libDpip.a \
|
||||
IO/libDiof.a \
|
||||
$(top_builddir)/dw/libDw-widgets.a \
|
||||
$(top_builddir)/dw/libDw-fltk.a \
|
||||
$(top_builddir)/dw/libDw-core.a \
|
||||
$(top_builddir)/lout/liblout.a \
|
||||
@LIBJPEG_LIBS@ @LIBPNG_LIBS@ @LIBWEBP_LIBS@ @LIBFLTK_LIBS@ @LIBZ_LIBS@ \
|
||||
@LIBICONV_LIBS@ @LIBPTHREAD_LIBS@ @LIBX11_LIBS@ @LIBSSL_LIBS@
|
||||
|
||||
dillo_SOURCES = \
|
||||
dillo.cc \
|
||||
version.cc \
|
||||
version.hh \
|
||||
paths.cc \
|
||||
paths.hh \
|
||||
tipwin.cc \
|
||||
tipwin.hh \
|
||||
ui.cc \
|
||||
ui.hh \
|
||||
uicmd.cc \
|
||||
uicmd.hh \
|
||||
bw.h \
|
||||
bw.c \
|
||||
cookies.c \
|
||||
cookies.h \
|
||||
actions.c \
|
||||
actions.h \
|
||||
hsts.c \
|
||||
hsts.h \
|
||||
auth.c \
|
||||
auth.h \
|
||||
md5.c \
|
||||
md5.h \
|
||||
digest.c \
|
||||
digest.h \
|
||||
colors.c \
|
||||
colors.h \
|
||||
binaryconst.h \
|
||||
misc.c \
|
||||
misc.h \
|
||||
history.h \
|
||||
history.c \
|
||||
prefs.c \
|
||||
prefs.h \
|
||||
prefsparser.cc \
|
||||
prefsparser.hh \
|
||||
keys.cc \
|
||||
keys.hh \
|
||||
msg.h \
|
||||
list.h \
|
||||
url.c \
|
||||
url.h \
|
||||
bitvec.c \
|
||||
bitvec.h \
|
||||
klist.c \
|
||||
klist.h \
|
||||
chain.c \
|
||||
chain.h \
|
||||
utf8.cc \
|
||||
utf8.hh \
|
||||
timeout.cc \
|
||||
timeout.hh \
|
||||
dialog.cc \
|
||||
dialog.hh \
|
||||
\
|
||||
\
|
||||
web.cc \
|
||||
web.hh \
|
||||
nav.c \
|
||||
nav.h \
|
||||
cache.c \
|
||||
cache.h \
|
||||
decode.c \
|
||||
decode.h \
|
||||
dicache.c \
|
||||
dicache.h \
|
||||
capi.c \
|
||||
capi.h \
|
||||
domain.c \
|
||||
domain.h \
|
||||
css.cc \
|
||||
css.hh \
|
||||
cssparser.cc \
|
||||
cssparser.hh \
|
||||
doctree.hh \
|
||||
styleengine.cc \
|
||||
styleengine.hh \
|
||||
plain.cc \
|
||||
html.cc \
|
||||
html.hh \
|
||||
html_charrefs.h \
|
||||
html_common.hh \
|
||||
form.cc \
|
||||
form.hh \
|
||||
table.cc \
|
||||
table.hh \
|
||||
bookmark.c \
|
||||
bookmark.h \
|
||||
dns.c \
|
||||
dns.h \
|
||||
gif.c \
|
||||
dgif.h \
|
||||
jpeg.c \
|
||||
djpeg.h \
|
||||
png.c \
|
||||
dpng.h \
|
||||
webp.c \
|
||||
dwebp.h \
|
||||
svg.c \
|
||||
nanosvg.h \
|
||||
nanosvgrast.h \
|
||||
dsvg.h \
|
||||
imgbuf.cc \
|
||||
imgbuf.hh \
|
||||
image.cc \
|
||||
image.hh \
|
||||
menu.hh \
|
||||
menu.cc \
|
||||
dpiapi.c \
|
||||
dpiapi.h \
|
||||
pixmaps.h \
|
||||
findbar.cc \
|
||||
findbar.hh \
|
||||
xembed.cc \
|
||||
xembed.hh
|
||||
|
||||
# https://www.gnu.org/software/automake/manual/html_node/Built-Sources-Example.html
|
||||
nodist_dillo_SOURCES = commit.h
|
||||
version.$(OBJEXT) dillo.$(OBJEXT): commit.h
|
||||
CLEANFILES = commit.h
|
||||
|
||||
if GIT_AVAILABLE
|
||||
# Rebuild commit.tmp.h every time, but only change commit.h
|
||||
# if the version is different to avoid rebuilds.
|
||||
commit.h: commit.tmp.h
|
||||
test -f $@ || (echo "" > $@)
|
||||
if diff $@ $^ >/dev/null; then rm $^; else mv -f $^ $@; fi
|
||||
|
||||
commit.tmp.h:
|
||||
printf '#define GIT_COMMIT "%s"\n' `git --work-tree="$(top_srcdir)" describe --dirty` > $@
|
||||
else
|
||||
# No need to rebuild
|
||||
commit.h:
|
||||
echo "" > commit.h
|
||||
endif # GIT_AVAILABLE
|
||||
|
||||
dist_sysconf_DATA = domainrc keysrc hsts_preload
|
||||
EXTRA_DIST = chg srch
|
||||
65
src/actions.c
Normal file
65
src/actions.c
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* File: actions.c
|
||||
*
|
||||
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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 "actions.h"
|
||||
#include "msg.h"
|
||||
#include "../dlib/dlib.h"
|
||||
#include <errno.h>
|
||||
|
||||
static Dlist *link_actions = NULL;
|
||||
|
||||
void
|
||||
action_parse(char *line)
|
||||
{
|
||||
char *label = strtok(line, ":");
|
||||
|
||||
if (label == NULL || strlen(label) == 0) {
|
||||
MSG("Missing action label, ignoring '%s'\n", line);
|
||||
return;
|
||||
}
|
||||
|
||||
//MSG("Got label='%s'\n", label);
|
||||
|
||||
char *cmd = strtok(NULL, "");
|
||||
|
||||
if (cmd == NULL || strlen(cmd) == 0) {
|
||||
MSG("Missing action command, ignoring '%s'\n", line);
|
||||
return;
|
||||
}
|
||||
|
||||
//MSG("Got action label='%s' cmd='%s'\n", label, cmd);
|
||||
|
||||
Action *action = dMalloc(sizeof(Action));
|
||||
action->label = dStrdup(label);
|
||||
action->cmd = dStrdup(cmd);
|
||||
|
||||
dList_append(link_actions, action);
|
||||
}
|
||||
|
||||
void
|
||||
a_Actions_init(void)
|
||||
{
|
||||
int n = dList_length(prefs.link_actions);
|
||||
|
||||
link_actions = dList_new(n);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
char *line = dList_nth_data(prefs.link_actions, i);
|
||||
if (line)
|
||||
action_parse(line);
|
||||
}
|
||||
}
|
||||
|
||||
Dlist *
|
||||
a_Actions_link_get(void)
|
||||
{
|
||||
return link_actions;
|
||||
}
|
||||
32
src/actions.h
Normal file
32
src/actions.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* File: actions.h
|
||||
*
|
||||
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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 ACTIONS_H
|
||||
#define ACTIONS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#include "dlib/dlib.h"
|
||||
|
||||
typedef struct {
|
||||
char *label;
|
||||
char *cmd;
|
||||
} Action;
|
||||
|
||||
void a_Actions_init(void);
|
||||
Dlist *a_Actions_link_get(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* ACTIONS_H*/
|
||||
687
src/auth.c
Normal file
687
src/auth.c
Normal file
@ -0,0 +1,687 @@
|
||||
/*
|
||||
* File: auth.c
|
||||
*
|
||||
* Copyright 2008 Jeremy Henty <onepoint@starurchin.org>
|
||||
* Copyright 2009 Justus Winter <4winter@informatik.uni-hamburg.de>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* Handling of HTTP AUTH takes place here.
|
||||
* This implementation aims to follow RFC 2617:
|
||||
* http://www.ietf.org/rfc/rfc2617.txt
|
||||
*/
|
||||
|
||||
|
||||
#include <ctype.h> /* iscntrl, isascii */
|
||||
#include "auth.h"
|
||||
#include "msg.h"
|
||||
#include "misc.h"
|
||||
#include "dialog.hh"
|
||||
#include "digest.h"
|
||||
#include "../dlib/dlib.h"
|
||||
|
||||
typedef struct {
|
||||
int ok;
|
||||
enum AuthParseHTTPAuthType_t type;
|
||||
const char *realm;
|
||||
const char *nonce;
|
||||
const char *opaque;
|
||||
int stale;
|
||||
enum AuthParseDigestAlgorithm_t algorithm;
|
||||
const char *domain;
|
||||
enum AuthParseDigestQOP_t qop;
|
||||
} AuthParse_t;
|
||||
|
||||
typedef struct {
|
||||
char *scheme;
|
||||
char *authority;
|
||||
Dlist *realms;
|
||||
} AuthHost_t;
|
||||
|
||||
typedef struct {
|
||||
const AuthParse_t *auth_parse;
|
||||
const DilloUrl *url;
|
||||
} AuthDialogData_t;
|
||||
|
||||
/**
|
||||
* Local data
|
||||
*/
|
||||
static Dlist *auth_hosts;
|
||||
|
||||
/**
|
||||
* Initialize the auth module.
|
||||
*/
|
||||
void a_Auth_init(void)
|
||||
{
|
||||
auth_hosts = dList_new(1);
|
||||
}
|
||||
|
||||
static AuthParse_t *Auth_parse_new(void)
|
||||
{
|
||||
AuthParse_t *auth_parse = dNew(AuthParse_t, 1);
|
||||
auth_parse->ok = 0;
|
||||
auth_parse->type = TYPENOTSET;
|
||||
auth_parse->realm = NULL;
|
||||
auth_parse->nonce = NULL;
|
||||
auth_parse->opaque = NULL;
|
||||
auth_parse->stale = 0;
|
||||
auth_parse->algorithm = ALGORITHMNOTSET;
|
||||
auth_parse->domain = NULL;
|
||||
auth_parse->qop = QOPNOTSET;
|
||||
return auth_parse;
|
||||
}
|
||||
|
||||
static void Auth_parse_free(AuthParse_t *auth_parse)
|
||||
{
|
||||
if (auth_parse) {
|
||||
dFree((void *)auth_parse->realm);
|
||||
dFree((void *)auth_parse->nonce);
|
||||
dFree((void *)auth_parse->opaque);
|
||||
dFree((void *)auth_parse->domain);
|
||||
dFree(auth_parse);
|
||||
}
|
||||
}
|
||||
|
||||
static int Auth_path_is_inside(const char *path1, const char *path2, int len)
|
||||
{
|
||||
/*
|
||||
* path2 is effectively truncated to length len. Typically len will be
|
||||
* strlen(path2), or 1 less when we want to ignore a trailing '/'.
|
||||
*/
|
||||
return
|
||||
strncmp(path1, path2, len) == 0 &&
|
||||
(path1[len] == '\0' || path1[len] == '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check valid chars.
|
||||
* Return: 0 if invalid, 1 otherwise.
|
||||
*/
|
||||
static int Auth_is_token_char(char c)
|
||||
{
|
||||
const char *invalid = "\"()<>@,;:\\[]?=/{} \t";
|
||||
return (!d_isascii(c) || strchr(invalid, c) || iscntrl((uchar_t)c)) ? 0 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unquote the content of a (potentially) quoted string.
|
||||
* Return: newly allocated unquoted content.
|
||||
*
|
||||
* Arguments:
|
||||
* valuep: pointer to a pointer to the first char.
|
||||
*
|
||||
* Preconditions:
|
||||
* *valuep points to a correctly quoted and escaped string.
|
||||
*
|
||||
* Postconditions:
|
||||
* *valuep points to the first not processed char.
|
||||
*
|
||||
*/
|
||||
static Dstr *Auth_unquote_value(char **valuep)
|
||||
{
|
||||
char c, quoted;
|
||||
char *value = *valuep;
|
||||
Dstr *result;
|
||||
|
||||
while (*value == ' ' || *value == '\t')
|
||||
value++;
|
||||
|
||||
if ((quoted = *value == '"'))
|
||||
value++;
|
||||
|
||||
result = dStr_new(NULL);
|
||||
while ((c = *value) &&
|
||||
(( quoted && c != '"') ||
|
||||
(!quoted && Auth_is_token_char(c)))) {
|
||||
dStr_append_c(result, (c == '\\' && value[1]) ? *++value : c);
|
||||
value++;
|
||||
}
|
||||
|
||||
if (quoted && *value == '\"')
|
||||
value++;
|
||||
*valuep = value;
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef int (Auth_parse_token_value_callback_t)(AuthParse_t *auth_parse,
|
||||
char *token,
|
||||
const char *value);
|
||||
|
||||
|
||||
/**
|
||||
* Parse authentication challenge into token-value pairs
|
||||
* and feed them into the callback function.
|
||||
*
|
||||
* The parsing is aborted should the callback function return 0.
|
||||
*
|
||||
* Return: 1 if the parse succeeds, 0 otherwise.
|
||||
*/
|
||||
static int Auth_parse_token_value(AuthParse_t *auth_parse, char **auth,
|
||||
Auth_parse_token_value_callback_t *callback)
|
||||
{
|
||||
char keep_going, expect_quoted;
|
||||
char *token, *beyond_token;
|
||||
Dstr *value;
|
||||
size_t *token_size;
|
||||
|
||||
while (**auth) {
|
||||
_MSG("Auth_parse_token_value: remaining: %s\n", *auth);
|
||||
|
||||
/* parse a token */
|
||||
token = *auth;
|
||||
|
||||
token_size = 0;
|
||||
while (Auth_is_token_char(**auth)) {
|
||||
(*auth)++;
|
||||
token_size++;
|
||||
}
|
||||
if (token_size == 0) {
|
||||
MSG("Auth_parse_token_value: missing auth token\n");
|
||||
return 0;
|
||||
}
|
||||
beyond_token = *auth;
|
||||
/* skip linear whitespace characters */
|
||||
while (**auth == ' ' || **auth == '\t')
|
||||
(*auth)++;
|
||||
|
||||
/* parse the '=' */
|
||||
switch (*(*auth)++) {
|
||||
case '=':
|
||||
*beyond_token = '\0';
|
||||
break;
|
||||
case '\0':
|
||||
case ',':
|
||||
MSG("Auth_parse_token_value: missing auth token value\n");
|
||||
return 0;
|
||||
break;
|
||||
default:
|
||||
MSG("Auth_parse_token_value: garbage after auth token\n");
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
value = Auth_unquote_value(auth);
|
||||
expect_quoted = !(strcmp(token, "stale") == 0 ||
|
||||
strcmp(token, "algorithm") == 0);
|
||||
|
||||
if (((*auth)[-1] == '"') != expect_quoted)
|
||||
MSG_WARN("Auth_parse_token_value: "
|
||||
"Values for key %s should%s be quoted.\n",
|
||||
token, expect_quoted ? "" : " not");
|
||||
|
||||
keep_going = callback(auth_parse, token, value->str);
|
||||
dStr_free(value, 1);
|
||||
if (!keep_going)
|
||||
break;
|
||||
|
||||
/* skip ' ' and ',' */
|
||||
while ((**auth == ' ') || (**auth == ','))
|
||||
(*auth)++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Auth_parse_basic_challenge_cb(AuthParse_t *auth_parse, char *token,
|
||||
const char *value)
|
||||
{
|
||||
if (dStrAsciiCasecmp("realm", token) == 0) {
|
||||
if (!auth_parse->realm)
|
||||
auth_parse->realm = dStrdup(value);
|
||||
return 0; /* end parsing */
|
||||
} else
|
||||
MSG("Auth_parse_basic_challenge_cb: Ignoring unknown parameter: %s = "
|
||||
"'%s'\n", token, value);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Auth_parse_digest_challenge_cb(AuthParse_t *auth_parse, char *token,
|
||||
const char *value)
|
||||
{
|
||||
const char *const fn = "Auth_parse_digest_challenge_cb";
|
||||
|
||||
if (!dStrAsciiCasecmp("realm", token) && !auth_parse->realm)
|
||||
auth_parse->realm = dStrdup(value);
|
||||
else if (!strcmp("domain", token) && !auth_parse->domain)
|
||||
auth_parse->domain = dStrdup(value);
|
||||
else if (!strcmp("nonce", token) && !auth_parse->nonce)
|
||||
auth_parse->nonce = dStrdup(value);
|
||||
else if (!strcmp("opaque", token) && !auth_parse->opaque)
|
||||
auth_parse->opaque = dStrdup(value);
|
||||
else if (strcmp("stale", token) == 0) {
|
||||
if (dStrAsciiCasecmp("true", value) == 0)
|
||||
auth_parse->stale = 1;
|
||||
else if (dStrAsciiCasecmp("false", value) == 0)
|
||||
auth_parse->stale = 0;
|
||||
else {
|
||||
MSG("%s: Invalid stale value: %s\n", fn, value);
|
||||
return 0;
|
||||
}
|
||||
} else if (strcmp("algorithm", token) == 0) {
|
||||
if (strcmp("MD5", value) == 0)
|
||||
auth_parse->algorithm = MD5;
|
||||
else if (strcmp("MD5-sess", value) == 0) {
|
||||
/* auth_parse->algorithm = MD5SESS; */
|
||||
MSG("%s: MD5-sess algorithm disabled (not tested because 'not "
|
||||
"correctly implemented yet' in Apache 2.2)\n", fn);
|
||||
return 0;
|
||||
} else {
|
||||
MSG("%s: Unknown algorithm: %s\n", fn, value);
|
||||
return 0;
|
||||
}
|
||||
} else if (strcmp("qop", token) == 0) {
|
||||
while (*value) {
|
||||
int len = strcspn(value, ", \t");
|
||||
if (len == 4 && strncmp("auth", value, 4) == 0) {
|
||||
auth_parse->qop = AUTH;
|
||||
break;
|
||||
}
|
||||
if (len == 8 && strncmp("auth-int", value, 8) == 0) {
|
||||
/* auth_parse->qop = AUTHINT; */
|
||||
/* Keep searching; maybe we'll find an "auth" yet. */
|
||||
MSG("%s: auth-int qop disabled (not tested because 'not "
|
||||
"implemented yet' in Apache 2.2)\n", fn);
|
||||
} else {
|
||||
MSG("%s: Unknown qop value in %s\n", fn, value);
|
||||
}
|
||||
value += len;
|
||||
while (*value == ' ' || *value == '\t')
|
||||
value++;
|
||||
if (*value == ',')
|
||||
value++;
|
||||
while (*value == ' ' || *value == '\t')
|
||||
value++;
|
||||
}
|
||||
} else {
|
||||
MSG("%s: Ignoring unknown parameter: %s = '%s'\n", fn, token, value);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void Auth_parse_challenge_args(AuthParse_t *auth_parse,
|
||||
char **challenge,
|
||||
Auth_parse_token_value_callback_t *cb)
|
||||
{
|
||||
/* parse comma-separated token-value pairs */
|
||||
while (1) {
|
||||
/* skip space and comma characters */
|
||||
while (**challenge == ' ' || **challenge == ',')
|
||||
(*challenge)++;
|
||||
/* end of string? */
|
||||
if (!**challenge)
|
||||
break;
|
||||
/* parse token-value pair */
|
||||
if (!Auth_parse_token_value(auth_parse, challenge, cb))
|
||||
break;
|
||||
}
|
||||
|
||||
if (auth_parse->type == BASIC) {
|
||||
if (auth_parse->realm) {
|
||||
auth_parse->ok = 1;
|
||||
} else {
|
||||
MSG("Auth_parse_challenge_args: missing Basic auth realm\n");
|
||||
return;
|
||||
}
|
||||
} else if (auth_parse->type == DIGEST) {
|
||||
if (auth_parse->realm && auth_parse->nonce) {
|
||||
auth_parse->ok = 1;
|
||||
} else {
|
||||
MSG("Auth_parse_challenge_args: Digest challenge incomplete\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Auth_parse_challenge(AuthParse_t *auth_parse, char *challenge)
|
||||
{
|
||||
Auth_parse_token_value_callback_t *cb;
|
||||
|
||||
MSG("auth.c: Auth_parse_challenge: challenge = '%s'\n", challenge);
|
||||
if (auth_parse->type == DIGEST) {
|
||||
challenge += 7;
|
||||
cb = Auth_parse_digest_challenge_cb;
|
||||
} else {
|
||||
challenge += 6;
|
||||
cb = Auth_parse_basic_challenge_cb;
|
||||
}
|
||||
Auth_parse_challenge_args(auth_parse, &challenge, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the host that contains a URL, or NULL if there is no such host.
|
||||
*/
|
||||
static AuthHost_t *Auth_host_by_url(const DilloUrl *url)
|
||||
{
|
||||
AuthHost_t *host;
|
||||
int i;
|
||||
|
||||
for (i = 0; (host = dList_nth_data(auth_hosts, i)); i++)
|
||||
if (((dStrAsciiCasecmp(URL_SCHEME(url), host->scheme) == 0) &&
|
||||
(dStrAsciiCasecmp(URL_AUTHORITY(url), host->authority) == 0)))
|
||||
return host;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search all realms for the one with the given name.
|
||||
*/
|
||||
static AuthRealm_t *Auth_realm_by_name(const AuthHost_t *host,
|
||||
const char *name)
|
||||
{
|
||||
AuthRealm_t *realm;
|
||||
int i;
|
||||
|
||||
for (i = 0; (realm = dList_nth_data(host->realms, i)); i++)
|
||||
if (strcmp(realm->name, name) == 0)
|
||||
return realm;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search all realms for the one with the best-matching path.
|
||||
*/
|
||||
static AuthRealm_t *Auth_realm_by_path(const AuthHost_t *host,
|
||||
const char *path)
|
||||
{
|
||||
AuthRealm_t *realm_best, *realm;
|
||||
int i, j;
|
||||
int match_length = 0;
|
||||
|
||||
realm_best = NULL;
|
||||
for (i = 0; (realm = dList_nth_data(host->realms, i)); i++) {
|
||||
char *realm_path;
|
||||
|
||||
for (j = 0; (realm_path = dList_nth_data(realm->paths, j)); j++) {
|
||||
int realm_path_length = strlen(realm_path);
|
||||
if (Auth_path_is_inside(path, realm_path, realm_path_length) &&
|
||||
!(realm_best && match_length >= realm_path_length)) {
|
||||
realm_best = realm;
|
||||
match_length = realm_path_length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return realm_best;
|
||||
}
|
||||
|
||||
static void Auth_realm_delete(AuthRealm_t *realm)
|
||||
{
|
||||
int i;
|
||||
|
||||
MSG("Auth_realm_delete: \"%s\"\n", realm->name);
|
||||
for (i = dList_length(realm->paths) - 1; i >= 0; i--)
|
||||
dFree(dList_nth_data(realm->paths, i));
|
||||
dList_free(realm->paths);
|
||||
dFree(realm->name);
|
||||
dFree(realm->username);
|
||||
dFree(realm->authorization);
|
||||
dFree(realm->cnonce);
|
||||
dFree(realm->nonce);
|
||||
dFree(realm->opaque);
|
||||
dFree(realm->domain);
|
||||
dFree(realm);
|
||||
}
|
||||
|
||||
static int Auth_realm_includes_path(const AuthRealm_t *realm, const char *path)
|
||||
{
|
||||
int i;
|
||||
char *realm_path;
|
||||
|
||||
for (i = 0; (realm_path = dList_nth_data(realm->paths, i)); i++)
|
||||
if (Auth_path_is_inside(path, realm_path, strlen(realm_path)))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void Auth_realm_add_path(AuthRealm_t *realm, const char *path)
|
||||
{
|
||||
int len, i;
|
||||
char *realm_path, *n_path;
|
||||
|
||||
n_path = dStrdup(path);
|
||||
len = strlen(n_path);
|
||||
|
||||
/* remove trailing '/' */
|
||||
if (len && n_path[len - 1] == '/')
|
||||
n_path[--len] = 0;
|
||||
|
||||
/* delete existing paths that are inside the new one */
|
||||
for (i = 0; (realm_path = dList_nth_data(realm->paths, i)); i++) {
|
||||
if (Auth_path_is_inside(realm_path, path, len)) {
|
||||
dList_remove_fast(realm->paths, realm_path);
|
||||
dFree(realm_path);
|
||||
i--; /* reconsider this slot */
|
||||
}
|
||||
}
|
||||
|
||||
dList_append(realm->paths, n_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the authorization header for an HTTP query.
|
||||
* request_uri is a separate argument because we want it precisely as
|
||||
* formatted in the request.
|
||||
*/
|
||||
char *a_Auth_get_auth_str(const DilloUrl *url, const char *request_uri)
|
||||
{
|
||||
char *ret = NULL;
|
||||
AuthHost_t *host;
|
||||
AuthRealm_t *realm;
|
||||
|
||||
if ((host = Auth_host_by_url(url)) &&
|
||||
(realm = Auth_realm_by_path(host, URL_PATH(url)))) {
|
||||
if (realm->type == BASIC)
|
||||
ret = dStrdup(realm->authorization);
|
||||
else if (realm->type == DIGEST)
|
||||
ret = a_Digest_authorization_hdr(realm, url, request_uri);
|
||||
else
|
||||
MSG("a_Auth_get_auth_str() got an unknown realm type: %i.\n",
|
||||
realm->type);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user needs to authenticate.
|
||||
*/
|
||||
static int Auth_do_auth_required(const AuthParse_t *auth_parse,
|
||||
const DilloUrl *url)
|
||||
{
|
||||
/*
|
||||
* TO DO: I dislike the way that this code must decide whether we
|
||||
* sent authentication during the request and trust us to resend it
|
||||
* after the reload. Could it be more robust if every DilloUrl
|
||||
* recorded its authentication, and whether it was accepted? (JCH)
|
||||
*/
|
||||
|
||||
AuthHost_t *host;
|
||||
AuthRealm_t *realm;
|
||||
|
||||
/*
|
||||
* The size of the following comments reflects the concerns in the
|
||||
* TO DO at the top of this function. It should not be so hard to
|
||||
* explain why code is correct! (JCH)
|
||||
*/
|
||||
|
||||
/*
|
||||
* If we have authentication but did not send it (because we did
|
||||
* not know this path was in the realm) then we update the realm.
|
||||
* We do not re-authenticate because our authentication is probably
|
||||
* OK. Thanks to the updated realm the forthcoming reload will
|
||||
* make us send the authentication. If our authentication is not
|
||||
* OK the server will challenge us again after the reload and then
|
||||
* we will re-authenticate.
|
||||
*/
|
||||
if ((host = Auth_host_by_url(url)) &&
|
||||
(realm = Auth_realm_by_name(host, auth_parse->realm))) {
|
||||
if (!Auth_realm_includes_path(realm, URL_PATH(url))) {
|
||||
_MSG("Auth_do_auth_required: updating realm '%s' with URL '%s'\n",
|
||||
auth_parse->realm, URL_STR(url));
|
||||
Auth_realm_add_path(realm, URL_PATH(url));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (auth_parse->type == DIGEST && auth_parse->stale) {
|
||||
/* we do have valid credentials but our nonce is old */
|
||||
dFree((void *)realm->nonce);
|
||||
realm->nonce = dStrdup(auth_parse->nonce);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Either we had no authentication or we sent it and the server
|
||||
* rejected it, so we must re-authenticate.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void Auth_do_auth_dialog_cb(const char *user, const char *password,
|
||||
void *vData)
|
||||
{
|
||||
AuthDialogData_t *data;
|
||||
AuthHost_t *host;
|
||||
AuthRealm_t *realm;
|
||||
|
||||
data = (AuthDialogData_t *)vData;
|
||||
|
||||
/* find or create the host */
|
||||
if (!(host = Auth_host_by_url(data->url))) {
|
||||
/* create a new host */
|
||||
host = dNew(AuthHost_t, 1);
|
||||
host->scheme = dStrdup(URL_SCHEME(data->url));
|
||||
host->authority = dStrdup(URL_AUTHORITY(data->url));
|
||||
host->realms = dList_new(1);
|
||||
dList_append(auth_hosts, host);
|
||||
}
|
||||
|
||||
/* find or create the realm */
|
||||
if (!(realm = Auth_realm_by_name(host, data->auth_parse->realm))) {
|
||||
realm = dNew0(AuthRealm_t, 1);
|
||||
realm->name = dStrdup(data->auth_parse->realm);
|
||||
realm->paths = dList_new(1);
|
||||
dList_append(host->realms, realm);
|
||||
}
|
||||
realm->type = data->auth_parse->type;
|
||||
dFree(realm->authorization);
|
||||
realm->authorization = NULL;
|
||||
|
||||
Auth_realm_add_path(realm, URL_PATH(data->url));
|
||||
|
||||
if (realm->type == BASIC) {
|
||||
char *user_password = dStrconcat(user, ":", password, NULL);
|
||||
char *response = a_Misc_encode_base64(user_password);
|
||||
char *authorization =
|
||||
dStrconcat("Authorization: Basic ", response, "\r\n", NULL);
|
||||
dFree(realm->authorization);
|
||||
realm->authorization = authorization;
|
||||
dFree(response);
|
||||
dStrshred(user_password);
|
||||
dFree(user_password);
|
||||
} else if (realm->type == DIGEST) {
|
||||
dFree(realm->username);
|
||||
realm->username = dStrdup(user);
|
||||
realm->nonce_count = 0;
|
||||
dFree(realm->nonce);
|
||||
realm->nonce = dStrdup(data->auth_parse->nonce);
|
||||
dFree(realm->opaque);
|
||||
realm->opaque = dStrdup(data->auth_parse->opaque);
|
||||
realm->algorithm = data->auth_parse->algorithm;
|
||||
dFree(realm->domain);
|
||||
realm->domain = dStrdup(data->auth_parse->domain);
|
||||
realm->qop = data->auth_parse->qop;
|
||||
dFree(realm->cnonce);
|
||||
if (realm->qop != QOPNOTSET)
|
||||
realm->cnonce = a_Digest_create_cnonce();
|
||||
if (!a_Digest_compute_digest(realm, user, password)) {
|
||||
MSG("Auth_do_auth_dialog_cb: a_Digest_compute_digest failed.\n");
|
||||
dList_remove_fast(host->realms, realm);
|
||||
Auth_realm_delete(realm);
|
||||
}
|
||||
} else {
|
||||
MSG("Auth_do_auth_dialog_cb: Unknown auth type: %i\n",
|
||||
realm->type);
|
||||
}
|
||||
dStrshred((char *)password);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return: Nonzero if we got new credentials from the user and everything
|
||||
* seems fine.
|
||||
*/
|
||||
static int Auth_do_auth_dialog(const AuthParse_t *auth_parse,
|
||||
const DilloUrl *url)
|
||||
{
|
||||
int ret;
|
||||
char *title, *msg;
|
||||
AuthDialogData_t *data;
|
||||
const char *typestr = auth_parse->type == DIGEST ? "Digest" : "Basic";
|
||||
|
||||
_MSG("auth.c: Auth_do_auth_dialog: realm = '%s'\n", auth_parse->realm);
|
||||
|
||||
title = dStrconcat("Dillo: Password for ", auth_parse->realm, NULL);
|
||||
msg = dStrconcat("The server at ", URL_HOST(url), " requires a username"
|
||||
" and password for \"", auth_parse->realm, "\".\n\n"
|
||||
"Authentication scheme: ", typestr, NULL);
|
||||
data = dNew(AuthDialogData_t, 1);
|
||||
data->auth_parse = auth_parse;
|
||||
data->url = a_Url_dup(url);
|
||||
ret = a_Dialog_user_password(title, msg, Auth_do_auth_dialog_cb, data);
|
||||
dFree(title); dFree(msg);
|
||||
a_Url_free((void *)data->url);
|
||||
dFree(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do authorization for an auth string.
|
||||
*/
|
||||
static int Auth_do_auth(char *challenge, enum AuthParseHTTPAuthType_t type,
|
||||
const DilloUrl *url)
|
||||
{
|
||||
AuthParse_t *auth_parse;
|
||||
int reload = 0;
|
||||
|
||||
_MSG("auth.c: Auth_do_auth: challenge={%s}\n", challenge);
|
||||
auth_parse = Auth_parse_new();
|
||||
auth_parse->type = type;
|
||||
Auth_parse_challenge(auth_parse, challenge);
|
||||
if (auth_parse->ok)
|
||||
reload =
|
||||
Auth_do_auth_required(auth_parse, url) ?
|
||||
Auth_do_auth_dialog(auth_parse, url)
|
||||
: 1;
|
||||
Auth_parse_free(auth_parse);
|
||||
|
||||
return reload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given authentication challenge(s), prepare authorization.
|
||||
* Return: 0 on failure
|
||||
* nonzero on success. A new query will be sent to the server.
|
||||
*/
|
||||
int a_Auth_do_auth(Dlist *challenges, const DilloUrl *url)
|
||||
{
|
||||
int i;
|
||||
char *chal;
|
||||
|
||||
for (i = 0; (chal = dList_nth_data(challenges, i)); ++i)
|
||||
if (!dStrnAsciiCasecmp(chal, "Digest ", 7))
|
||||
if (Auth_do_auth(chal, DIGEST, url))
|
||||
return 1;
|
||||
for (i = 0; (chal = dList_nth_data(challenges, i)); ++i)
|
||||
if (!dStrnAsciiCasecmp(chal, "Basic ", 6))
|
||||
if (Auth_do_auth(chal, BASIC, url))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
40
src/auth.h
Normal file
40
src/auth.h
Normal file
@ -0,0 +1,40 @@
|
||||
#ifndef __AUTH_H__
|
||||
#define __AUTH_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#include "url.h"
|
||||
|
||||
enum AuthParseHTTPAuthType_t { TYPENOTSET, BASIC, DIGEST };
|
||||
enum AuthParseDigestAlgorithm_t { ALGORITHMNOTSET, MD5, MD5SESS };
|
||||
enum AuthParseDigestQOP_t { QOPNOTSET, AUTH, AUTHINT };
|
||||
|
||||
typedef struct {
|
||||
enum AuthParseHTTPAuthType_t type;
|
||||
char *name;
|
||||
Dlist *paths; /* stripped of any trailing '/', so the root path is "" */
|
||||
char *authorization; /* BASIC: the authorization request header */
|
||||
/* DIGEST: the hexdigest of A1 */
|
||||
/* digest state ahead */
|
||||
char *username;
|
||||
char *cnonce;
|
||||
unsigned int nonce_count;
|
||||
char *nonce;
|
||||
char *opaque;
|
||||
enum AuthParseDigestAlgorithm_t algorithm;
|
||||
char *domain; /* NOT USED */
|
||||
enum AuthParseDigestQOP_t qop;
|
||||
} AuthRealm_t;
|
||||
|
||||
|
||||
char *a_Auth_get_auth_str(const DilloUrl *url, const char *request_uri);
|
||||
int a_Auth_do_auth(Dlist *auth_string, const DilloUrl *url);
|
||||
void a_Auth_init(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* !__AUTH_H__ */
|
||||
45
src/binaryconst.h
Normal file
45
src/binaryconst.h
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef __BINARYCONST_H__
|
||||
#define __BINARYCONST_H__
|
||||
|
||||
/* @file
|
||||
*
|
||||
* Macros for allowing binary constants in C.
|
||||
* By Tom Torfs - donated to the public domain
|
||||
*
|
||||
* binaryconst.h was integrated into the Dillo project in April 2004, and
|
||||
* presumably comes from the ancestor of the code found at
|
||||
* http://cprog.tomsweb.net/binconst.txt
|
||||
*/
|
||||
|
||||
#define HEX__(n) 0x##n##LU
|
||||
|
||||
/* 8-bit conversion function */
|
||||
#define B8__(x) ((x&0x0000000FLU)?1:0) \
|
||||
+((x&0x000000F0LU)?2:0) \
|
||||
+((x&0x00000F00LU)?4:0) \
|
||||
+((x&0x0000F000LU)?8:0) \
|
||||
+((x&0x000F0000LU)?16:0) \
|
||||
+((x&0x00F00000LU)?32:0) \
|
||||
+((x&0x0F000000LU)?64:0) \
|
||||
+((x&0xF0000000LU)?128:0)
|
||||
|
||||
|
||||
/*
|
||||
* *** USER MACROS ***
|
||||
*/
|
||||
|
||||
/* for upto 8-bit binary constants */
|
||||
#define B8(d) ((unsigned char)B8__(HEX__(d)))
|
||||
|
||||
/* for upto 16-bit binary constants, MSB first */
|
||||
#define B16(dmsb,dlsb) (((unsigned short)B8(dmsb)<<8) + B8(dlsb))
|
||||
|
||||
/*
|
||||
* Sample usage:
|
||||
* B8(01010101) = 85
|
||||
* B16(10101010,01010101) = 43605
|
||||
*/
|
||||
|
||||
|
||||
#endif /* __BINARYCONST_H__ */
|
||||
|
||||
67
src/bitvec.c
Normal file
67
src/bitvec.c
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* File: bitvec.c
|
||||
*
|
||||
* Copyright 2001 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.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* A simple ADT for bit arrays
|
||||
*/
|
||||
|
||||
#include "../dlib/dlib.h"
|
||||
#include "bitvec.h"
|
||||
|
||||
|
||||
/**
|
||||
* Create a new bitvec with 'num_bits' size
|
||||
*/
|
||||
bitvec_t *a_Bitvec_new(int num_bits)
|
||||
{
|
||||
bitvec_t *bvec = dNew(bitvec_t, 1);
|
||||
|
||||
bvec->vec = dNew0(uchar_t, num_bits/BVEC_SIZE + 1);
|
||||
bvec->len = num_bits;
|
||||
return bvec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a bitvec
|
||||
*/
|
||||
void a_Bitvec_clear(bitvec_t *bvec)
|
||||
{
|
||||
memset(bvec->vec, 0, sizeof(uchar_t) * bvec->len/BVEC_SIZE + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a bitvec
|
||||
*/
|
||||
void a_Bitvec_free(bitvec_t *bvec)
|
||||
{
|
||||
if (bvec) {
|
||||
dFree(bvec->vec);
|
||||
dFree(bvec);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a bit
|
||||
*/
|
||||
int a_Bitvec_get_bit(bitvec_t *bvec, int pos)
|
||||
{
|
||||
dReturn_val_if_fail (pos < bvec->len, 0);
|
||||
return (bvec->vec[pos/BVEC_SIZE] & 1 << pos % BVEC_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a bit
|
||||
*/
|
||||
void a_Bitvec_set_bit(bitvec_t *bvec, int pos)
|
||||
{
|
||||
dReturn_if_fail (pos < bvec->len);
|
||||
bvec->vec[pos/BVEC_SIZE] |= 1 << (pos % BVEC_SIZE);
|
||||
}
|
||||
35
src/bitvec.h
Normal file
35
src/bitvec.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef __BITVEC_H__
|
||||
#define __BITVEC_H__
|
||||
|
||||
#include "d_size.h"
|
||||
|
||||
#define BVEC_TYPE uchar_t
|
||||
#define BVEC_SIZE sizeof(BVEC_TYPE)
|
||||
|
||||
typedef struct {
|
||||
BVEC_TYPE *vec;
|
||||
int len; /* number of bits [1 based] */
|
||||
} bitvec_t;
|
||||
|
||||
|
||||
/*
|
||||
* Function prototypes
|
||||
*/
|
||||
bitvec_t *a_Bitvec_new(int bits);
|
||||
void a_Bitvec_free(bitvec_t *bvec);
|
||||
int a_Bitvec_get_bit(bitvec_t *bvec, int pos);
|
||||
void a_Bitvec_set_bit(bitvec_t *bvec, int pos);
|
||||
void a_Bitvec_clear(bitvec_t *bvec);
|
||||
|
||||
/*
|
||||
#define a_Bitvec_get_bit(bvec,pos) \
|
||||
((bvec)->vec[(pos)/BVEC_SIZE] & 1 << (pos) % BVEC_SIZE)
|
||||
|
||||
#define a_Bitvec_set_bit(bvec,pos) \
|
||||
((bvec)->vec[(pos)/BVEC_SIZE] |= 1 << (pos) % BVEC_SIZE)
|
||||
*/
|
||||
#define a_Bitvec_clear_bit(bvec,pos) \
|
||||
((bvec)->vec[(pos)/BVEC_SIZE] &= ~(1 << (pos) % BVEC_SIZE))
|
||||
|
||||
|
||||
#endif /* __BITVEC_H__ */
|
||||
86
src/bookmark.c
Normal file
86
src/bookmark.c
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* File: bookmark.c
|
||||
*
|
||||
* Copyright 2002-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.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "msg.h"
|
||||
#include "history.h"
|
||||
#include "capi.h"
|
||||
#include "bookmark.h" /* for prototypes */
|
||||
#include "../dpip/dpip.h"
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Have a short chat with the bookmarks server,
|
||||
* and finally ask it to add a new bookmark.
|
||||
* (this is an example of dpi chat)
|
||||
*/
|
||||
void a_Bookmarks_chat_add(BrowserWindow *Bw, char *Cmd, char *answer)
|
||||
{
|
||||
static char *cmd1 = NULL, *cmd2 = NULL, *cmd3 = NULL, *cmd4 = NULL;
|
||||
static BrowserWindow *bw = NULL;
|
||||
|
||||
if (!cmd1) {
|
||||
cmd1 = a_Dpip_build_cmd("cmd=%s msg=%s", "chat", "Hi server");
|
||||
cmd2 = a_Dpip_build_cmd("cmd=%s msg=%s", "chat",
|
||||
"I want to set a bookmark");
|
||||
cmd3 = a_Dpip_build_cmd("cmd=%s msg=%s", "chat", "Sure it is!");
|
||||
}
|
||||
|
||||
_MSG("a_Bookmarks_chat_add\n answer=%s\n", answer ? answer : "(null)");
|
||||
|
||||
if (Bw)
|
||||
bw = Bw;
|
||||
if (!cmd4 && Cmd)
|
||||
cmd4 = dStrdup(Cmd);
|
||||
|
||||
if (!answer) {
|
||||
a_Capi_dpi_send_cmd(NULL, bw, cmd1, "bookmarks", 1);
|
||||
|
||||
} else {
|
||||
/* we have an answer */
|
||||
if (answer) {
|
||||
if (*answer == 'H') {
|
||||
/* "Hi browser" */
|
||||
a_Capi_dpi_send_cmd(NULL, bw, cmd2, "bookmarks", 0);
|
||||
} else if (*answer == 'I') {
|
||||
/* "Is it worth?" */
|
||||
a_Capi_dpi_send_cmd(NULL, bw, cmd3, "bookmarks", 0);
|
||||
} else if (*answer == 'O') {
|
||||
/* "OK, send it!" */
|
||||
a_Capi_dpi_send_cmd(NULL, bw, cmd4, "bookmarks", 0);
|
||||
dFree(cmd4);
|
||||
cmd4 = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the new bookmark through the bookmarks server
|
||||
*/
|
||||
void a_Bookmarks_add(BrowserWindow *bw, const DilloUrl *url)
|
||||
{
|
||||
const char *title;
|
||||
char *cmd;
|
||||
|
||||
dReturn_if_fail(url != NULL);
|
||||
|
||||
/* if the page has no title, we'll use the url string */
|
||||
title = a_History_get_title_by_url(url, 1);
|
||||
|
||||
cmd = a_Dpip_build_cmd("cmd=%s url=%s title=%s",
|
||||
"add_bookmark", URL_STR(url), title);
|
||||
a_Bookmarks_chat_add(bw, cmd, NULL);
|
||||
dFree(cmd);
|
||||
}
|
||||
|
||||
19
src/bookmark.h
Normal file
19
src/bookmark.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef __BOOKMARK_H__
|
||||
#define __BOOKMARK_H__
|
||||
|
||||
#include "bw.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void a_Bookmarks_add(BrowserWindow *bw, const DilloUrl *url);
|
||||
|
||||
/* TODO: this is for testing purposes */
|
||||
void a_Bookmarks_chat_add(BrowserWindow *Bw, char *Cmd, char *answer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __BOOKMARK_H__ */
|
||||
343
src/bw.c
Normal file
343
src/bw.c
Normal file
@ -0,0 +1,343 @@
|
||||
/*
|
||||
* File: bw.c
|
||||
*
|
||||
* Copyright (C) 2006-2007 Jorge Arellano Cid <jcid@dillo.org>
|
||||
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* Data structures for each browser window
|
||||
*/
|
||||
|
||||
|
||||
#include "bw.h"
|
||||
#include "msg.h"
|
||||
#include "list.h"
|
||||
#include "capi.h"
|
||||
#include "uicmd.hh"
|
||||
|
||||
|
||||
/*
|
||||
* Local Data
|
||||
*/
|
||||
/* A list of working browser windows */
|
||||
static BrowserWindow **bws;
|
||||
static int num_bws, num_bws_max;
|
||||
|
||||
|
||||
/**
|
||||
* Initialize global data
|
||||
*/
|
||||
void a_Bw_init(void)
|
||||
{
|
||||
num_bws = 0;
|
||||
num_bws_max = 16;
|
||||
bws = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new browser window and return it.
|
||||
* (the new window is stored in browser_window[])
|
||||
*/
|
||||
BrowserWindow *a_Bw_new(void)
|
||||
{
|
||||
BrowserWindow *bw;
|
||||
|
||||
/* We use dNew0() to zero the memory */
|
||||
bw = dNew0(BrowserWindow, 1);
|
||||
a_List_add(bws, num_bws, num_bws_max);
|
||||
bws[num_bws++] = bw;
|
||||
|
||||
/* Initialize nav_stack */
|
||||
bw->nav_stack = dList_new(8);
|
||||
bw->nav_stack_ptr = -1;
|
||||
|
||||
/* Init expect */
|
||||
bw->nav_expect_url = NULL;
|
||||
|
||||
bw->redirect_level = 0;
|
||||
bw->meta_refresh_status = 0;
|
||||
bw->meta_refresh_url = NULL;
|
||||
|
||||
bw->RootClients = dList_new(8);
|
||||
bw->ImageClients = dList_new(8);
|
||||
bw->NumImages = 0;
|
||||
bw->NumImagesGot = 0;
|
||||
bw->NumPendingStyleSheets = 0;
|
||||
bw->PageUrls = dList_new(8);
|
||||
bw->Docs = dList_new(8);
|
||||
|
||||
bw->num_page_bugs = 0;
|
||||
bw->page_bugs = dStr_new("");
|
||||
|
||||
bw->zoom = 1.0;
|
||||
|
||||
/* now that the bw is made, let's customize it.. */
|
||||
//Interface_browser_window_customize(bw);
|
||||
|
||||
return bw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free resources associated to a bw.
|
||||
*/
|
||||
void a_Bw_free(BrowserWindow *bw)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < num_bws; i++) {
|
||||
if (bws[i] == bw) {
|
||||
a_List_remove(bws, i, num_bws);
|
||||
|
||||
dList_free(bw->RootClients);
|
||||
dList_free(bw->ImageClients);
|
||||
dList_free(bw->Docs);
|
||||
|
||||
a_Url_free(bw->nav_expect_url);
|
||||
for (j = 0; j < dList_length(bw->PageUrls); ++j)
|
||||
a_Url_free(dList_nth_data(bw->PageUrls, j));
|
||||
dList_free(bw->PageUrls);
|
||||
|
||||
for (j = 0; j < dList_length(bw->nav_stack); ++j)
|
||||
dFree(dList_nth_data(bw->nav_stack, j));
|
||||
dList_free(bw->nav_stack);
|
||||
|
||||
a_Url_free(bw->meta_refresh_url);
|
||||
|
||||
dStr_free(bw->page_bugs, 1);
|
||||
dFree(bw);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*- Clients ----------------------------------------------------------------*/
|
||||
/**
|
||||
* Add a reference to a cache-client. It is kept int this bw's list.
|
||||
* This helps us keep track of which are active in the window so that it's
|
||||
* possible to abort/stop them.
|
||||
* (Root: Flag, whether a Root URL or not)
|
||||
*
|
||||
* TODO: Make NumImages count different images.
|
||||
*/
|
||||
void a_Bw_add_client(BrowserWindow *bw, int Key, int Root)
|
||||
{
|
||||
dReturn_if_fail ( bw != NULL );
|
||||
|
||||
if (Root) {
|
||||
dList_append(bw->RootClients, INT2VOIDP(Key));
|
||||
} else {
|
||||
dList_append(bw->ImageClients, INT2VOIDP(Key));
|
||||
bw->NumImages++;
|
||||
/* --Images progress-bar stuff-- */
|
||||
a_UIcmd_set_img_prog(bw, bw->NumImagesGot, bw->NumImages, 1);
|
||||
}
|
||||
if (dList_length(bw->RootClients) + dList_length(bw->ImageClients) == 1)
|
||||
a_UIcmd_set_buttons_sens(bw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the cache-client from the bw's list
|
||||
* (client can be a image or a html page)
|
||||
* Return: 0 if found, 1 otherwise.
|
||||
*/
|
||||
int a_Bw_remove_client(BrowserWindow *bw, int ClientKey)
|
||||
{
|
||||
void *data;
|
||||
|
||||
if ((data = dList_find(bw->RootClients, INT2VOIDP(ClientKey)))) {
|
||||
dList_remove_fast(bw->RootClients, data);
|
||||
} else if ((data = dList_find(bw->ImageClients, INT2VOIDP(ClientKey)))) {
|
||||
dList_remove_fast(bw->ImageClients, data);
|
||||
++bw->NumImagesGot;
|
||||
}
|
||||
return data ? 0 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a cache-client upon successful retrieval.
|
||||
* Remove the cache-client from the bw list and update the meters.
|
||||
* (client can be a image or a html page)
|
||||
*/
|
||||
void a_Bw_close_client(BrowserWindow *bw, int ClientKey)
|
||||
{
|
||||
if (a_Bw_remove_client(bw, ClientKey) == 0) {
|
||||
a_UIcmd_set_img_prog(bw, bw->NumImagesGot, bw->NumImages, 1);
|
||||
if (bw->NumImagesGot == bw->NumImages)
|
||||
a_UIcmd_set_img_prog(bw, 0, 0, 0);
|
||||
if (dList_length(bw->RootClients) == 0)
|
||||
a_UIcmd_set_buttons_sens(bw);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the active clients of this bw's top page.
|
||||
* Note: rendering stops, but the cache continues to be fed.
|
||||
*/
|
||||
void a_Bw_stop_clients(BrowserWindow *bw, int flags)
|
||||
{
|
||||
void *data;
|
||||
|
||||
if (flags & BW_Root) {
|
||||
/* Remove root clients */
|
||||
while ((data = dList_nth_data(bw->RootClients, 0))) {
|
||||
a_Capi_stop_client(VOIDP2INT(data), (flags & BW_Force));
|
||||
dList_remove_fast(bw->RootClients, data);
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & BW_Img) {
|
||||
/* Remove image clients */
|
||||
while ((data = dList_nth_data(bw->ImageClients, 0))) {
|
||||
a_Capi_stop_client(VOIDP2INT(data), (flags & BW_Force));
|
||||
dList_remove_fast(bw->ImageClients, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*- Page -------------------------------------------------------------------*/
|
||||
/**
|
||||
* Add an URL to the browser window's list.
|
||||
* This helps us keep track of page-requested URLs so that it's
|
||||
* possible to stop, abort and reload them.
|
||||
*/
|
||||
void a_Bw_add_url(BrowserWindow *bw, const DilloUrl *Url)
|
||||
{
|
||||
dReturn_if_fail ( bw != NULL && Url != NULL );
|
||||
|
||||
if (!dList_find_custom(bw->PageUrls, Url, (dCompareFunc)a_Url_cmp)) {
|
||||
dList_append(bw->PageUrls, a_Url_dup(Url));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a document to the browser window's list.
|
||||
*/
|
||||
void a_Bw_add_doc(BrowserWindow *bw, void *vdoc)
|
||||
{
|
||||
dReturn_if_fail ( bw != NULL && vdoc != NULL);
|
||||
|
||||
dList_append(bw->Docs, vdoc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current document.
|
||||
*/
|
||||
void *a_Bw_get_current_doc(BrowserWindow *bw)
|
||||
{
|
||||
void *doc = NULL;
|
||||
int len = dList_length(bw->Docs);
|
||||
|
||||
if (len == 1)
|
||||
doc = dList_nth_data(bw->Docs, 0);
|
||||
else if (len > 1)
|
||||
MSG("a_Bw_get_current_doc() multiple docs not implemented\n");
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get document by URL.
|
||||
*
|
||||
* This is currently used by popup menus that need to ensure that the
|
||||
* page has not changed while the menu was popped up.
|
||||
*/
|
||||
void *a_Bw_get_url_doc(BrowserWindow *bw, const DilloUrl *url)
|
||||
{
|
||||
void *doc = NULL;
|
||||
|
||||
if (url && dList_find_custom(bw->PageUrls, url, (dCompareFunc)a_Url_cmp)) {
|
||||
doc = a_Bw_get_current_doc(bw);
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a document from the bw's list
|
||||
*/
|
||||
void a_Bw_remove_doc(BrowserWindow *bw, void *vdoc)
|
||||
{
|
||||
void *data;
|
||||
|
||||
if ((data = dList_find(bw->Docs, vdoc))) {
|
||||
dList_remove_fast(bw->Docs, data);
|
||||
}
|
||||
}
|
||||
|
||||
/*- Cleanup ----------------------------------------------------------------*/
|
||||
/**
|
||||
* Empty RootClients, ImageClients and PageUrls lists and
|
||||
* reset progress bar data.
|
||||
*/
|
||||
void a_Bw_cleanup(BrowserWindow *bw)
|
||||
{
|
||||
void *data;
|
||||
|
||||
/* Remove root clients */
|
||||
while ((data = dList_nth_data(bw->RootClients, 0))) {
|
||||
dList_remove_fast(bw->RootClients, data);
|
||||
}
|
||||
/* Remove image clients */
|
||||
while ((data = dList_nth_data(bw->ImageClients, 0))) {
|
||||
dList_remove_fast(bw->ImageClients, data);
|
||||
}
|
||||
/* Remove PageUrls */
|
||||
while ((data = dList_nth_data(bw->PageUrls, 0))) {
|
||||
a_Url_free(data);
|
||||
dList_remove_fast(bw->PageUrls, data);
|
||||
}
|
||||
|
||||
/* Zero image-progress data */
|
||||
bw->NumImages = 0;
|
||||
bw->NumImagesGot = 0;
|
||||
|
||||
/* Zero stylesheet counter */
|
||||
bw->NumPendingStyleSheets = 0;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
int a_Bw_num(void)
|
||||
{
|
||||
return num_bws;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a bw by index
|
||||
*/
|
||||
BrowserWindow *a_Bw_get(int i)
|
||||
{
|
||||
if (i >= 0 && i < num_bws)
|
||||
return bws[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* expect API ------------------------------------------------------------- */
|
||||
|
||||
void a_Bw_expect(BrowserWindow *bw, const DilloUrl *url)
|
||||
{
|
||||
a_Url_free(bw->nav_expect_url);
|
||||
bw->nav_expect_url = a_Url_dup(url);
|
||||
}
|
||||
|
||||
void a_Bw_cancel_expect(BrowserWindow *bw)
|
||||
{
|
||||
a_Url_free(bw->nav_expect_url);
|
||||
bw->nav_expect_url = NULL;
|
||||
}
|
||||
|
||||
bool_t a_Bw_expecting(BrowserWindow *bw)
|
||||
{
|
||||
return (bw->nav_expect_url != NULL);
|
||||
}
|
||||
|
||||
const DilloUrl *a_Bw_expected_url(BrowserWindow *bw)
|
||||
{
|
||||
return bw->nav_expect_url;
|
||||
}
|
||||
|
||||
112
src/bw.h
Normal file
112
src/bw.h
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* File: bw.h
|
||||
*
|
||||
* Copyright (C) 2006-2011 Jorge Arellano Cid <jcid@dillo.org>
|
||||
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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 __BW_H__
|
||||
#define __BW_H__
|
||||
|
||||
#include "url.h" /* for DilloUrl */
|
||||
|
||||
/*
|
||||
* Flag Defines for a_Bw_stop_clients()
|
||||
*/
|
||||
#define BW_Root (1) /* Root URLs */
|
||||
#define BW_Img (2) /* Image URLs */
|
||||
#define BW_Force (4) /* Stop connection too */
|
||||
|
||||
|
||||
/** Contains the specific data for a single window */
|
||||
typedef struct {
|
||||
/** Pointer to the UI object this bw belongs to */
|
||||
void *ui;
|
||||
|
||||
/** All the rendering is done by this.
|
||||
* It is defined as a void pointer to avoid C++ in this structure.
|
||||
* C++ sources have to include "dw/core.hh" and cast it into an object. */
|
||||
void *render_layout;
|
||||
|
||||
/** Root document(s). Currently only used by DilloHtml */
|
||||
Dlist *Docs;
|
||||
|
||||
/** A list of active cache clients in the window (The primary Key) */
|
||||
Dlist *RootClients;
|
||||
/** Image Keys for all active connections in the window */
|
||||
Dlist *ImageClients;
|
||||
/** Number of images in the page */
|
||||
int NumImages;
|
||||
/** Number of images already loaded */
|
||||
int NumImagesGot;
|
||||
/** Number of not yet arrived style sheets */
|
||||
int NumPendingStyleSheets;
|
||||
/** List of all Urls requested by this page (and its types) */
|
||||
Dlist *PageUrls;
|
||||
|
||||
/** The navigation stack (holds indexes to history list) */
|
||||
Dlist *nav_stack;
|
||||
/** 'nav_stack_ptr' refers to what's being displayed */
|
||||
int nav_stack_ptr; /* [0 based; -1 = empty] */
|
||||
/** When the user clicks a link, the URL isn't pushed directly to history;
|
||||
* nav_expect_url holds it until a dw is assigned to it. Only then an entry
|
||||
* is made in history and referenced at the top of nav_stack */
|
||||
DilloUrl *nav_expect_url;
|
||||
|
||||
/** Counter for the number of hops on a redirection. Used to stop
|
||||
* redirection loops (accounts for WEB_RootUrl only) */
|
||||
int redirect_level;
|
||||
|
||||
/** Url for zero-delay redirections in the META element */
|
||||
int meta_refresh_status;
|
||||
DilloUrl *meta_refresh_url;
|
||||
|
||||
/** HTML-bugs detected at parse time */
|
||||
int num_page_bugs;
|
||||
Dstr *page_bugs;
|
||||
|
||||
/* Zoom factor */
|
||||
float zoom;
|
||||
} BrowserWindow;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
void a_Bw_init(void);
|
||||
BrowserWindow *a_Bw_new(void);
|
||||
void a_Bw_free(BrowserWindow *bw);
|
||||
BrowserWindow *a_Bw_get(int i);
|
||||
int a_Bw_num(void);
|
||||
|
||||
void a_Bw_add_client(BrowserWindow *bw, int Key, int Root);
|
||||
int a_Bw_remove_client(BrowserWindow *bw, int ClientKey);
|
||||
void a_Bw_close_client(BrowserWindow *bw, int ClientKey);
|
||||
void a_Bw_stop_clients(BrowserWindow *bw, int flags);
|
||||
void a_Bw_add_doc(BrowserWindow *bw, void *vdoc);
|
||||
void *a_Bw_get_current_doc(BrowserWindow *bw);
|
||||
void *a_Bw_get_url_doc(BrowserWindow *bw, const DilloUrl *Url);
|
||||
void a_Bw_remove_doc(BrowserWindow *bw, void *vdoc);
|
||||
void a_Bw_add_url(BrowserWindow *bw, const DilloUrl *Url);
|
||||
void a_Bw_cleanup(BrowserWindow *bw);
|
||||
/* expect API */
|
||||
void a_Bw_expect(BrowserWindow *bw, const DilloUrl *Url);
|
||||
void a_Bw_cancel_expect(BrowserWindow *bw);
|
||||
bool_t a_Bw_expecting(BrowserWindow *bw);
|
||||
const DilloUrl *a_Bw_expected_url(BrowserWindow *bw);
|
||||
|
||||
typedef void (*BwCallback_t)(BrowserWindow *bw, const void *data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __BROWSER_H__ */
|
||||
|
||||
1464
src/cache.c
Normal file
1464
src/cache.c
Normal file
File diff suppressed because it is too large
Load Diff
84
src/cache.h
Normal file
84
src/cache.h
Normal file
@ -0,0 +1,84 @@
|
||||
#ifndef __CACHE_H__
|
||||
#define __CACHE_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#include "chain.h"
|
||||
#include "url.h"
|
||||
|
||||
/*
|
||||
* Cache Op codes
|
||||
*/
|
||||
#define CA_Send (0) /* Normal update */
|
||||
#define CA_Close (1) /* Successful operation close */
|
||||
#define CA_Abort (2) /* Operation abort */
|
||||
|
||||
/*
|
||||
* Flag Defines
|
||||
*/
|
||||
#define CA_GotHeader 0x1 /* True after header is completely got */
|
||||
#define CA_GotContentType 0x2 /* True after Content-Type is known */
|
||||
#define CA_GotLength 0x4 /* True if Content-Length is known */
|
||||
#define CA_InProgress 0x8 /* True if we are getting data */
|
||||
#define CA_Redirect 0x10 /* Data actually points to a redirect */
|
||||
#define CA_ForceRedirect 0x20 /* Unconditional redirect */
|
||||
#define CA_TempRedirect 0x40 /* Temporary redirect */
|
||||
#define CA_NotFound 0x80 /* True if remote server didn't find the URL */
|
||||
#define CA_Aborted 0x100 /* Aborted before getting full data */
|
||||
#define CA_MsgErased 0x200 /* Used to erase the bw's status bar */
|
||||
#define CA_RedirectLoop 0x400 /* Redirect loop */
|
||||
#define CA_InternalUrl 0x800 /* URL content is generated by dillo */
|
||||
#define CA_HugeFile 0x1000 /* URL content is too big */
|
||||
#define CA_IsEmpty 0x2000 /* True until a byte of content arrives */
|
||||
#define CA_KeepAlive 0x4000
|
||||
|
||||
typedef struct CacheClient CacheClient_t;
|
||||
|
||||
/**
|
||||
* Callback type for cache clients
|
||||
*/
|
||||
typedef void (*CA_Callback_t)(int Op, CacheClient_t *Client);
|
||||
|
||||
/**
|
||||
* Data structure for cache clients.
|
||||
*/
|
||||
struct CacheClient {
|
||||
int Key; /**< Primary Key for this client */
|
||||
const DilloUrl *Url; /**< Pointer to a cache entry Url */
|
||||
int Version; /**< Dicache version of this Url (0 if not used) */
|
||||
void *Buf; /**< Pointer to cache-data */
|
||||
uint_t BufSize; /**< Valid size of cache-data */
|
||||
CA_Callback_t Callback; /**< Client function */
|
||||
void *CbData; /**< Client function data */
|
||||
void *Web; /**< Pointer to the Web structure of our client */
|
||||
};
|
||||
|
||||
/*
|
||||
* Function prototypes
|
||||
*/
|
||||
void a_Cache_init(void);
|
||||
int a_Cache_open_url(void *Web, CA_Callback_t Call, void *CbData);
|
||||
int a_Cache_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize);
|
||||
void a_Cache_unref_buf(const DilloUrl *Url);
|
||||
const char *a_Cache_get_content_type(const DilloUrl *url);
|
||||
const char *a_Cache_set_content_type(const DilloUrl *url, const char *ctype,
|
||||
const char *from);
|
||||
uint_t a_Cache_get_flags(const DilloUrl *url);
|
||||
uint_t a_Cache_get_flags_with_redirection(const DilloUrl *url);
|
||||
bool_t a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size,
|
||||
const DilloUrl *Url);
|
||||
int a_Cache_download_enabled(const DilloUrl *url);
|
||||
void a_Cache_entry_remove_by_url(DilloUrl *url);
|
||||
void a_Cache_freeall(void);
|
||||
CacheClient_t *a_Cache_client_get_if_unique(int Key);
|
||||
void a_Cache_stop_client(int Key);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* __CACHE_H__ */
|
||||
|
||||
835
src/capi.c
Normal file
835
src/capi.c
Normal file
@ -0,0 +1,835 @@
|
||||
/*
|
||||
* File: capi.c
|
||||
*
|
||||
* Copyright 2002-2007 Jorge Arellano Cid <jcid@dillo.org>
|
||||
* Copyright 2023-2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Cache API.
|
||||
* This is the module that manages the cache and starts the CCC chains
|
||||
* to get the requests served. Kind of a broker.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "msg.h"
|
||||
#include "capi.h"
|
||||
#include "IO/IO.h" /* for IORead &friends */
|
||||
#include "IO/Url.h"
|
||||
#include "chain.h"
|
||||
#include "history.h"
|
||||
#include "nav.h"
|
||||
#include "dpiapi.h"
|
||||
#include "uicmd.hh"
|
||||
#include "domain.h"
|
||||
#include "../dpip/dpip.h"
|
||||
#include "prefs.h"
|
||||
|
||||
/* for testing dpi chat */
|
||||
#include "bookmark.h"
|
||||
|
||||
typedef struct {
|
||||
DilloUrl *url; /**< local copy of web->url */
|
||||
void *bw;
|
||||
char *server;
|
||||
char *datastr;
|
||||
int SockFD;
|
||||
int Flags;
|
||||
ChainLink *InfoSend;
|
||||
ChainLink *InfoRecv;
|
||||
|
||||
int Ref;
|
||||
} capi_conn_t;
|
||||
|
||||
/** Flags for conn */
|
||||
enum {
|
||||
PENDING = 1,
|
||||
TIMEOUT = 2, /* unused */
|
||||
ABORTED = 4
|
||||
};
|
||||
|
||||
/*
|
||||
* Local data
|
||||
*/
|
||||
/** Data list for active dpi connections */
|
||||
static Dlist *CapiConns; /* Data list for active connections; it holds
|
||||
* pointers to capi_conn_t structures. */
|
||||
/** Last URL asked for view source */
|
||||
static DilloUrl *CapiVsUrl = NULL;
|
||||
|
||||
/*
|
||||
* Forward declarations
|
||||
*/
|
||||
void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
|
||||
void *Data1, void *Data2);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Initialize capi&cache data
|
||||
*/
|
||||
void a_Capi_init(void)
|
||||
{
|
||||
/* create an empty list */
|
||||
CapiConns = dList_new(32);
|
||||
/* init cache */
|
||||
a_Cache_init();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Create a new connection data structure
|
||||
*/
|
||||
static capi_conn_t *
|
||||
Capi_conn_new(const DilloUrl *url, void *bw, char *server, char *datastr)
|
||||
{
|
||||
capi_conn_t *conn;
|
||||
|
||||
conn = dNew(capi_conn_t, 1);
|
||||
conn->url = url ? a_Url_dup(url) : NULL;
|
||||
conn->bw = bw;
|
||||
conn->server = dStrdup(server);
|
||||
conn->datastr = dStrdup(datastr);
|
||||
conn->SockFD = -1;
|
||||
conn->Flags = (strcmp(server, "http") != 0) ? PENDING : 0;
|
||||
conn->InfoSend = NULL;
|
||||
conn->InfoRecv = NULL;
|
||||
conn->Ref = 0; /* Reference count */
|
||||
return conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a capi_conn_t pointer.
|
||||
* @return NULL if not valid, conn otherwise.
|
||||
*/
|
||||
static capi_conn_t *Capi_conn_valid(capi_conn_t *conn)
|
||||
{
|
||||
return dList_find(CapiConns, conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the reference count and add to the list if not present
|
||||
*/
|
||||
static void Capi_conn_ref(capi_conn_t *conn)
|
||||
{
|
||||
if (++conn->Ref == 1) {
|
||||
/* add the connection data to list */
|
||||
dList_append(CapiConns, (void *)conn);
|
||||
}
|
||||
_MSG(" Capi_conn_ref #%d %p\n", conn->Ref, conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement the reference count (and remove from list when zero)
|
||||
*/
|
||||
static void Capi_conn_unref(capi_conn_t *conn)
|
||||
{
|
||||
_MSG(" Capi_conn_unref #%d %p\n", conn->Ref - 1, conn);
|
||||
|
||||
/* We may validate conn here, but it doesn't *seem* necessary */
|
||||
if (--conn->Ref == 0) {
|
||||
/* remove conn preserving the list order */
|
||||
dList_remove(CapiConns, (void *)conn);
|
||||
/* free dynamic memory */
|
||||
a_Url_free(conn->url);
|
||||
dFree(conn->server);
|
||||
dFree(conn->datastr);
|
||||
dFree(conn);
|
||||
}
|
||||
_MSG(" Capi_conn_unref CapiConns=%d\n", dList_length(CapiConns));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare function for searching a conn by server string
|
||||
*/
|
||||
static int Capi_conn_by_server_cmp(const void *v1, const void *v2)
|
||||
{
|
||||
const capi_conn_t *node = v1;
|
||||
const char *server = v2;
|
||||
dReturn_val_if_fail(node && node->server && server, 1);
|
||||
return strcmp(node->server, server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find connection data by server
|
||||
*/
|
||||
static capi_conn_t *Capi_conn_find(char *server)
|
||||
{
|
||||
return dList_find_custom(CapiConns, (void*)server, Capi_conn_by_server_cmp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume connections that were waiting for dpid to start.
|
||||
*/
|
||||
static void Capi_conn_resume(void)
|
||||
{
|
||||
int i;
|
||||
DataBuf *dbuf;
|
||||
capi_conn_t *conn;
|
||||
|
||||
for (i = 0; i < dList_length(CapiConns); ++i) {
|
||||
conn = dList_nth_data (CapiConns, i);
|
||||
if (conn->Flags & PENDING) {
|
||||
dbuf = a_Chain_dbuf_new(conn->datastr,(int)strlen(conn->datastr), 0);
|
||||
if (conn->InfoSend) {
|
||||
a_Capi_ccc(OpSend, 1, BCK, conn->InfoSend, dbuf, NULL);
|
||||
}
|
||||
dFree(dbuf);
|
||||
conn->Flags &= ~PENDING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort the connection for a given url, using its CCC.
|
||||
* (OpAbort 2,BCK removes the cache entry)
|
||||
* TODO: when conn is already done, the cache entry isn't removed.
|
||||
* This may be wrong and needs a revision.
|
||||
*/
|
||||
void a_Capi_conn_abort_by_url(const DilloUrl *url)
|
||||
{
|
||||
int i;
|
||||
capi_conn_t *conn;
|
||||
|
||||
for (i = 0; i < dList_length(CapiConns); ++i) {
|
||||
conn = dList_nth_data (CapiConns, i);
|
||||
if (a_Url_cmp(conn->url, url) == 0) {
|
||||
if (conn->InfoSend) {
|
||||
a_Capi_ccc(OpAbort, 1, BCK, conn->InfoSend, NULL, NULL);
|
||||
}
|
||||
if (conn->InfoRecv) {
|
||||
a_Capi_ccc(OpAbort, 2, BCK, conn->InfoRecv, NULL, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Store the last URL requested by "view source"
|
||||
*/
|
||||
void a_Capi_set_vsource_url(const DilloUrl *url)
|
||||
{
|
||||
a_Url_free(CapiVsUrl);
|
||||
CapiVsUrl = a_Url_dup(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Safety test: only allow GET|POST dpi-urls from dpi-generated pages.
|
||||
*/
|
||||
int a_Capi_dpi_verify_request(BrowserWindow *bw, DilloUrl *url)
|
||||
{
|
||||
const DilloUrl *referer;
|
||||
int allow = FALSE;
|
||||
|
||||
if (dStrAsciiCasecmp(URL_SCHEME(url), "dpi") == 0) {
|
||||
if (!(URL_FLAGS(url) & (URL_Post + URL_Get))) {
|
||||
allow = TRUE;
|
||||
} else if (!(URL_FLAGS(url) & URL_Post) &&
|
||||
strncmp(URL_PATH(url), "/vsource/", 9) == 0) {
|
||||
allow = TRUE;
|
||||
} else {
|
||||
/* only allow GET&POST dpi-requests from dpi-generated urls */
|
||||
if (a_Nav_stack_size(bw)) {
|
||||
referer = a_History_get_url(NAV_TOP_UIDX(bw));
|
||||
if (dStrAsciiCasecmp(URL_SCHEME(referer), "dpi") == 0) {
|
||||
allow = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
allow = TRUE;
|
||||
}
|
||||
|
||||
if (!allow) {
|
||||
MSG("a_Capi_dpi_verify_request: Permission Denied!\n");
|
||||
MSG(" URL_STR : %s\n", URL_STR(url));
|
||||
if (URL_FLAGS(url) & URL_Post) {
|
||||
MSG(" URL_DATA: %s\n", dStr_printable(URL_DATA(url), 1024));
|
||||
}
|
||||
}
|
||||
return allow;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the url belongs to a dpi server, return its name.
|
||||
*/
|
||||
static int Capi_url_uses_dpi(DilloUrl *url, char **server_ptr)
|
||||
{
|
||||
char *p, *server = NULL, *url_str = URL_STR(url);
|
||||
Dstr *tmp;
|
||||
|
||||
if ((dStrnAsciiCasecmp(url_str, "http:", 5) == 0) ||
|
||||
(dStrnAsciiCasecmp(url_str, "https:", 6) == 0) ||
|
||||
(dStrnAsciiCasecmp(url_str, "about:", 6) == 0)) {
|
||||
/* URL doesn't use dpi (server = NULL) */
|
||||
} else if (dStrnAsciiCasecmp(url_str, "dpi:/", 5) == 0) {
|
||||
/* dpi prefix, get this server's name */
|
||||
if ((p = strchr(url_str + 5, '/')) != NULL) {
|
||||
server = dStrndup(url_str + 5, (uint_t)(p - url_str - 5));
|
||||
} else {
|
||||
server = dStrdup("?");
|
||||
}
|
||||
if (strcmp(server, "bm") == 0) {
|
||||
dFree(server);
|
||||
server = dStrdup("bookmarks");
|
||||
}
|
||||
} else if ((p = strchr(url_str, ':')) != NULL) {
|
||||
tmp = dStr_new("proto.");
|
||||
dStr_append_l(tmp, url_str, p - url_str);
|
||||
server = tmp->str;
|
||||
dStr_free(tmp, 0);
|
||||
}
|
||||
|
||||
return ((*server_ptr = server) ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the dpip command tag, according to URL and server.
|
||||
*/
|
||||
static char *Capi_dpi_build_cmd(DilloWeb *web, char *server)
|
||||
{
|
||||
char *cmd;
|
||||
|
||||
if (strcmp(server, "downloads") == 0) {
|
||||
/* let the downloads server get it */
|
||||
cmd = a_Dpip_build_cmd("cmd=%s url=%s destination=%s user-agent=%s",
|
||||
"download", URL_STR(web->url), web->filename,
|
||||
prefs.http_user_agent);
|
||||
|
||||
} else {
|
||||
/* For everyone else, the url string is enough... */
|
||||
cmd = a_Dpip_build_cmd("cmd=%s url=%s", "open_url", URL_STR(web->url));
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the requested URL's source to the "view source" dpi
|
||||
*/
|
||||
static void Capi_dpi_send_source(BrowserWindow *bw, DilloUrl *url)
|
||||
{
|
||||
char *p, *buf, *cmd, size_str[32], *server="vsource";
|
||||
int buf_size;
|
||||
|
||||
if (!(p = strchr(URL_STR(url), ':')) || !(p = strchr(p + 1, ':')))
|
||||
return;
|
||||
|
||||
if (a_Capi_get_buf(CapiVsUrl, &buf, &buf_size)) {
|
||||
/* send the page's source to this dpi connection */
|
||||
snprintf(size_str, 32, "%d", buf_size);
|
||||
cmd = a_Dpip_build_cmd("cmd=%s url=%s data_size=%s",
|
||||
"start_send_page", URL_STR(url), size_str);
|
||||
a_Capi_dpi_send_cmd(NULL, bw, cmd, server, 0);
|
||||
a_Capi_dpi_send_data(url, bw, buf, buf_size, server, 0);
|
||||
} else {
|
||||
cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
|
||||
"DpiError", "Page is NOT cached");
|
||||
a_Capi_dpi_send_cmd(NULL, bw, cmd, server, 0);
|
||||
}
|
||||
dFree(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shall we permit this request to open a URL?
|
||||
*/
|
||||
static bool_t Capi_request_permitted(DilloWeb *web)
|
||||
{
|
||||
bool_t permit = FALSE;
|
||||
|
||||
/* web->requester is NULL if the action is initiated by user */
|
||||
if (!web->requester)
|
||||
return TRUE;
|
||||
|
||||
if (web->flags & ~WEB_RootUrl &&
|
||||
!dStrAsciiCasecmp(URL_SCHEME(web->requester), "https")) {
|
||||
const char *s = URL_SCHEME(web->url);
|
||||
|
||||
/* As of 2015, blocking of "active" mixed content is widespread
|
||||
* (style sheets, javascript, fonts, etc.), but the big browsers aren't
|
||||
* quite in a position to block "passive" mixed content (images) yet.
|
||||
* (Not clear whether there's consensus on which category to place
|
||||
* background images in.)
|
||||
*
|
||||
* We are blocking both, and only permitting secure->insecure page
|
||||
* redirection for now (e.g., duckduckgo has been seen providing links
|
||||
* to https URLs that redirect to http). As the web security landscape
|
||||
* evolves, we may be able to remove that permission.
|
||||
*/
|
||||
if (dStrAsciiCasecmp(s, "https") && dStrAsciiCasecmp(s, "data")) {
|
||||
MSG("capi: Blocked mixed content: %s -> %s\n",
|
||||
URL_STR(web->requester), URL_STR(web->url));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (a_Capi_get_flags(web->url) & CAPI_IsCached ||
|
||||
a_Domain_permit(web->requester, web->url)) {
|
||||
permit = TRUE;
|
||||
}
|
||||
return permit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Most used function for requesting a URL.
|
||||
* TODO: clean up the ad-hoc bindings with an API that allows dynamic
|
||||
* addition of new plugins.
|
||||
*
|
||||
* @return A primary key for identifying the client,
|
||||
* 0 if the client is aborted in the process.
|
||||
*/
|
||||
int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData)
|
||||
{
|
||||
int reload;
|
||||
char *cmd, *server;
|
||||
capi_conn_t *conn = NULL;
|
||||
const char *scheme = URL_SCHEME(web->url);
|
||||
int safe = 0, ret = 0, use_cache = 0;
|
||||
|
||||
if (Capi_request_permitted(web)) {
|
||||
/* reload test */
|
||||
reload = (!(a_Capi_get_flags(web->url) & CAPI_IsCached) ||
|
||||
(URL_FLAGS(web->url) & URL_E2EQuery));
|
||||
|
||||
if (web->flags & WEB_Download) {
|
||||
/* download request: if cached save from cache, else
|
||||
* for http, ftp or https, use the downloads dpi */
|
||||
if (a_Capi_get_flags_with_redirection(web->url) & CAPI_IsCached) {
|
||||
if (web->filename) {
|
||||
if ((web->stream = fopen(web->filename, "w"))) {
|
||||
use_cache = 1;
|
||||
} else {
|
||||
MSG_WARN("Cannot open \"%s\" for writing: %s.\n",
|
||||
web->filename, dStrerror(errno));
|
||||
}
|
||||
}
|
||||
} else if (a_Cache_download_enabled(web->url)) {
|
||||
server = "downloads";
|
||||
cmd = Capi_dpi_build_cmd(web, server);
|
||||
a_Capi_dpi_send_cmd(web->url, web->bw, cmd, server, 1);
|
||||
dFree(cmd);
|
||||
} else {
|
||||
MSG_WARN("Ignoring download request for '%s': "
|
||||
"not in cache and not downloadable.\n",
|
||||
URL_STR(web->url));
|
||||
}
|
||||
|
||||
} else if (Capi_url_uses_dpi(web->url, &server)) {
|
||||
/* dpi request */
|
||||
if ((safe = a_Capi_dpi_verify_request(web->bw, web->url))) {
|
||||
if (dStrAsciiCasecmp(scheme, "dpi") == 0) {
|
||||
if (strcmp(server, "vsource") == 0) {
|
||||
/* allow "view source" reload upon user request */
|
||||
} else {
|
||||
/* make the other "dpi:/" prefixed urls always reload. */
|
||||
a_Url_set_flags(web->url, URL_FLAGS(web->url) |URL_E2EQuery);
|
||||
reload = 1;
|
||||
}
|
||||
}
|
||||
if (reload) {
|
||||
a_Capi_conn_abort_by_url(web->url);
|
||||
/* Send dpip command */
|
||||
_MSG("a_Capi_open_url, reload url='%s'\n", URL_STR(web->url));
|
||||
cmd = Capi_dpi_build_cmd(web, server);
|
||||
a_Capi_dpi_send_cmd(web->url, web->bw, cmd, server, 1);
|
||||
dFree(cmd);
|
||||
if (strcmp(server, "vsource") == 0) {
|
||||
Capi_dpi_send_source(web->bw, web->url);
|
||||
}
|
||||
}
|
||||
use_cache = 1;
|
||||
}
|
||||
dFree(server);
|
||||
|
||||
} else if (!dStrAsciiCasecmp(scheme, "http") ||
|
||||
!dStrAsciiCasecmp(scheme, "https")) {
|
||||
/* http request */
|
||||
|
||||
#ifndef ENABLE_TLS
|
||||
if (!dStrAsciiCasecmp(scheme, "https")) {
|
||||
if (web->flags & WEB_RootUrl)
|
||||
a_UIcmd_set_msg(web->bw,
|
||||
"HTTPS was disabled at compilation time.");
|
||||
a_Web_free(web);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if (reload) {
|
||||
a_Capi_conn_abort_by_url(web->url);
|
||||
/* create a new connection and start the CCC operations */
|
||||
conn = Capi_conn_new(web->url, web->bw, "http", "none");
|
||||
/* start the reception branch before the query one because the DNS
|
||||
* may callback immediately. This may avoid a race condition. */
|
||||
a_Capi_ccc(OpStart, 2, BCK, a_Chain_new(), conn, "http");
|
||||
a_Capi_ccc(OpStart, 1, BCK, a_Chain_new(), conn, web);
|
||||
}
|
||||
use_cache = 1;
|
||||
|
||||
} else if (!dStrAsciiCasecmp(scheme, "about")) {
|
||||
/* internal request */
|
||||
use_cache = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_cache) {
|
||||
if (!conn || (conn && Capi_conn_valid(conn))) {
|
||||
/* not aborted, let's continue... */
|
||||
ret = a_Cache_open_url(web, Call, CbData);
|
||||
}
|
||||
} else {
|
||||
a_Web_free(web);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert cache-defined flags to Capi ones.
|
||||
*/
|
||||
static int Capi_map_cache_flags(uint_t flags)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (flags) {
|
||||
status |= CAPI_IsCached;
|
||||
if (flags & CA_IsEmpty)
|
||||
status |= CAPI_IsEmpty;
|
||||
if (flags & CA_InProgress)
|
||||
status |= CAPI_InProgress;
|
||||
else
|
||||
status |= CAPI_Completed;
|
||||
|
||||
/* CAPI_Aborted is not yet used/defined */
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return status information of an URL's content-transfer process.
|
||||
*/
|
||||
int a_Capi_get_flags(const DilloUrl *Url)
|
||||
{
|
||||
uint_t flags = a_Cache_get_flags(Url);
|
||||
int status = flags ? Capi_map_cache_flags(flags) : 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as a_Capi_get_flags() but following redirections.
|
||||
*/
|
||||
int a_Capi_get_flags_with_redirection(const DilloUrl *Url)
|
||||
{
|
||||
uint_t flags = a_Cache_get_flags_with_redirection(Url);
|
||||
int status = flags ? Capi_map_cache_flags(flags) : 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cache's buffer for the URL, and its size.
|
||||
* Return: 1 cached, 0 not cached.
|
||||
*/
|
||||
int a_Capi_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize)
|
||||
{
|
||||
return a_Cache_get_buf(Url, PBuf, BufSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unref the cache's buffer when no longer using it.
|
||||
*/
|
||||
void a_Capi_unref_buf(const DilloUrl *Url)
|
||||
{
|
||||
a_Cache_unref_buf(Url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Content-Type associated with the URL
|
||||
*/
|
||||
const char *a_Capi_get_content_type(const DilloUrl *url)
|
||||
{
|
||||
return a_Cache_get_content_type(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Content-Type for the URL.
|
||||
*/
|
||||
const char *a_Capi_set_content_type(const DilloUrl *url, const char *ctype,
|
||||
const char *from)
|
||||
{
|
||||
return a_Cache_set_content_type(url, ctype, from);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send data to a dpi (e.g. add_bookmark, open_url, send_preferences, ...).
|
||||
* Most of the time we send dpi commands, but it also serves for raw data
|
||||
* as with "view source".
|
||||
*/
|
||||
int a_Capi_dpi_send_data(const DilloUrl *url, void *bw,
|
||||
char *data, int data_sz, char *server, int flags)
|
||||
{
|
||||
capi_conn_t *conn;
|
||||
DataBuf *dbuf;
|
||||
|
||||
if (flags & 1) {
|
||||
/* open a new connection to server */
|
||||
|
||||
/* Create a new connection data struct and add it to the list */
|
||||
conn = Capi_conn_new(url, bw, server, data);
|
||||
/* start the CCC operations */
|
||||
a_Capi_ccc(OpStart, 2, BCK, a_Chain_new(), conn, server);
|
||||
a_Capi_ccc(OpStart, 1, BCK, a_Chain_new(), conn, server);
|
||||
|
||||
} else {
|
||||
/* Re-use an open connection */
|
||||
conn = Capi_conn_find(server);
|
||||
if (conn && conn->InfoSend) {
|
||||
/* found & active */
|
||||
dbuf = a_Chain_dbuf_new(data, data_sz, 0);
|
||||
a_Capi_ccc(OpSend, 1, BCK, conn->InfoSend, dbuf, NULL);
|
||||
dFree(dbuf);
|
||||
} else {
|
||||
MSG(" ERROR: [a_Capi_dpi_send_data] No open connection found\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a dpi cmd.
|
||||
* (For instance: add_bookmark, open_url, send_preferences, ...)
|
||||
*/
|
||||
int a_Capi_dpi_send_cmd(DilloUrl *url, void *bw, char *cmd, char *server,
|
||||
int flags)
|
||||
{
|
||||
return a_Capi_dpi_send_data(url, bw, cmd, strlen(cmd), server, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a client from the cache client queue.
|
||||
* force = also abort the CCC if this is the last client.
|
||||
*/
|
||||
void a_Capi_stop_client(int Key, int force)
|
||||
{
|
||||
CacheClient_t *Client;
|
||||
|
||||
_MSG("a_Capi_stop_client: force=%d\n", force);
|
||||
|
||||
Client = a_Cache_client_get_if_unique(Key);
|
||||
if (Client && (force || Client->BufSize == 0)) {
|
||||
/* remove empty entries too */
|
||||
a_Capi_conn_abort_by_url(Client->Url);
|
||||
}
|
||||
a_Cache_stop_client(Key);
|
||||
}
|
||||
|
||||
/**
|
||||
* CCC function for the CAPI module
|
||||
*/
|
||||
void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
|
||||
void *Data1, void *Data2)
|
||||
{
|
||||
capi_conn_t *conn;
|
||||
|
||||
dReturn_if_fail( a_Chain_check("a_Capi_ccc", Op, Branch, Dir, Info) );
|
||||
|
||||
if (Branch == 1) {
|
||||
if (Dir == BCK) {
|
||||
/* Command sending branch */
|
||||
switch (Op) {
|
||||
case OpStart:
|
||||
/* Data1 = conn; Data2 = {Web | server} */
|
||||
conn = Data1;
|
||||
Capi_conn_ref(conn);
|
||||
Info->LocalKey = conn;
|
||||
conn->InfoSend = Info;
|
||||
if (strcmp(conn->server, "http") == 0 ||
|
||||
strcmp(conn->server, "https") == 0) {
|
||||
a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Http_ccc, 1, 1);
|
||||
a_Chain_bcb(OpStart, Info, Data2, NULL);
|
||||
} else {
|
||||
a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Dpi_ccc, 1, 1);
|
||||
a_Chain_bcb(OpStart, Info, Data2, NULL);
|
||||
}
|
||||
break;
|
||||
case OpSend:
|
||||
/* Data1 = dbuf */
|
||||
a_Chain_bcb(OpSend, Info, Data1, NULL);
|
||||
break;
|
||||
case OpEnd:
|
||||
conn = Info->LocalKey;
|
||||
conn->InfoSend = NULL;
|
||||
a_Chain_bcb(OpEnd, Info, NULL, NULL);
|
||||
Capi_conn_unref(conn);
|
||||
dFree(Info);
|
||||
break;
|
||||
case OpAbort:
|
||||
conn = Info->LocalKey;
|
||||
conn->InfoSend = NULL;
|
||||
a_Chain_bcb(OpAbort, Info, NULL, NULL);
|
||||
Capi_conn_unref(conn);
|
||||
dFree(Info);
|
||||
break;
|
||||
default:
|
||||
MSG_WARN("Unused CCC Capi 1B\n");
|
||||
break;
|
||||
}
|
||||
} else { /* 1 FWD */
|
||||
/* Command sending branch (status) */
|
||||
switch (Op) {
|
||||
case OpSend:
|
||||
if (!Data2) {
|
||||
MSG_WARN("Capi.c: Opsend [1F] Data2 = NULL\n");
|
||||
} else if (strcmp(Data2, "FD") == 0) {
|
||||
conn = Info->LocalKey;
|
||||
conn->SockFD = *(int*)Data1;
|
||||
/* communicate the FD through the answer branch */
|
||||
a_Capi_ccc(OpSend, 2, BCK, conn->InfoRecv, &conn->SockFD, "FD");
|
||||
} else if (strcmp(Data2, "DpidOK") == 0) {
|
||||
/* resume pending dpi requests */
|
||||
Capi_conn_resume();
|
||||
}
|
||||
break;
|
||||
case OpAbort:
|
||||
conn = Info->LocalKey;
|
||||
conn->InfoSend = NULL;
|
||||
a_Cache_process_dbuf(IOAbort, NULL, 0, conn->url);
|
||||
if (Data2) {
|
||||
if (!strcmp(Data2, "DpidERROR")) {
|
||||
a_UIcmd_set_msg(conn->bw,
|
||||
"ERROR: can't start dpid daemon "
|
||||
"(URL scheme = '%s')!",
|
||||
conn->url ? URL_SCHEME(conn->url) : "");
|
||||
} else if (!strcmp(Data2, "Both") && conn->InfoRecv) {
|
||||
/* abort the other branch too */
|
||||
a_Capi_ccc(OpAbort, 2, BCK, conn->InfoRecv, NULL, NULL);
|
||||
}
|
||||
}
|
||||
/* if URL == expect-url */
|
||||
a_Nav_cancel_expect_if_eq(conn->bw, conn->url);
|
||||
/* finish conn */
|
||||
Capi_conn_unref(conn);
|
||||
dFree(Info);
|
||||
break;
|
||||
default:
|
||||
MSG_WARN("Unused CCC Capi 1F\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (Branch == 2) {
|
||||
if (Dir == BCK) {
|
||||
/* Answer branch */
|
||||
switch (Op) {
|
||||
case OpStart:
|
||||
/* Data1 = conn; Data2 = {"http" | "<dpi server name>"} */
|
||||
conn = Data1;
|
||||
Capi_conn_ref(conn);
|
||||
Info->LocalKey = conn;
|
||||
conn->InfoRecv = Info;
|
||||
if (strcmp(conn->server, "http") == 0)
|
||||
a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Http_ccc, 2, 2);
|
||||
else
|
||||
a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Dpi_ccc, 2, 2);
|
||||
a_Chain_bcb(OpStart, Info, NULL, Data2);
|
||||
break;
|
||||
case OpSend:
|
||||
/* Data1 = FD */
|
||||
if (Data2 && strcmp(Data2, "FD") == 0) {
|
||||
a_Chain_bcb(OpSend, Info, Data1, Data2);
|
||||
}
|
||||
break;
|
||||
case OpAbort:
|
||||
conn = Info->LocalKey;
|
||||
conn->InfoRecv = NULL;
|
||||
a_Chain_bcb(OpAbort, Info, NULL, NULL);
|
||||
/* remove the cache entry for this URL */
|
||||
a_Cache_entry_remove_by_url(conn->url);
|
||||
Capi_conn_unref(conn);
|
||||
dFree(Info);
|
||||
break;
|
||||
default:
|
||||
MSG_WARN("Unused CCC Capi 2B\n");
|
||||
break;
|
||||
}
|
||||
} else { /* 2 FWD */
|
||||
/* Server listening branch */
|
||||
switch (Op) {
|
||||
case OpSend:
|
||||
conn = Info->LocalKey;
|
||||
if (strcmp(Data2, "send_page_2eof") == 0) {
|
||||
/* Data1 = dbuf */
|
||||
DataBuf *dbuf = Data1;
|
||||
bool_t finished = a_Cache_process_dbuf(IORead, dbuf->Buf,
|
||||
dbuf->Size, conn->url);
|
||||
if (finished && Capi_conn_valid(conn) && conn->InfoRecv) {
|
||||
/* If we have a persistent connection where cache tells us
|
||||
* that we've received the full response, and cache didn't
|
||||
* trigger an abort and tear everything down, tell upstream.
|
||||
*/
|
||||
a_Chain_bcb(OpSend, conn->InfoRecv, NULL, "reply_complete");
|
||||
}
|
||||
} else if (strcmp(Data2, "send_status_message") == 0) {
|
||||
a_UIcmd_set_msg(conn->bw, "%s", Data1);
|
||||
} else if (strcmp(Data2, "chat") == 0) {
|
||||
a_UIcmd_set_msg(conn->bw, "%s", Data1);
|
||||
a_Bookmarks_chat_add(NULL, NULL, Data1);
|
||||
} else if (strcmp(Data2, "dialog") == 0) {
|
||||
a_Dpiapi_dialog(conn->bw, conn->server, Data1);
|
||||
} else if (strcmp(Data2, "reload_request") == 0) {
|
||||
a_Nav_reload(conn->bw);
|
||||
} else if (strcmp(Data2, "start_send_page") == 0) {
|
||||
/* prepare the cache to receive the data stream for this URL
|
||||
*
|
||||
* a_Capi_open_url() already added a new cache entry,
|
||||
* and a client for it.
|
||||
*/
|
||||
}
|
||||
break;
|
||||
case OpEnd:
|
||||
conn = Info->LocalKey;
|
||||
conn->InfoRecv = NULL;
|
||||
|
||||
a_Cache_process_dbuf(IOClose, NULL, 0, conn->url);
|
||||
|
||||
if (conn->InfoSend) {
|
||||
/* Propagate OpEnd to the sending branch too */
|
||||
a_Capi_ccc(OpEnd, 1, BCK, conn->InfoSend, NULL, NULL);
|
||||
}
|
||||
Capi_conn_unref(conn);
|
||||
dFree(Info);
|
||||
break;
|
||||
case OpAbort:
|
||||
conn = Info->LocalKey;
|
||||
conn->InfoRecv = NULL;
|
||||
a_Cache_process_dbuf(IOAbort, NULL, 0, conn->url);
|
||||
if (Data2) {
|
||||
if (!strcmp(Data2, "Both") && conn->InfoSend) {
|
||||
/* abort the other branch too */
|
||||
a_Capi_ccc(OpAbort, 1, BCK, conn->InfoSend, NULL, NULL);
|
||||
}
|
||||
}
|
||||
/* if URL == expect-url */
|
||||
a_Nav_cancel_expect_if_eq(conn->bw, conn->url);
|
||||
/* finish conn */
|
||||
Capi_conn_unref(conn);
|
||||
dFree(Info);
|
||||
break;
|
||||
default:
|
||||
MSG_WARN("Unused CCC Capi 2F\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
48
src/capi.h
Normal file
48
src/capi.h
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef __CAPI_H__
|
||||
#define __CAPI_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#include "cache.h"
|
||||
#include "web.hh"
|
||||
|
||||
/*
|
||||
* Flag defines
|
||||
*/
|
||||
#define CAPI_IsCached (0x1)
|
||||
#define CAPI_IsEmpty (0x2)
|
||||
#define CAPI_InProgress (0x4)
|
||||
#define CAPI_Aborted (0x8)
|
||||
#define CAPI_Completed (0x10)
|
||||
|
||||
/*
|
||||
* Function prototypes
|
||||
*/
|
||||
void a_Capi_init(void);
|
||||
int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData);
|
||||
int a_Capi_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize);
|
||||
void a_Capi_unref_buf(const DilloUrl *Url);
|
||||
const char *a_Capi_get_content_type(const DilloUrl *url);
|
||||
const char *a_Capi_set_content_type(const DilloUrl *url, const char *ctype,
|
||||
const char *from);
|
||||
int a_Capi_get_flags(const DilloUrl *Url);
|
||||
int a_Capi_get_flags_with_redirection(const DilloUrl *Url);
|
||||
int a_Capi_dpi_verify_request(BrowserWindow *bw, DilloUrl *url);
|
||||
int a_Capi_dpi_send_data(const DilloUrl *url, void *bw,
|
||||
char *data, int data_sz, char *server, int flags);
|
||||
int a_Capi_dpi_send_cmd(DilloUrl *url, void *bw, char *cmd, char *server,
|
||||
int flags);
|
||||
void a_Capi_set_vsource_url(const DilloUrl *url);
|
||||
void a_Capi_stop_client(int Key, int force);
|
||||
void a_Capi_conn_abort_by_url(const DilloUrl *url);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __CAPI_H__ */
|
||||
|
||||
204
src/chain.c
Normal file
204
src/chain.c
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright 2001-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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Concomitant control chain (CCC).
|
||||
* Theory and code by Jorge Arellano Cid
|
||||
*/
|
||||
|
||||
#include "msg.h"
|
||||
#include "chain.h"
|
||||
#include "../dlib/dlib.h"
|
||||
|
||||
#define VERBOSE 0
|
||||
|
||||
/**
|
||||
* Show debugging info
|
||||
*/
|
||||
#if VERBOSE
|
||||
static void Chain_debug_msg(char *FuncStr, int Op, int Branch, int Dir,
|
||||
ChainLink *Info)
|
||||
{
|
||||
const char *StrOps[] = {"", "OpStart", "OpSend",
|
||||
"OpStop", "OpEnd", "OpAbort"};
|
||||
MSG("%-*s: %-*s [%d%s] Info=%p Flags=%d\n",
|
||||
12, FuncStr, 7, StrOps[Op], Branch, (Dir == 1) ? "F" : "B",
|
||||
Info, Info ? Info->Flags : -1);
|
||||
}
|
||||
#else
|
||||
static void Chain_debug_msg(char *FuncStr, int Op, int Branch, int Dir,
|
||||
ChainLink *Info) { }
|
||||
#endif
|
||||
/**
|
||||
* Create and initialize a new chain-link
|
||||
*/
|
||||
ChainLink *a_Chain_new(void)
|
||||
{
|
||||
return dNew0(ChainLink, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new link from module A to module B.
|
||||
* @param Direction tells whether to make a forward or backward link.
|
||||
* - The link from 'A' to 'B' has @p Direction direction.
|
||||
* - The main flow of information names the FWD direction.
|
||||
* @param AtoB_branch branch on which 'B' receives communications from 'A'
|
||||
* @param BtoA_branch branch on which 'A' receives communications from 'B'
|
||||
*/
|
||||
ChainLink *a_Chain_link_new(ChainLink *AInfo, ChainFunction_t AFunc,
|
||||
int Direction, ChainFunction_t BFunc,
|
||||
int AtoB_branch, int BtoA_branch)
|
||||
{
|
||||
ChainLink *NewLink = a_Chain_new();
|
||||
ChainLink *OldLink = AInfo;
|
||||
|
||||
if (Direction == BCK) {
|
||||
NewLink->Fcb = AFunc;
|
||||
NewLink->FcbInfo = AInfo;
|
||||
NewLink->FcbBranch = BtoA_branch;
|
||||
OldLink->Bcb = BFunc;
|
||||
OldLink->BcbInfo = NewLink;
|
||||
OldLink->BcbBranch = AtoB_branch;
|
||||
|
||||
} else { /* FWD */
|
||||
NewLink->Bcb = AFunc;
|
||||
NewLink->BcbInfo = AInfo;
|
||||
NewLink->BcbBranch = BtoA_branch;
|
||||
OldLink->Fcb = BFunc;
|
||||
OldLink->FcbInfo = NewLink;
|
||||
OldLink->FcbBranch = AtoB_branch;
|
||||
}
|
||||
|
||||
return NewLink;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink a previously used link.
|
||||
* @param Direction tells whether to unlink the forward or backward link.
|
||||
*/
|
||||
void a_Chain_unlink(ChainLink *Info, int Direction)
|
||||
{
|
||||
if (Direction == FWD) {
|
||||
Info->Fcb = NULL;
|
||||
Info->FcbInfo = NULL;
|
||||
Info->FcbBranch = 0;
|
||||
} else { /* BCK */
|
||||
Info->Bcb = NULL;
|
||||
Info->BcbInfo = NULL;
|
||||
Info->BcbBranch = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue the forward callback of the 'Info' link
|
||||
* @return 1 if OK, 0 if not operative.
|
||||
*/
|
||||
int a_Chain_fcb(int Op, ChainLink *Info, void *Data1, void *Data2)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (Info->Flags & (CCC_Ended + CCC_Aborted)) {
|
||||
/* CCC is not operative */
|
||||
} else if (Info->Fcb) {
|
||||
/* flag the caller */
|
||||
if (Op == OpEnd)
|
||||
Info->Flags |= CCC_Ended;
|
||||
else if (Op == OpAbort)
|
||||
Info->Flags |= CCC_Aborted;
|
||||
|
||||
Info->Fcb(Op, Info->FcbBranch, FWD, Info->FcbInfo, Data1, Data2);
|
||||
ret = 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue the backward callback of the 'Info' link
|
||||
* @return 1 if OK, 0 if not operative.
|
||||
*/
|
||||
int a_Chain_bcb(int Op, ChainLink *Info, void *Data1, void *Data2)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (Info->Flags & (CCC_Ended + CCC_Aborted)) {
|
||||
/* CCC is not operative */
|
||||
} else if (Info->Bcb) {
|
||||
/* flag the caller */
|
||||
if (Op == OpEnd)
|
||||
Info->Flags |= CCC_Ended;
|
||||
else if (Op == OpAbort)
|
||||
Info->Flags |= CCC_Aborted;
|
||||
|
||||
Info->Bcb(Op, Info->BcbBranch, BCK, Info->BcbInfo, Data1, Data2);
|
||||
ret = 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue the backward callback of the 'Info' link and then the
|
||||
* forward callback (used for OpAbort and OpStop).
|
||||
* @return 1 if OK, 0 if not operative.
|
||||
*/
|
||||
int a_Chain_bfcb(int Op, ChainLink *Info, void *Data1, void *Data2)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = a_Chain_bcb(Op, Info, Data1, Data2);
|
||||
if (ret == 1) {
|
||||
/* we need to clear the flag to reuse this 'Info' ChainLink */
|
||||
if (Op == OpEnd)
|
||||
Info->Flags &= ~CCC_Ended;
|
||||
else if (Op == OpAbort)
|
||||
Info->Flags &= ~CCC_Aborted;
|
||||
|
||||
ret = a_Chain_fcb(Op, Info, Data1, Data2);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allocate and initialize a new DataBuf structure
|
||||
*/
|
||||
DataBuf *a_Chain_dbuf_new(void *buf, int size, int code)
|
||||
{
|
||||
DataBuf *dbuf = dNew(DataBuf, 1);
|
||||
dbuf->Buf = buf;
|
||||
dbuf->Size = size;
|
||||
dbuf->Code = code;
|
||||
return dbuf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the CCC is operative.
|
||||
* Also used to hook debug information.
|
||||
*
|
||||
* @return 1 if ready to use, 0 if not operative.
|
||||
*/
|
||||
int a_Chain_check(char *FuncStr, int Op, int Branch, int Dir,
|
||||
ChainLink *Info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Show status information */
|
||||
Chain_debug_msg(FuncStr, Op, Branch, Dir, Info);
|
||||
|
||||
if (Info->Flags & (CCC_Ended + CCC_Aborted)) {
|
||||
/* CCC is not operative */
|
||||
MSG_WARN("CCC: call on already finished chain. Flags=%s%s\n",
|
||||
Info->Flags & CCC_Ended ? "CCC_Ended " : "",
|
||||
Info->Flags & CCC_Aborted ? "CCC_Aborted" : "");
|
||||
} else {
|
||||
ret = 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
76
src/chain.h
Normal file
76
src/chain.h
Normal file
@ -0,0 +1,76 @@
|
||||
#ifndef __CHAIN_H__
|
||||
#define __CHAIN_H__
|
||||
|
||||
/*
|
||||
* Concomitant control chain (CCC)
|
||||
* Theory and code by Jorge Arellano Cid <jcid@dillo.org>
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Supported CCC operations
|
||||
*/
|
||||
#define OpStart 1
|
||||
#define OpSend 2
|
||||
#define OpStop 3
|
||||
#define OpEnd 4
|
||||
#define OpAbort 5
|
||||
|
||||
/*
|
||||
* CCC flags
|
||||
*/
|
||||
#define CCC_Stopped (1 << 0)
|
||||
#define CCC_Ended (1 << 1)
|
||||
#define CCC_Aborted (1 << 2)
|
||||
|
||||
/*
|
||||
* Linking direction
|
||||
*/
|
||||
#define FWD 1
|
||||
#define BCK 2
|
||||
|
||||
typedef struct ChainLink ChainLink;
|
||||
typedef void (*ChainFunction_t)(int Op, int Branch, int Dir, ChainLink *Info,
|
||||
void *Data1, void *Data2);
|
||||
|
||||
/** Main data structure for CCC nodes */
|
||||
struct ChainLink {
|
||||
void *LocalKey;
|
||||
|
||||
int Flags;
|
||||
|
||||
ChainLink *FcbInfo;
|
||||
ChainFunction_t Fcb;
|
||||
int FcbBranch;
|
||||
|
||||
ChainLink *BcbInfo;
|
||||
ChainFunction_t Bcb;
|
||||
int BcbBranch;
|
||||
};
|
||||
|
||||
/** A convenience data structure for passing data chunks between nodes */
|
||||
typedef struct {
|
||||
char *Buf;
|
||||
int Size;
|
||||
int Code;
|
||||
} DataBuf;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Function prototypes
|
||||
*/
|
||||
ChainLink *a_Chain_new(void);
|
||||
ChainLink *a_Chain_link_new(ChainLink *AInfo, ChainFunction_t AFunc,
|
||||
int Direction, ChainFunction_t BFunc,
|
||||
int AtoB_branch, int BtoA_branch);
|
||||
void a_Chain_unlink(ChainLink *Info, int Direction);
|
||||
int a_Chain_fcb(int Op, ChainLink *Info, void *Data1, void *Data2);
|
||||
int a_Chain_bcb(int Op, ChainLink *Info, void *Data1, void *Data2);
|
||||
int a_Chain_bfcb(int Op, ChainLink *Info, void *Data1, void *Data2);
|
||||
int a_Chain_check(char *FuncStr, int Op, int Branch, int Dir,
|
||||
ChainLink *Info);
|
||||
|
||||
DataBuf *a_Chain_dbuf_new(void *buf, int size, int code);
|
||||
|
||||
#endif /* __CHAIN_H__ */
|
||||
28
src/chg
Executable file
28
src/chg
Executable file
@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Shell script for name changing source code
|
||||
#
|
||||
|
||||
if [ ! $# = 3 ]; then
|
||||
echo "Usage: chg <source> <old_word> <new_word>"
|
||||
echo " (this script changes <source> directly)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -r $1 ]; then
|
||||
echo "source file ->$1<- doesn't exist..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -r $1.BAK ]; then
|
||||
echo "creating backup file: $1.BAK"
|
||||
cp $1 $1.BAK
|
||||
fi
|
||||
|
||||
sed "s/$2/$3/g" $1 > out
|
||||
#sed s/$2/$3/ $1 > out
|
||||
rm $1
|
||||
mv out $1
|
||||
echo "done!"
|
||||
|
||||
|
||||
386
src/colors.c
Normal file
386
src/colors.c
Normal file
@ -0,0 +1,386 @@
|
||||
/*
|
||||
* File: colors.c
|
||||
*
|
||||
* Copyright (C) 2000-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.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include "colors.h"
|
||||
|
||||
#include "msg.h"
|
||||
|
||||
/*
|
||||
* If EXTENDED_COLOR is defined, the extended set of named colors is supported.
|
||||
* These colors're not standard but they're supported in most browsers.
|
||||
* NOTE: The colors MUST be in alphabetical order and lower case because the
|
||||
* code uses a binary search.
|
||||
*/
|
||||
|
||||
#define EXTENDED_COLOR
|
||||
|
||||
static const struct key {
|
||||
char *key;
|
||||
int32_t val;
|
||||
} color_keyword [] = {
|
||||
#ifdef EXTENDED_COLOR
|
||||
{ "aliceblue", 0xf0f8ff},
|
||||
{ "antiquewhite", 0xfaebd7},
|
||||
#endif
|
||||
{ "aqua", 0x00ffff},
|
||||
#ifdef EXTENDED_COLOR
|
||||
{ "aquamarine", 0x7fffd4},
|
||||
{ "azure", 0xf0ffff},
|
||||
{ "beige", 0xf5f5dc},
|
||||
{ "bisque", 0xffe4c4},
|
||||
#endif
|
||||
{ "black", 0x000000},
|
||||
#ifdef EXTENDED_COLOR
|
||||
{ "blanchedalmond", 0xffebcd},
|
||||
#endif
|
||||
{"blue", 0x0000ff},
|
||||
#ifdef EXTENDED_COLOR
|
||||
{ "blueviolet", 0x8a2be2},
|
||||
{ "brown", 0xa52a2a},
|
||||
{ "burlywood", 0xdeb887},
|
||||
{ "cadetblue", 0x5f9ea0},
|
||||
{ "chartreuse", 0x7fff00},
|
||||
{ "chocolate", 0xd2691e},
|
||||
{ "coral", 0xff7f50},
|
||||
{ "cornflowerblue", 0x6495ed},
|
||||
{ "cornsilk", 0xfff8dc},
|
||||
{ "crimson", 0xdc1436},
|
||||
{ "cyan", 0x00ffff},
|
||||
{ "darkblue", 0x00008b},
|
||||
{ "darkcyan", 0x008b8b},
|
||||
{ "darkgoldenrod", 0xb8860b},
|
||||
{ "darkgray", 0xa9a9a9},
|
||||
{ "darkgreen", 0x006400},
|
||||
{ "darkgrey", 0xa9a9a9},
|
||||
{ "darkkhaki", 0xbdb76b},
|
||||
{ "darkmagenta", 0x8b008b},
|
||||
{ "darkolivegreen", 0x556b2f},
|
||||
{ "darkorange", 0xff8c00},
|
||||
{ "darkorchid", 0x9932cc},
|
||||
{ "darkred", 0x8b0000},
|
||||
{ "darksalmon", 0xe9967a},
|
||||
{ "darkseagreen", 0x8fbc8f},
|
||||
{ "darkslateblue", 0x483d8b},
|
||||
{ "darkslategray", 0x2f4f4f},
|
||||
{ "darkslategrey", 0x2f4f4f},
|
||||
{ "darkturquoise", 0x00ced1},
|
||||
{ "darkviolet", 0x9400d3},
|
||||
{ "deeppink", 0xff1493},
|
||||
{ "deepskyblue", 0x00bfff},
|
||||
{ "dimgray", 0x696969},
|
||||
{ "dimgrey", 0x696969},
|
||||
{ "dodgerblue", 0x1e90ff},
|
||||
{ "firebrick", 0xb22222},
|
||||
{ "floralwhite", 0xfffaf0},
|
||||
{ "forestgreen", 0x228b22},
|
||||
#endif
|
||||
{ "fuchsia", 0xff00ff},
|
||||
#ifdef EXTENDED_COLOR
|
||||
{ "gainsboro", 0xdcdcdc},
|
||||
{ "ghostwhite", 0xf8f8ff},
|
||||
{ "gold", 0xffd700},
|
||||
{ "goldenrod", 0xdaa520},
|
||||
#endif
|
||||
{ "gray", 0x808080},
|
||||
{ "green", 0x008000},
|
||||
#ifdef EXTENDED_COLOR
|
||||
{ "greenyellow", 0xadff2f},
|
||||
{ "grey", 0x808080},
|
||||
{ "honeydew", 0xf0fff0},
|
||||
{ "hotpink", 0xff69b4},
|
||||
{ "indianred", 0xcd5c5c},
|
||||
{ "indigo", 0x4b0082},
|
||||
{ "ivory", 0xfffff0},
|
||||
{ "khaki", 0xf0e68c},
|
||||
{ "lavender", 0xe6e6fa},
|
||||
{ "lavenderblush", 0xfff0f5},
|
||||
{ "lawngreen", 0x7cfc00},
|
||||
{ "lemonchiffon", 0xfffacd},
|
||||
{ "lightblue", 0xadd8e6},
|
||||
{ "lightcoral", 0xf08080},
|
||||
{ "lightcyan", 0xe0ffff},
|
||||
{ "lightgoldenrodyellow", 0xfafad2},
|
||||
{ "lightgray", 0xd3d3d3},
|
||||
{ "lightgreen", 0x90ee90},
|
||||
{ "lightgrey", 0xd3d3d3},
|
||||
{ "lightpink", 0xffb6c1},
|
||||
{ "lightsalmon", 0xffa07a},
|
||||
{ "lightseagreen", 0x20b2aa},
|
||||
{ "lightskyblue", 0x87cefa},
|
||||
{ "lightslategray", 0x778899},
|
||||
{ "lightslategrey", 0x778899},
|
||||
{ "lightsteelblue", 0xb0c4de},
|
||||
{ "lightyellow", 0xffffe0},
|
||||
#endif
|
||||
{ "lime", 0x00ff00},
|
||||
#ifdef EXTENDED_COLOR
|
||||
{ "limegreen", 0x32cd32},
|
||||
{ "linen", 0xfaf0e6},
|
||||
{ "magenta", 0xff00ff},
|
||||
#endif
|
||||
{ "maroon", 0x800000},
|
||||
#ifdef EXTENDED_COLOR
|
||||
{ "mediumaquamarine", 0x66cdaa},
|
||||
{ "mediumblue", 0x0000cd},
|
||||
{ "mediumorchid", 0xba55d3},
|
||||
{ "mediumpurple", 0x9370db},
|
||||
{ "mediumseagreen", 0x3cb371},
|
||||
{ "mediumslateblue", 0x7b68ee},
|
||||
{ "mediumspringgreen", 0x00fa9a},
|
||||
{ "mediumturquoise", 0x48d1cc},
|
||||
{ "mediumvioletred", 0xc71585},
|
||||
{ "midnightblue", 0x191970},
|
||||
{ "mintcream", 0xf5fffa},
|
||||
{ "mistyrose", 0xffe4e1},
|
||||
{ "moccasin", 0xffe4b5},
|
||||
{ "navajowhite", 0xffdead},
|
||||
#endif
|
||||
{ "navy", 0x000080},
|
||||
#ifdef EXTENDED_COLOR
|
||||
{ "oldlace", 0xfdf5e6},
|
||||
#endif
|
||||
{ "olive", 0x808000},
|
||||
#ifdef EXTENDED_COLOR
|
||||
{ "olivedrab", 0x6b8e23},
|
||||
{ "orange", 0xffa500},
|
||||
{ "orangered", 0xff4500},
|
||||
{ "orchid", 0xda70d6},
|
||||
{ "palegoldenrod", 0xeee8aa},
|
||||
{ "palegreen", 0x98fb98},
|
||||
{ "paleturquoise", 0xafeeee},
|
||||
{ "palevioletred", 0xdb7093},
|
||||
{ "papayawhip", 0xffefd5},
|
||||
{ "peachpuff", 0xffdab9},
|
||||
{ "peru", 0xcd853f},
|
||||
{ "pink", 0xffc0cb},
|
||||
{ "plum", 0xdda0dd},
|
||||
{ "powderblue", 0xb0e0e6},
|
||||
#endif
|
||||
{ "purple", 0x800080},
|
||||
{ "red", 0xff0000},
|
||||
#ifdef EXTENDED_COLOR
|
||||
{ "rosybrown", 0xbc8f8f},
|
||||
{ "royalblue", 0x4169e1},
|
||||
{ "saddlebrown", 0x8b4513},
|
||||
{ "salmon", 0xfa8072},
|
||||
{ "sandybrown", 0xf4a460},
|
||||
{ "seagreen", 0x2e8b57},
|
||||
{ "seashell", 0xfff5ee},
|
||||
{ "sienna", 0xa0522d},
|
||||
#endif
|
||||
{ "silver", 0xc0c0c0},
|
||||
#ifdef EXTENDED_COLOR
|
||||
{ "skyblue", 0x87ceeb},
|
||||
{ "slateblue", 0x6a5acd},
|
||||
{ "slategray", 0x708090},
|
||||
{ "slategrey", 0x708090},
|
||||
{ "snow", 0xfffafa},
|
||||
{ "springgreen", 0x00ff7f},
|
||||
{ "steelblue", 0x4682b4},
|
||||
{ "tan", 0xd2b48c},
|
||||
#endif
|
||||
{ "teal", 0x008080},
|
||||
#ifdef EXTENDED_COLOR
|
||||
{ "thistle", 0xd8bfd8},
|
||||
{ "tomato", 0xff6347},
|
||||
{ "turquoise", 0x40e0d0},
|
||||
{ "violet", 0xee82ee},
|
||||
{ "wheat", 0xf5deb3},
|
||||
#endif
|
||||
{ "white", 0xffffff},
|
||||
#ifdef EXTENDED_COLOR
|
||||
{ "whitesmoke", 0xf5f5f5},
|
||||
#endif
|
||||
{ "yellow", 0xffff00},
|
||||
#ifdef EXTENDED_COLOR
|
||||
{ "yellowgreen", 0x9acd32},
|
||||
#endif
|
||||
};
|
||||
|
||||
#define NCOLORS (sizeof(color_keyword) / sizeof(color_keyword[0]))
|
||||
|
||||
/**
|
||||
* Parse a color in hex (RRGGBB) or (RGB).
|
||||
*
|
||||
* @return
|
||||
* - parsed color if successful (err = 0),
|
||||
* - default_color on error (err = 1).
|
||||
*/
|
||||
static int32_t Color_parse_hex (const char *s, int32_t default_color, int *err)
|
||||
{
|
||||
int32_t ret_color;
|
||||
char *tail;
|
||||
|
||||
*err = 1;
|
||||
ret_color = strtol(s, &tail, 16);
|
||||
if (tail - s == 6)
|
||||
*err = 0;
|
||||
else if (tail - s == 3) { /* #RGB as allowed by CSS */
|
||||
*err = 0;
|
||||
ret_color = ((ret_color & 0xf00) << 12) | ((ret_color & 0xf00) << 8) |
|
||||
((ret_color & 0x0f0) << 8) | ((ret_color & 0x0f0) << 4) |
|
||||
((ret_color & 0x00f) << 4) | ((ret_color & 0x00f) << 0);
|
||||
} else
|
||||
ret_color = default_color;
|
||||
|
||||
return ret_color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a color string.
|
||||
*
|
||||
* - If the string begins with # or with 0x, return the color number
|
||||
* (with 'RGB' expanded to 'RRGGBB').
|
||||
* - Else search the set of named colors.
|
||||
* - As a last resort, treat it as bare hex as in the first case.
|
||||
*
|
||||
* @return
|
||||
* - Parsed color if successful,
|
||||
* - default_color on error.
|
||||
*
|
||||
* "err" argument:
|
||||
* - 0 if a color beginning with '#' is successfully parsed
|
||||
* or the color is a recognized word.
|
||||
* - 1 if the color is bare hex or can't be parsed at all.
|
||||
* - 2 if a color beginning with 0[xX] is successfully parsed.
|
||||
*/
|
||||
int32_t a_Color_parse (const char *str, int32_t default_color, int *err)
|
||||
{
|
||||
const char *cp;
|
||||
int32_t ret_color;
|
||||
int ret, low, mid, high, st = 1;
|
||||
|
||||
/* skip leading spaces */
|
||||
for (cp = str; dIsspace(*cp); cp++);
|
||||
|
||||
ret_color = default_color;
|
||||
if (*cp == '#') {
|
||||
ret_color = Color_parse_hex(cp + 1, default_color, &st);
|
||||
|
||||
} else if (*cp == '0' && (cp[1] == 'x' || cp[1] == 'X') ) {
|
||||
ret_color = Color_parse_hex(cp + 2, default_color, &st);
|
||||
if (!st)
|
||||
st = 2;
|
||||
} else {
|
||||
/* Binary search */
|
||||
low = 0;
|
||||
high = NCOLORS - 1;
|
||||
while (low <= high) {
|
||||
mid = (low + high) / 2;
|
||||
if ((ret = dStrAsciiCasecmp(cp, color_keyword[mid].key)) < 0)
|
||||
high = mid - 1;
|
||||
else if (ret > 0)
|
||||
low = mid + 1;
|
||||
else {
|
||||
ret_color = color_keyword[mid].val;
|
||||
st = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (low > high) {
|
||||
/* try for RRGGBB lacking the leading '#' */
|
||||
ret_color = Color_parse_hex(cp, default_color, &st);
|
||||
st = 1;
|
||||
}
|
||||
}
|
||||
|
||||
_MSG("color string: %s\n", str);
|
||||
_MSG("color : %X\n", ret_color);
|
||||
|
||||
*err = st;
|
||||
return ret_color;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* Return a "distance" measure (between [0, 10])
|
||||
*/
|
||||
static int Color_distance(long c1, long c2)
|
||||
{
|
||||
return (labs((c1 & 0x0000ff) - (c2 & 0x0000ff)) +
|
||||
labs(((c1 & 0x00ff00) - (c2 & 0x00ff00)) >> 8) +
|
||||
labs(((c1 & 0xff0000) - (c2 & 0xff0000)) >> 16)) / 75;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Return: [0-3]
|
||||
*/
|
||||
static int Color_distance2(long c1, long c2)
|
||||
{
|
||||
return (labs((c1 & 0x0000ff) - (c2 & 0x0000ff)) >= 0x000060) +
|
||||
(labs((c1 & 0x00ff00) - (c2 & 0x00ff00)) >= 0x006000) +
|
||||
(labs((c1 & 0xff0000) - (c2 & 0xff0000)) >= 0x600000);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return: [0-3] (requires less contrast than distance2)
|
||||
*/
|
||||
static int Color_distance3(long c1, long c2)
|
||||
{
|
||||
return (labs((c1 & 0x0000ff) - (c2 & 0x0000ff)) >= 0x000040) +
|
||||
(labs((c1 & 0x00ff00) - (c2 & 0x00ff00)) >= 0x004000) +
|
||||
(labs((c1 & 0xff0000) - (c2 & 0xff0000)) >= 0x400000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a suitable "visited link" color
|
||||
* @return
|
||||
* - if candidate has good contrast with C_txt, C_lnk and C_bg -> candidate
|
||||
* - else another color (from the internal list)
|
||||
*/
|
||||
int32_t a_Color_vc(int32_t candidate,
|
||||
int32_t C_txt, int32_t C_lnk, int32_t C_bg)
|
||||
{
|
||||
/* candidate purple darkcyan darkmagenta olive */
|
||||
static int32_t v[] = {0x000000, 0x800080, 0x008b8b, 0x8b008b, 0x808000,
|
||||
/* darkred coral black */
|
||||
0x8b0000, 0xff7f50, 0x000000};
|
||||
int v_size = sizeof(v) / sizeof(v[0]);
|
||||
int i, max_i, score, max_score, d_bg, d_txt, d_lnk;
|
||||
|
||||
|
||||
/* set candidate in the list */
|
||||
v[0] = candidate;
|
||||
|
||||
/* Try to get good overall and individual contrast */
|
||||
max_i = max_score = 0;
|
||||
for (i = 0; i < v_size; ++i) {
|
||||
_MSG("a_Color_vc: [%d]%.6x: %d %d %d\n", i, v[i],
|
||||
Color_distance2(C_txt, v[i]),
|
||||
Color_distance2(C_lnk, v[i]),
|
||||
Color_distance2(C_bg, v[i]));
|
||||
|
||||
/* Tuned with: slashdot.org, paulgraham.com, newsforge.com,
|
||||
* linuxjournal.com
|
||||
*/
|
||||
d_txt = Color_distance2(C_txt, v[i]);
|
||||
d_lnk = Color_distance2(C_lnk, v[i]);
|
||||
d_bg = Color_distance2(C_bg, v[i]);
|
||||
score = (d_bg >= 2 ? 4 : 2 * d_bg) +
|
||||
(d_txt + d_lnk >= 2 ? 2 : d_txt + d_lnk) +
|
||||
(Color_distance3(C_lnk, v[i]) >= 1 ? 1 : 0);
|
||||
if (score >= 7) {
|
||||
/* enough distance, use this color */
|
||||
max_i = i;
|
||||
break;
|
||||
} else if (score > max_score) {
|
||||
/* keep track of the best candidate so far */
|
||||
max_score = score;
|
||||
max_i = i;
|
||||
}
|
||||
}
|
||||
return v[max_i];
|
||||
}
|
||||
18
src/colors.h
Normal file
18
src/colors.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef __COLORS_H__
|
||||
#define __COLORS_H__
|
||||
|
||||
#include "config.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int32_t a_Color_parse (const char *str, int32_t default_color, int *err);
|
||||
int32_t a_Color_vc(int32_t candidate, int32_t c1, int32_t c2, int32_t c3);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __COLORS_H__ */
|
||||
370
src/cookies.c
Normal file
370
src/cookies.c
Normal file
@ -0,0 +1,370 @@
|
||||
/*
|
||||
* File: cookies.c
|
||||
*
|
||||
* Copyright 2001 Lars Clausen <lrclause@cs.uiuc.edu>
|
||||
* Jörgen Viksell <jorgen.viksell@telia.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Handling of cookies takes place here.
|
||||
*/
|
||||
|
||||
#include "msg.h"
|
||||
|
||||
#ifdef DISABLE_COOKIES
|
||||
|
||||
/**
|
||||
* Initialize the cookies module
|
||||
*/
|
||||
void a_Cookies_init(void)
|
||||
{
|
||||
MSG("Cookies: absolutely disabled at compilation time.\n");
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/file.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "IO/Url.h"
|
||||
#include "list.h"
|
||||
#include "cookies.h"
|
||||
#include "capi.h"
|
||||
#include "../dpip/dpip.h"
|
||||
|
||||
|
||||
/** The maximum length of a line in the cookie file */
|
||||
#define LINE_MAXLEN 4096
|
||||
|
||||
typedef enum {
|
||||
COOKIE_ACCEPT,
|
||||
COOKIE_ACCEPT_SESSION,
|
||||
COOKIE_DENY
|
||||
} CookieControlAction;
|
||||
|
||||
typedef struct {
|
||||
CookieControlAction action;
|
||||
char *domain;
|
||||
} CookieControl;
|
||||
|
||||
/* Variables for access control */
|
||||
static CookieControl *ccontrol = NULL;
|
||||
static int num_ccontrol = 0;
|
||||
static int num_ccontrol_max = 1;
|
||||
static CookieControlAction default_action = COOKIE_DENY;
|
||||
|
||||
static bool_t disabled;
|
||||
|
||||
static FILE *Cookies_fopen(const char *file, char *init_str);
|
||||
static CookieControlAction Cookies_control_check(const DilloUrl *url);
|
||||
static CookieControlAction Cookies_control_check_domain(const char *domain);
|
||||
static int Cookie_control_init(void);
|
||||
|
||||
/**
|
||||
* Return a file pointer. If the file doesn't exist, try to create it,
|
||||
* with the optional 'init_str' as its content.
|
||||
*/
|
||||
static FILE *Cookies_fopen(const char *filename, char *init_str)
|
||||
{
|
||||
FILE *F_in;
|
||||
int fd, rc;
|
||||
|
||||
if ((F_in = fopen(filename, "r")) == NULL) {
|
||||
/* Create the file */
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
|
||||
if (fd != -1) {
|
||||
if (init_str) {
|
||||
rc = write(fd, init_str, strlen(init_str));
|
||||
if (rc == -1) {
|
||||
MSG("Cookies: Could not write initial string to file %s: %s\n",
|
||||
filename, dStrerror(errno));
|
||||
}
|
||||
}
|
||||
dClose(fd);
|
||||
|
||||
MSG("Cookies: Created file: %s\n", filename);
|
||||
F_in = fopen(filename, "r");
|
||||
} else {
|
||||
MSG("Cookies: Could not create file: %s!\n", filename);
|
||||
}
|
||||
}
|
||||
|
||||
if (F_in) {
|
||||
/* set close on exec */
|
||||
fcntl(fileno(F_in), F_SETFD, FD_CLOEXEC | fcntl(fileno(F_in), F_GETFD));
|
||||
}
|
||||
|
||||
return F_in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the cookies module
|
||||
* (The 'disabled' variable is writable only within a_Cookies_init)
|
||||
*/
|
||||
void a_Cookies_init(void)
|
||||
{
|
||||
/* Default setting */
|
||||
disabled = TRUE;
|
||||
|
||||
/* Read and parse the cookie control file (cookiesrc) */
|
||||
if (Cookie_control_init() != 0) {
|
||||
MSG("Disabling cookies.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
MSG("Enabling cookies as from cookiesrc...\n");
|
||||
disabled = FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush cookies to disk and free all the memory allocated.
|
||||
*/
|
||||
void a_Cookies_freeall(void)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value corresponding to the cookie string
|
||||
*/
|
||||
void a_Cookies_set(Dlist *cookie_strings, const DilloUrl *set_url,
|
||||
const char *date)
|
||||
{
|
||||
CookieControlAction action;
|
||||
char *cmd, *cookie_string, *dpip_tag;
|
||||
const char *path;
|
||||
int i;
|
||||
|
||||
if (disabled)
|
||||
return;
|
||||
|
||||
action = Cookies_control_check(set_url);
|
||||
if (action == COOKIE_DENY) {
|
||||
_MSG("Cookies: denied SET for %s\n", URL_HOST_(set_url));
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; (cookie_string = dList_nth_data(cookie_strings, i)); ++i) {
|
||||
path = URL_PATH_(set_url);
|
||||
if (date)
|
||||
cmd = a_Dpip_build_cmd("cmd=%s cookie=%s host=%s path=%s date=%s",
|
||||
"set_cookie", cookie_string,
|
||||
URL_HOST_(set_url), path ? path : "/", date);
|
||||
else
|
||||
cmd = a_Dpip_build_cmd("cmd=%s cookie=%s host=%s path=%s",
|
||||
"set_cookie", cookie_string,
|
||||
URL_HOST_(set_url), path ? path : "/");
|
||||
|
||||
_MSG("Cookies.c: a_Cookies_set \n\t \"%s\" \n",cmd );
|
||||
/* This call is commented because it doesn't guarantee the order
|
||||
* in which cookies are set and got. (It may deadlock too) */
|
||||
//a_Capi_dpi_send_cmd(NULL, NULL, cmd, "cookies", 1);
|
||||
|
||||
dpip_tag = a_Dpi_send_blocking_cmd("cookies", cmd);
|
||||
_MSG("a_Cookies_set: dpip_tag = {%s}\n", dpip_tag);
|
||||
dFree(dpip_tag);
|
||||
dFree(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string containing cookie data for an HTTP query.
|
||||
*/
|
||||
char *a_Cookies_get_query(const DilloUrl *query_url, const DilloUrl *requester)
|
||||
{
|
||||
char *cmd, *dpip_tag, *query;
|
||||
const char *path;
|
||||
CookieControlAction action;
|
||||
|
||||
if (disabled)
|
||||
return dStrdup("");
|
||||
|
||||
action = Cookies_control_check(query_url);
|
||||
if (action == COOKIE_DENY) {
|
||||
_MSG("Cookies: denied GET for %s\n", URL_HOST_(query_url));
|
||||
return dStrdup("");
|
||||
}
|
||||
|
||||
if (requester == NULL) {
|
||||
/* request made by user */
|
||||
} else if (!a_Url_same_organization(query_url, requester)) {
|
||||
MSG("Cookies: not sent for request by '%s' for '%s'\n",
|
||||
URL_HOST(requester), URL_HOST(query_url));
|
||||
return dStrdup("");
|
||||
}
|
||||
|
||||
path = URL_PATH_(query_url);
|
||||
|
||||
cmd = a_Dpip_build_cmd("cmd=%s scheme=%s host=%s path=%s",
|
||||
"get_cookie", URL_SCHEME(query_url),
|
||||
URL_HOST(query_url), path ? path : "/");
|
||||
|
||||
/* Get the answer from cookies.dpi */
|
||||
_MSG("cookies.c: a_Dpi_send_blocking_cmd cmd = {%s}\n", cmd);
|
||||
dpip_tag = a_Dpi_send_blocking_cmd("cookies", cmd);
|
||||
_MSG("cookies.c: after a_Dpi_send_blocking_cmd resp={%s}\n", dpip_tag);
|
||||
dFree(cmd);
|
||||
|
||||
if (dpip_tag != NULL) {
|
||||
query = a_Dpip_get_attr(dpip_tag, "cookie");
|
||||
dFree(dpip_tag);
|
||||
} else {
|
||||
query = dStrdup("");
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------
|
||||
* Access control routines
|
||||
* ------------------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* Get the cookie control rules (from cookiesrc).
|
||||
* @return
|
||||
* - 0 = Parsed OK, with cookies enabled
|
||||
* - 1 = Parsed OK, with cookies disabled
|
||||
* - 2 = Can't open the control file
|
||||
*/
|
||||
static int Cookie_control_init(void)
|
||||
{
|
||||
CookieControl cc;
|
||||
FILE *stream;
|
||||
char *filename, *rc;
|
||||
char line[LINE_MAXLEN];
|
||||
char domain[LINE_MAXLEN];
|
||||
char rule[LINE_MAXLEN];
|
||||
bool_t enabled = FALSE;
|
||||
|
||||
/* Get a file pointer */
|
||||
filename = dStrconcat(dGethomedir(), "/.dillo/cookiesrc", NULL);
|
||||
stream = Cookies_fopen(filename, "DEFAULT DENY\n");
|
||||
dFree(filename);
|
||||
|
||||
if (!stream)
|
||||
return 2;
|
||||
|
||||
/* Get all lines in the file */
|
||||
while (!feof(stream)) {
|
||||
line[0] = '\0';
|
||||
rc = fgets(line, LINE_MAXLEN, stream);
|
||||
if (!rc && ferror(stream)) {
|
||||
MSG("Cookies1: Error while reading rule from cookiesrc: %s\n",
|
||||
dStrerror(errno));
|
||||
fclose(stream);
|
||||
return 2; /* bail out */
|
||||
}
|
||||
|
||||
/* Remove leading and trailing whitespaces */
|
||||
dStrstrip(line);
|
||||
|
||||
if (line[0] != '\0' && line[0] != '#') {
|
||||
int i = 0, j = 0;
|
||||
|
||||
/* Get the domain */
|
||||
while (line[i] != '\0' && !dIsspace(line[i]))
|
||||
domain[j++] = line[i++];
|
||||
domain[j] = '\0';
|
||||
|
||||
/* Skip past whitespaces */
|
||||
while (dIsspace(line[i]))
|
||||
i++;
|
||||
|
||||
/* Get the rule */
|
||||
j = 0;
|
||||
while (line[i] != '\0' && !dIsspace(line[i]))
|
||||
rule[j++] = line[i++];
|
||||
rule[j] = '\0';
|
||||
|
||||
if (dStrAsciiCasecmp(rule, "ACCEPT") == 0)
|
||||
cc.action = COOKIE_ACCEPT;
|
||||
else if (dStrAsciiCasecmp(rule, "ACCEPT_SESSION") == 0)
|
||||
cc.action = COOKIE_ACCEPT_SESSION;
|
||||
else if (dStrAsciiCasecmp(rule, "DENY") == 0)
|
||||
cc.action = COOKIE_DENY;
|
||||
else {
|
||||
MSG("Cookies: rule '%s' for domain '%s' is not recognised.\n",
|
||||
rule, domain);
|
||||
continue;
|
||||
}
|
||||
|
||||
cc.domain = dStrdup(domain);
|
||||
if (dStrAsciiCasecmp(cc.domain, "DEFAULT") == 0) {
|
||||
/* Set the default action */
|
||||
default_action = cc.action;
|
||||
dFree(cc.domain);
|
||||
} else {
|
||||
int i;
|
||||
uint_t len = strlen(cc.domain);
|
||||
|
||||
/* Insert into list such that longest rules come first. */
|
||||
a_List_add(ccontrol, num_ccontrol, num_ccontrol_max);
|
||||
for (i = num_ccontrol++;
|
||||
i > 0 && (len > strlen(ccontrol[i-1].domain));
|
||||
i--) {
|
||||
ccontrol[i] = ccontrol[i-1];
|
||||
}
|
||||
ccontrol[i] = cc;
|
||||
}
|
||||
|
||||
if (cc.action != COOKIE_DENY)
|
||||
enabled = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(stream);
|
||||
|
||||
return (enabled ? 0 : 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the rules for an appropriate action for this domain.
|
||||
* The rules are ordered by domain length, with longest first, so the
|
||||
* first match is the most specific.
|
||||
*/
|
||||
static CookieControlAction Cookies_control_check_domain(const char *domain)
|
||||
{
|
||||
int i, diff;
|
||||
|
||||
for (i = 0; i < num_ccontrol; i++) {
|
||||
if (ccontrol[i].domain[0] == '.') {
|
||||
diff = strlen(domain) - strlen(ccontrol[i].domain);
|
||||
if (diff >= 0) {
|
||||
if (dStrAsciiCasecmp(domain + diff, ccontrol[i].domain) != 0)
|
||||
continue;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (dStrAsciiCasecmp(domain, ccontrol[i].domain) != 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If we got here we have a match */
|
||||
return( ccontrol[i].action );
|
||||
}
|
||||
|
||||
return default_action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as @ref Cookies_control_check_domain except it takes an URL
|
||||
*/
|
||||
static CookieControlAction Cookies_control_check(const DilloUrl *url)
|
||||
{
|
||||
return Cookies_control_check_domain(URL_HOST(url));
|
||||
}
|
||||
|
||||
#endif /* !DISABLE_COOKIES */
|
||||
26
src/cookies.h
Normal file
26
src/cookies.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef __COOKIES_H__
|
||||
#define __COOKIES_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void a_Cookies_init( void );
|
||||
|
||||
#ifdef DISABLE_COOKIES
|
||||
# define a_Cookies_get_query(url, requester) dStrdup("")
|
||||
# define a_Cookies_set() ;
|
||||
# define a_Cookies_freeall() ;
|
||||
#else
|
||||
char *a_Cookies_get_query(const DilloUrl *query_url,
|
||||
const DilloUrl *requester);
|
||||
void a_Cookies_set(Dlist *cookie_string, const DilloUrl *set_url,
|
||||
const char *server_date);
|
||||
void a_Cookies_freeall( void );
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* !__COOKIES_H__ */
|
||||
552
src/css.cc
Normal file
552
src/css.cc
Normal file
@ -0,0 +1,552 @@
|
||||
/*
|
||||
* File: css.cc
|
||||
*
|
||||
* Copyright 2008-2014 Johannes Hofmann <Johannes.Hofmann@gmx.de>
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include "../dlib/dlib.h"
|
||||
#include "msg.h"
|
||||
#include "html_common.hh"
|
||||
#include "css.hh"
|
||||
|
||||
using namespace dw::core::style;
|
||||
|
||||
void CssProperty::print () {
|
||||
fprintf (stderr, "%s - %d\n",
|
||||
CssParser::propertyNameString((CssPropertyName)name),
|
||||
(int)value.intVal);
|
||||
}
|
||||
|
||||
CssPropertyList::CssPropertyList (const CssPropertyList &p, bool deep) :
|
||||
lout::misc::SimpleVector <CssProperty> (p)
|
||||
{
|
||||
refCount = 0;
|
||||
safe = p.safe;
|
||||
if (deep) {
|
||||
for (int i = 0; i < size (); i++) {
|
||||
CssProperty *p = getRef(i);
|
||||
switch (p->type) {
|
||||
case CSS_TYPE_STRING:
|
||||
case CSS_TYPE_SYMBOL:
|
||||
p->value.strVal = dStrdup (p->value.strVal);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
ownerOfStrings = true;
|
||||
} else {
|
||||
ownerOfStrings = false;
|
||||
}
|
||||
}
|
||||
|
||||
CssPropertyList::~CssPropertyList () {
|
||||
if (ownerOfStrings)
|
||||
for (int i = 0; i < size (); i++)
|
||||
getRef (i)->free ();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set property to a given name and type.
|
||||
*/
|
||||
void CssPropertyList::set (CssPropertyName name, CssValueType type,
|
||||
CssPropertyValue value) {
|
||||
CssProperty *prop;
|
||||
|
||||
if (name == CSS_PROPERTY_DISPLAY || name == CSS_PROPERTY_BACKGROUND_IMAGE)
|
||||
safe = false;
|
||||
|
||||
for (int i = 0; i < size (); i++) {
|
||||
prop = getRef (i);
|
||||
|
||||
if (prop->name == name) {
|
||||
if (ownerOfStrings)
|
||||
prop->free ();
|
||||
prop->type = type;
|
||||
prop->value = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
increase ();
|
||||
prop = getRef (size () - 1);
|
||||
prop->name = name;
|
||||
prop->type = type;
|
||||
prop->value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Merge properties into argument property list.
|
||||
*/
|
||||
void CssPropertyList::apply (CssPropertyList *props) {
|
||||
for (int i = 0; i < size (); i++) {
|
||||
CssPropertyValue value = getRef (i)->value;
|
||||
|
||||
if (props->ownerOfStrings &&
|
||||
(getRef (i)->type == CSS_TYPE_STRING ||
|
||||
getRef (i)->type == CSS_TYPE_SYMBOL))
|
||||
value.strVal = dStrdup(value.strVal);
|
||||
|
||||
props->set ((CssPropertyName) getRef (i)->name,
|
||||
(CssValueType) getRef (i)->type,
|
||||
value);
|
||||
}
|
||||
}
|
||||
|
||||
void CssPropertyList::print () {
|
||||
for (int i = 0; i < size (); i++)
|
||||
getRef (i)->print ();
|
||||
}
|
||||
|
||||
CssSelector::CssSelector () {
|
||||
struct CombinatorAndSelector *cs;
|
||||
|
||||
refCount = 0;
|
||||
matchCacheOffset = -1;
|
||||
selectorList.increase ();
|
||||
cs = selectorList.getRef (selectorList.size () - 1);
|
||||
|
||||
cs->combinator = COMB_NONE;
|
||||
cs->selector = new CssSimpleSelector ();
|
||||
}
|
||||
|
||||
CssSelector::~CssSelector () {
|
||||
for (int i = selectorList.size () - 1; i >= 0; i--)
|
||||
delete selectorList.getRef (i)->selector;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return whether selector matches at a given node in the document tree.
|
||||
*/
|
||||
bool CssSelector::match (Doctree *docTree, const DoctreeNode *node,
|
||||
int i, Combinator comb, MatchCache *matchCache) {
|
||||
int *matchCacheEntry;
|
||||
assert (node);
|
||||
|
||||
if (i < 0)
|
||||
return true;
|
||||
|
||||
struct CombinatorAndSelector *cs = selectorList.getRef (i);
|
||||
CssSimpleSelector *sel = cs->selector;
|
||||
|
||||
switch (comb) {
|
||||
case COMB_NONE:
|
||||
break;
|
||||
case COMB_CHILD:
|
||||
node = docTree->parent (node);
|
||||
break;
|
||||
case COMB_ADJACENT_SIBLING:
|
||||
node = docTree->sibling (node);
|
||||
break;
|
||||
case COMB_DESCENDANT:
|
||||
node = docTree->parent (node);
|
||||
matchCacheEntry = matchCache->getRef(matchCacheOffset + i);
|
||||
|
||||
for (const DoctreeNode *n = node;
|
||||
n && n->num > *matchCacheEntry; n = docTree->parent (n))
|
||||
if (sel->match (n) &&
|
||||
match (docTree, n, i - 1, cs->combinator, matchCache))
|
||||
return true;
|
||||
|
||||
if (node) // remember that it didn't match to avoid future tests
|
||||
*matchCacheEntry = node->num;
|
||||
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false; // \todo implement other combinators
|
||||
}
|
||||
|
||||
if (!node || !sel->match (node))
|
||||
return false;
|
||||
|
||||
// tail recursion should be optimized by the compiler
|
||||
return match (docTree, node, i - 1, cs->combinator, matchCache);
|
||||
}
|
||||
|
||||
void CssSelector::addSimpleSelector (Combinator c) {
|
||||
struct CombinatorAndSelector *cs;
|
||||
|
||||
assert (matchCacheOffset == -1);
|
||||
selectorList.increase ();
|
||||
cs = selectorList.getRef (selectorList.size () - 1);
|
||||
|
||||
cs->combinator = c;
|
||||
cs->selector = new CssSimpleSelector ();
|
||||
}
|
||||
|
||||
bool CssSelector::checksPseudoClass () {
|
||||
for (int i = 0; i < selectorList.size (); i++)
|
||||
if (selectorList.getRef (i)->selector->getPseudoClass ())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the specificity of the selector.
|
||||
*
|
||||
* The specificity of a CSS selector is defined in
|
||||
* http://www.w3.org/TR/CSS21/cascade.html#specificity
|
||||
*/
|
||||
int CssSelector::specificity () {
|
||||
int spec = 0;
|
||||
|
||||
for (int i = 0; i < selectorList.size (); i++)
|
||||
spec += selectorList.getRef (i)->selector->specificity ();
|
||||
|
||||
return spec;
|
||||
}
|
||||
|
||||
void CssSelector::print () {
|
||||
for (int i = 0; i < selectorList.size (); i++) {
|
||||
selectorList.getRef (i)->selector->print ();
|
||||
|
||||
if (i < selectorList.size () - 1) {
|
||||
switch (selectorList.getRef (i + 1)->combinator) {
|
||||
case COMB_CHILD:
|
||||
fprintf (stderr, "> ");
|
||||
break;
|
||||
case COMB_DESCENDANT:
|
||||
fprintf (stderr, "\" \" ");
|
||||
break;
|
||||
case COMB_ADJACENT_SIBLING:
|
||||
fprintf (stderr, "+ ");
|
||||
break;
|
||||
default:
|
||||
fprintf (stderr, "? ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
|
||||
CssSimpleSelector::CssSimpleSelector () {
|
||||
element = ELEMENT_ANY;
|
||||
id = NULL;
|
||||
pseudo = NULL;
|
||||
}
|
||||
|
||||
CssSimpleSelector::~CssSimpleSelector () {
|
||||
for (int i = 0; i < klass.size (); i++)
|
||||
dFree (klass.get (i));
|
||||
dFree (id);
|
||||
dFree (pseudo);
|
||||
}
|
||||
|
||||
void CssSimpleSelector::setSelect (SelectType t, const char *v) {
|
||||
switch (t) {
|
||||
case SELECT_CLASS:
|
||||
klass.increase ();
|
||||
klass.set (klass.size () - 1, dStrdup (v));
|
||||
break;
|
||||
case SELECT_PSEUDO_CLASS:
|
||||
if (pseudo == NULL)
|
||||
pseudo = dStrdup (v);
|
||||
break;
|
||||
case SELECT_ID:
|
||||
if (id == NULL)
|
||||
id = dStrdup (v);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return whether simple selector matches at a given node of
|
||||
* the document tree.
|
||||
*/
|
||||
bool CssSimpleSelector::match (const DoctreeNode *n) {
|
||||
assert (n);
|
||||
if (element != ELEMENT_ANY && element != n->element)
|
||||
return false;
|
||||
if (pseudo != NULL &&
|
||||
(n->pseudo == NULL || dStrAsciiCasecmp (pseudo, n->pseudo) != 0))
|
||||
return false;
|
||||
if (id != NULL && (n->id == NULL || dStrAsciiCasecmp (id, n->id) != 0))
|
||||
return false;
|
||||
for (int i = 0; i < klass.size (); i++) {
|
||||
bool found = false;
|
||||
if (n->klass != NULL) {
|
||||
for (int j = 0; j < n->klass->size (); j++) {
|
||||
if (dStrAsciiCasecmp (klass.get(i), n->klass->get(j)) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! found)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the specificity of the simple selector.
|
||||
*
|
||||
* The result is used in CssSelector::specificity ().
|
||||
*/
|
||||
int CssSimpleSelector::specificity () {
|
||||
int spec = 0;
|
||||
|
||||
if (id)
|
||||
spec += 1 << 20;
|
||||
spec += klass.size() << 10;
|
||||
if (pseudo)
|
||||
spec += 1 << 10;
|
||||
if (element != ELEMENT_ANY)
|
||||
spec += 1;
|
||||
|
||||
return spec;
|
||||
}
|
||||
|
||||
void CssSimpleSelector::print () {
|
||||
fprintf (stderr, "Element %d, pseudo %s, id %s ",
|
||||
element, pseudo, id);
|
||||
fprintf (stderr, "class ");
|
||||
for (int i = 0; i < klass.size (); i++)
|
||||
fprintf (stderr, ".%s", klass.get (i));
|
||||
}
|
||||
|
||||
CssRule::CssRule (CssSelector *selector, CssPropertyList *props, int pos) {
|
||||
assert (selector->size () > 0);
|
||||
|
||||
this->selector = selector;
|
||||
this->selector->ref ();
|
||||
this->props = props;
|
||||
this->props->ref ();
|
||||
this->pos = pos;
|
||||
spec = selector->specificity ();
|
||||
}
|
||||
|
||||
CssRule::~CssRule () {
|
||||
selector->unref ();
|
||||
props->unref ();
|
||||
}
|
||||
|
||||
void CssRule::apply (CssPropertyList *props, Doctree *docTree,
|
||||
const DoctreeNode *node, MatchCache *matchCache) const {
|
||||
if (selector->match (docTree, node, matchCache))
|
||||
this->props->apply (props);
|
||||
}
|
||||
|
||||
void CssRule::print () {
|
||||
selector->print ();
|
||||
props->print ();
|
||||
}
|
||||
|
||||
/*
|
||||
* \brief Insert rule with increasing specificity.
|
||||
*
|
||||
* If two rules have the same specificity, the one that was added later
|
||||
* will be added behind the others.
|
||||
* This gives later added rules more weight.
|
||||
*/
|
||||
void CssStyleSheet::RuleList::insert (CssRule *rule) {
|
||||
increase ();
|
||||
int i = size () - 1;
|
||||
|
||||
while (i > 0 && rule->specificity () < get (i - 1)->specificity ()) {
|
||||
*getRef (i) = get (i - 1);
|
||||
i--;
|
||||
}
|
||||
|
||||
*getRef (i) = rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Insert a rule into CssStyleSheet.
|
||||
*
|
||||
* To improve matching performance the rules are organized into
|
||||
* rule lists based on the topmost simple selector of their selector.
|
||||
*/
|
||||
void CssStyleSheet::addRule (CssRule *rule) {
|
||||
CssSimpleSelector *top = rule->selector->top ();
|
||||
RuleList *ruleList = NULL;
|
||||
lout::object::ConstString *string;
|
||||
|
||||
if (top->getId ()) {
|
||||
string = new lout::object::ConstString (top->getId ());
|
||||
ruleList = idTable.get (string);
|
||||
if (ruleList == NULL) {
|
||||
ruleList = new RuleList ();
|
||||
idTable.put (string, ruleList);
|
||||
} else {
|
||||
delete string;
|
||||
}
|
||||
} else if (top->getClass () && top->getClass ()->size () > 0) {
|
||||
string = new lout::object::ConstString (top->getClass ()->get (0));
|
||||
ruleList = classTable.get (string);
|
||||
if (ruleList == NULL) {
|
||||
ruleList = new RuleList;
|
||||
classTable.put (string, ruleList);
|
||||
} else {
|
||||
delete string;
|
||||
}
|
||||
} else if (top->getElement () >= 0 && top->getElement () < ntags) {
|
||||
ruleList = &elementTable[top->getElement ()];
|
||||
} else if (top->getElement () == CssSimpleSelector::ELEMENT_ANY) {
|
||||
ruleList = &anyTable;
|
||||
}
|
||||
|
||||
if (ruleList) {
|
||||
ruleList->insert (rule);
|
||||
if (rule->selector->getRequiredMatchCache () > requiredMatchCache)
|
||||
requiredMatchCache = rule->selector->getRequiredMatchCache ();
|
||||
} else {
|
||||
assert (top->getElement () == CssSimpleSelector::ELEMENT_NONE);
|
||||
delete rule;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Apply a stylesheet to a property list.
|
||||
*
|
||||
* The properties are set as defined by the rules in the stylesheet that
|
||||
* match at the given node in the document tree.
|
||||
*/
|
||||
void CssStyleSheet::apply (CssPropertyList *props, Doctree *docTree,
|
||||
const DoctreeNode *node, MatchCache *matchCache) const {
|
||||
static const int maxLists = 32;
|
||||
const RuleList *ruleList[maxLists];
|
||||
int numLists = 0, index[maxLists] = {0};
|
||||
|
||||
if (node->id) {
|
||||
lout::object::ConstString idString (node->id);
|
||||
|
||||
ruleList[numLists] = idTable.get (&idString);
|
||||
if (ruleList[numLists])
|
||||
numLists++;
|
||||
}
|
||||
|
||||
if (node->klass) {
|
||||
for (int i = 0; i < node->klass->size (); i++) {
|
||||
if (i >= maxLists - 4) {
|
||||
MSG_WARN("Maximum number of classes per element exceeded.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
lout::object::ConstString classString (node->klass->get (i));
|
||||
|
||||
ruleList[numLists] = classTable.get (&classString);
|
||||
if (ruleList[numLists])
|
||||
numLists++;
|
||||
}
|
||||
}
|
||||
|
||||
ruleList[numLists] = &elementTable[node->element];
|
||||
if (ruleList[numLists])
|
||||
numLists++;
|
||||
|
||||
ruleList[numLists] = &anyTable;
|
||||
if (ruleList[numLists])
|
||||
numLists++;
|
||||
|
||||
// Apply potentially matching rules from ruleList[0-numLists] with
|
||||
// ascending specificity.
|
||||
// If specificity is equal, rules are applied in order of appearance.
|
||||
// Each ruleList is sorted already.
|
||||
while (true) {
|
||||
int minSpec = 1 << 30;
|
||||
int minPos = 1 << 30;
|
||||
int minSpecIndex = -1;
|
||||
|
||||
for (int i = 0; i < numLists; i++) {
|
||||
const RuleList *rl = ruleList[i];
|
||||
|
||||
if (rl && rl->size () > index[i] &&
|
||||
(rl->get(index[i])->specificity () < minSpec ||
|
||||
(rl->get(index[i])->specificity () == minSpec &&
|
||||
rl->get(index[i])->position () < minPos))) {
|
||||
|
||||
minSpec = rl->get(index[i])->specificity ();
|
||||
minPos = rl->get(index[i])->position ();
|
||||
minSpecIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (minSpecIndex >= 0) {
|
||||
CssRule *rule = ruleList[minSpecIndex]->get (index[minSpecIndex]);
|
||||
rule->apply(props, docTree, node, matchCache);
|
||||
index[minSpecIndex]++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CssStyleSheet CssContext::userAgentSheet;
|
||||
|
||||
CssContext::CssContext () {
|
||||
pos = 0;
|
||||
matchCache.setSize (userAgentSheet.getRequiredMatchCache (), -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Apply a CSS context to a property list.
|
||||
*
|
||||
* The stylesheets in the context are applied one after the other
|
||||
* in the ordering defined by CSS 2.1.
|
||||
* Stylesheets that are applied later can overwrite properties set
|
||||
* by previous stylesheets.
|
||||
* This allows e.g. user styles to overwrite author styles.
|
||||
*/
|
||||
void CssContext::apply (CssPropertyList *props, Doctree *docTree,
|
||||
DoctreeNode *node,
|
||||
CssPropertyList *tagStyle, CssPropertyList *tagStyleImportant,
|
||||
CssPropertyList *nonCssHints) {
|
||||
|
||||
userAgentSheet.apply (props, docTree, node, &matchCache);
|
||||
|
||||
sheet[CSS_PRIMARY_USER].apply (props, docTree, node, &matchCache);
|
||||
|
||||
if (nonCssHints)
|
||||
nonCssHints->apply (props);
|
||||
|
||||
sheet[CSS_PRIMARY_AUTHOR].apply (props, docTree, node, &matchCache);
|
||||
|
||||
if (tagStyle)
|
||||
tagStyle->apply (props);
|
||||
|
||||
sheet[CSS_PRIMARY_AUTHOR_IMPORTANT].apply (props, docTree, node,
|
||||
&matchCache);
|
||||
|
||||
if (tagStyleImportant)
|
||||
tagStyleImportant->apply (props);
|
||||
|
||||
sheet[CSS_PRIMARY_USER_IMPORTANT].apply (props, docTree, node, &matchCache);
|
||||
}
|
||||
|
||||
void CssContext::addRule (CssSelector *sel, CssPropertyList *props,
|
||||
CssPrimaryOrder order) {
|
||||
|
||||
if (props->size () > 0) {
|
||||
CssRule *rule = new CssRule (sel, props, pos++);
|
||||
|
||||
if ((order == CSS_PRIMARY_AUTHOR ||
|
||||
order == CSS_PRIMARY_AUTHOR_IMPORTANT) &&
|
||||
!rule->isSafe ()) {
|
||||
_MSG_WARN ("Ignoring unsafe author style that might reveal browsing history\n");
|
||||
delete rule;
|
||||
} else {
|
||||
rule->selector->setMatchCacheOffset(matchCache.size ());
|
||||
if (rule->selector->getRequiredMatchCache () > matchCache.size ())
|
||||
matchCache.setSize (rule->selector->getRequiredMatchCache (), -1);
|
||||
|
||||
if (order == CSS_PRIMARY_USER_AGENT) {
|
||||
userAgentSheet.addRule (rule);
|
||||
} else {
|
||||
sheet[order].addRule (rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
536
src/css.hh
Normal file
536
src/css.hh
Normal file
@ -0,0 +1,536 @@
|
||||
/*
|
||||
* File: css.hh
|
||||
*
|
||||
* Copyright (C) 2008-2014 Johannes Hofmann <Johannes.Hofmann@gmx.de>
|
||||
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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 __CSS_HH__
|
||||
#define __CSS_HH__
|
||||
|
||||
#include "dw/core.hh"
|
||||
#include "doctree.hh"
|
||||
#include "html.hh"
|
||||
|
||||
/* Origin and weight. Used only internally.*/
|
||||
typedef enum {
|
||||
CSS_PRIMARY_USER_AGENT,
|
||||
CSS_PRIMARY_USER,
|
||||
CSS_PRIMARY_AUTHOR,
|
||||
CSS_PRIMARY_AUTHOR_IMPORTANT,
|
||||
CSS_PRIMARY_USER_IMPORTANT,
|
||||
CSS_PRIMARY_LAST
|
||||
} CssPrimaryOrder;
|
||||
|
||||
typedef enum {
|
||||
CSS_ORIGIN_USER_AGENT,
|
||||
CSS_ORIGIN_USER,
|
||||
CSS_ORIGIN_AUTHOR
|
||||
} CssOrigin;
|
||||
|
||||
typedef enum {
|
||||
CSS_TYPE_INTEGER, /**< This type is only used internally, for x-*
|
||||
properties. */
|
||||
CSS_TYPE_ENUM, /**< Value is i, if represented by
|
||||
enum_symbols[i]. */
|
||||
CSS_TYPE_MULTI_ENUM, /**< For all enum_symbols[i], 1 << i are
|
||||
combined. */
|
||||
CSS_TYPE_LENGTH_PERCENTAGE, /**< <length> or <percentage>. Represented by
|
||||
CssLength. */
|
||||
CSS_TYPE_LENGTH, /**< <length>, represented as CssLength.
|
||||
Note: In some cases, CSS_TYPE_LENGTH is used
|
||||
instead of CSS_TYPE_LENGTH_PERCENTAGE,
|
||||
only because Dw cannot handle percentages
|
||||
in this particular case (e.g.
|
||||
'margin-*-width'). */
|
||||
CSS_TYPE_SIGNED_LENGTH, /**< As CSS_TYPE_LENGTH but may be negative. */
|
||||
CSS_TYPE_LENGTH_PERCENTAGE_NUMBER, /* <length> or <percentage>, or <number> */
|
||||
CSS_TYPE_AUTO, /**< Represented as CssLength of type
|
||||
CSS_LENGTH_TYPE_AUTO */
|
||||
CSS_TYPE_COLOR, /**< Represented as integer. */
|
||||
CSS_TYPE_FONT_WEIGHT, /**< this very special and only used by
|
||||
'font-weight' */
|
||||
CSS_TYPE_STRING, /**< <string> */
|
||||
CSS_TYPE_SYMBOL, /**< Symbols, which are directly copied (as
|
||||
opposed to CSS_TYPE_ENUM and
|
||||
CSS_TYPE_MULTI_ENUM). Used for
|
||||
'font-family'. */
|
||||
CSS_TYPE_URI, /**< <uri> */
|
||||
CSS_TYPE_BACKGROUND_POSITION,
|
||||
CSS_TYPE_UNUSED /**< Not yet used. Will itself get unused some
|
||||
day. */
|
||||
} CssValueType;
|
||||
|
||||
/**
|
||||
* CSS lengths are represented by the CssLength struct, which can hold
|
||||
* different types of values.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
CSS_LENGTH_TYPE_NONE,
|
||||
CSS_LENGTH_TYPE_PX,
|
||||
CSS_LENGTH_TYPE_MM, /**< "cm", "in", "pt" and "pc" are converted into
|
||||
millimeters. */
|
||||
CSS_LENGTH_TYPE_EM,
|
||||
CSS_LENGTH_TYPE_EX,
|
||||
CSS_LENGTH_TYPE_CH,
|
||||
CSS_LENGTH_TYPE_REM,
|
||||
CSS_LENGTH_TYPE_VW,
|
||||
CSS_LENGTH_TYPE_VH,
|
||||
CSS_LENGTH_TYPE_VMIN,
|
||||
CSS_LENGTH_TYPE_VMAX,
|
||||
CSS_LENGTH_TYPE_PERCENTAGE,
|
||||
CSS_LENGTH_TYPE_RELATIVE, /**< This does not exist in CSS but
|
||||
is used in HTML */
|
||||
CSS_LENGTH_TYPE_AUTO /**< This can be used as a simple value. */
|
||||
} CssLengthType;
|
||||
|
||||
/* Aligned to 64 bits */
|
||||
typedef struct {
|
||||
CssLengthType type;
|
||||
union {
|
||||
int i;
|
||||
float f;
|
||||
};
|
||||
} CssLength;
|
||||
|
||||
inline CssLength CSS_CREATE_LENGTH (float v, CssLengthType t) {
|
||||
CssLength l;
|
||||
l.type = t;
|
||||
switch (t) {
|
||||
case CSS_LENGTH_TYPE_PX:
|
||||
l.i = lout::misc::roundInt(v);
|
||||
break;
|
||||
case CSS_LENGTH_TYPE_NONE:
|
||||
case CSS_LENGTH_TYPE_MM:
|
||||
case CSS_LENGTH_TYPE_EM:
|
||||
case CSS_LENGTH_TYPE_EX:
|
||||
case CSS_LENGTH_TYPE_CH:
|
||||
case CSS_LENGTH_TYPE_REM:
|
||||
case CSS_LENGTH_TYPE_VW:
|
||||
case CSS_LENGTH_TYPE_VH:
|
||||
case CSS_LENGTH_TYPE_VMIN:
|
||||
case CSS_LENGTH_TYPE_VMAX:
|
||||
case CSS_LENGTH_TYPE_PERCENTAGE:
|
||||
case CSS_LENGTH_TYPE_RELATIVE:
|
||||
l.f = v;
|
||||
break;
|
||||
case CSS_LENGTH_TYPE_AUTO:
|
||||
l.i = 0;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
inline CssLengthType CSS_LENGTH_TYPE (CssLength l) {
|
||||
return l.type;
|
||||
}
|
||||
|
||||
inline float CSS_LENGTH_VALUE (CssLength l) {
|
||||
switch (CSS_LENGTH_TYPE(l)) {
|
||||
case CSS_LENGTH_TYPE_PX:
|
||||
return (float) l.i;
|
||||
case CSS_LENGTH_TYPE_NONE:
|
||||
case CSS_LENGTH_TYPE_MM:
|
||||
case CSS_LENGTH_TYPE_EM:
|
||||
case CSS_LENGTH_TYPE_EX:
|
||||
case CSS_LENGTH_TYPE_CH:
|
||||
case CSS_LENGTH_TYPE_REM:
|
||||
case CSS_LENGTH_TYPE_VW:
|
||||
case CSS_LENGTH_TYPE_VH:
|
||||
case CSS_LENGTH_TYPE_VMIN:
|
||||
case CSS_LENGTH_TYPE_VMAX:
|
||||
case CSS_LENGTH_TYPE_PERCENTAGE:
|
||||
case CSS_LENGTH_TYPE_RELATIVE:
|
||||
return l.f;
|
||||
case CSS_LENGTH_TYPE_AUTO:
|
||||
return 0.0;
|
||||
default:
|
||||
assert(false);
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
CSS_PROPERTY_END = -1, // used as terminator in CssShorthandInfo
|
||||
CSS_PROPERTY_BACKGROUND_ATTACHMENT,
|
||||
CSS_PROPERTY_BACKGROUND_COLOR,
|
||||
CSS_PROPERTY_BACKGROUND_IMAGE,
|
||||
CSS_PROPERTY_BACKGROUND_POSITION,
|
||||
CSS_PROPERTY_BACKGROUND_REPEAT,
|
||||
CSS_PROPERTY_BORDER_BOTTOM_COLOR,
|
||||
CSS_PROPERTY_BORDER_BOTTOM_STYLE,
|
||||
CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
|
||||
CSS_PROPERTY_BORDER_COLLAPSE,
|
||||
CSS_PROPERTY_BORDER_LEFT_COLOR,
|
||||
CSS_PROPERTY_BORDER_LEFT_STYLE,
|
||||
CSS_PROPERTY_BORDER_LEFT_WIDTH,
|
||||
CSS_PROPERTY_BORDER_RIGHT_COLOR,
|
||||
CSS_PROPERTY_BORDER_RIGHT_STYLE,
|
||||
CSS_PROPERTY_BORDER_RIGHT_WIDTH,
|
||||
CSS_PROPERTY_BORDER_SPACING,
|
||||
CSS_PROPERTY_BORDER_TOP_COLOR,
|
||||
CSS_PROPERTY_BORDER_TOP_STYLE,
|
||||
CSS_PROPERTY_BORDER_TOP_WIDTH,
|
||||
CSS_PROPERTY_BOTTOM,
|
||||
CSS_PROPERTY_CAPTION_SIDE,
|
||||
CSS_PROPERTY_CLEAR,
|
||||
CSS_PROPERTY_CLIP,
|
||||
CSS_PROPERTY_COLOR,
|
||||
CSS_PROPERTY_CONTENT,
|
||||
CSS_PROPERTY_COUNTER_INCREMENT,
|
||||
CSS_PROPERTY_COUNTER_RESET,
|
||||
CSS_PROPERTY_CURSOR,
|
||||
CSS_PROPERTY_DIRECTION,
|
||||
CSS_PROPERTY_DISPLAY,
|
||||
CSS_PROPERTY_EMPTY_CELLS,
|
||||
CSS_PROPERTY_FLOAT,
|
||||
CSS_PROPERTY_FONT_FAMILY,
|
||||
CSS_PROPERTY_FONT_SIZE,
|
||||
CSS_PROPERTY_FONT_SIZE_ADJUST,
|
||||
CSS_PROPERTY_FONT_STRETCH,
|
||||
CSS_PROPERTY_FONT_STYLE,
|
||||
CSS_PROPERTY_FONT_VARIANT,
|
||||
CSS_PROPERTY_FONT_WEIGHT,
|
||||
CSS_PROPERTY_HEIGHT,
|
||||
CSS_PROPERTY_LEFT,
|
||||
CSS_PROPERTY_LETTER_SPACING,
|
||||
CSS_PROPERTY_LINE_HEIGHT,
|
||||
CSS_PROPERTY_LIST_STYLE_IMAGE,
|
||||
CSS_PROPERTY_LIST_STYLE_POSITION,
|
||||
CSS_PROPERTY_LIST_STYLE_TYPE,
|
||||
CSS_PROPERTY_MARGIN_BOTTOM,
|
||||
CSS_PROPERTY_MARGIN_LEFT,
|
||||
CSS_PROPERTY_MARGIN_RIGHT,
|
||||
CSS_PROPERTY_MARGIN_TOP,
|
||||
CSS_PROPERTY_MARKER_OFFSET,
|
||||
CSS_PROPERTY_MARKS,
|
||||
CSS_PROPERTY_MAX_HEIGHT,
|
||||
CSS_PROPERTY_MAX_WIDTH,
|
||||
CSS_PROPERTY_MIN_HEIGHT,
|
||||
CSS_PROPERTY_MIN_WIDTH,
|
||||
CSS_PROPERTY_OUTLINE_COLOR,
|
||||
CSS_PROPERTY_OUTLINE_STYLE,
|
||||
CSS_PROPERTY_OUTLINE_WIDTH,
|
||||
CSS_PROPERTY_OVERFLOW,
|
||||
CSS_PROPERTY_PADDING_BOTTOM,
|
||||
CSS_PROPERTY_PADDING_LEFT,
|
||||
CSS_PROPERTY_PADDING_RIGHT,
|
||||
CSS_PROPERTY_PADDING_TOP,
|
||||
CSS_PROPERTY_POSITION,
|
||||
CSS_PROPERTY_QUOTES,
|
||||
CSS_PROPERTY_RIGHT,
|
||||
CSS_PROPERTY_TEXT_ALIGN,
|
||||
CSS_PROPERTY_TEXT_DECORATION,
|
||||
CSS_PROPERTY_TEXT_INDENT,
|
||||
CSS_PROPERTY_TEXT_SHADOW,
|
||||
CSS_PROPERTY_TEXT_TRANSFORM,
|
||||
CSS_PROPERTY_TOP,
|
||||
CSS_PROPERTY_UNICODE_BIDI,
|
||||
CSS_PROPERTY_VERTICAL_ALIGN,
|
||||
CSS_PROPERTY_VISIBILITY,
|
||||
CSS_PROPERTY_WHITE_SPACE,
|
||||
CSS_PROPERTY_WIDTH,
|
||||
CSS_PROPERTY_WORD_SPACING,
|
||||
CSS_PROPERTY_Z_INDEX,
|
||||
CSS_PROPERTY_X_LINK,
|
||||
CSS_PROPERTY_X_COLSPAN,
|
||||
CSS_PROPERTY_X_ROWSPAN,
|
||||
PROPERTY_X_LINK,
|
||||
PROPERTY_X_LANG,
|
||||
PROPERTY_X_IMG,
|
||||
PROPERTY_X_TOOLTIP,
|
||||
CSS_PROPERTY_LAST
|
||||
} CssPropertyName;
|
||||
|
||||
typedef struct {
|
||||
CssLength posX;
|
||||
CssLength posY;
|
||||
} CssBackgroundPosition;
|
||||
|
||||
typedef union {
|
||||
int32_t intVal;
|
||||
CssLength lenVal;
|
||||
char *strVal;
|
||||
CssBackgroundPosition *posVal;
|
||||
} CssPropertyValue;
|
||||
|
||||
typedef enum {
|
||||
CSS_BORDER_WIDTH_THIN,
|
||||
CSS_BORDER_WIDTH_MEDIUM,
|
||||
CSS_BORDER_WIDTH_THICK
|
||||
} CssBorderWidthExtensions;
|
||||
|
||||
typedef enum {
|
||||
CSS_FONT_WEIGHT_BOLD,
|
||||
CSS_FONT_WEIGHT_BOLDER,
|
||||
CSS_FONT_WEIGHT_LIGHT,
|
||||
CSS_FONT_WEIGHT_LIGHTER,
|
||||
CSS_FONT_WEIGHT_NORMAL
|
||||
} CssFontWeightExtensions;
|
||||
|
||||
typedef enum {
|
||||
CSS_FONT_SIZE_LARGE,
|
||||
CSS_FONT_SIZE_LARGER,
|
||||
CSS_FONT_SIZE_MEDIUM,
|
||||
CSS_FONT_SIZE_SMALL,
|
||||
CSS_FONT_SIZE_SMALLER,
|
||||
CSS_FONT_SIZE_XX_LARGE,
|
||||
CSS_FONT_SIZE_XX_SMALL,
|
||||
CSS_FONT_SIZE_X_LARGE,
|
||||
CSS_FONT_SIZE_X_SMALL
|
||||
} CssFontSizeExtensions;
|
||||
|
||||
typedef enum {
|
||||
CSS_LETTER_SPACING_NORMAL
|
||||
} CssLetterSpacingExtensions;
|
||||
|
||||
typedef enum {
|
||||
CSS_WORD_SPACING_NORMAL
|
||||
} CssWordSpacingExtensions;
|
||||
|
||||
|
||||
/**
|
||||
* \brief This class holds a CSS property and value pair.
|
||||
*/
|
||||
class CssProperty {
|
||||
public:
|
||||
|
||||
short name;
|
||||
short type;
|
||||
CssPropertyValue value;
|
||||
|
||||
inline void free () {
|
||||
switch (type) {
|
||||
case CSS_TYPE_STRING:
|
||||
case CSS_TYPE_SYMBOL:
|
||||
case CSS_TYPE_URI:
|
||||
dFree (value.strVal);
|
||||
break;
|
||||
case CSS_TYPE_BACKGROUND_POSITION:
|
||||
dFree (value.posVal);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
void print ();
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief A list of CssProperty objects.
|
||||
*/
|
||||
class CssPropertyList : public lout::misc::SimpleVector <CssProperty> {
|
||||
int refCount;
|
||||
bool ownerOfStrings;
|
||||
bool safe;
|
||||
|
||||
public:
|
||||
inline CssPropertyList(bool ownerOfStrings = false) :
|
||||
lout::misc::SimpleVector <CssProperty> (1) {
|
||||
refCount = 0;
|
||||
safe = true;
|
||||
this->ownerOfStrings = ownerOfStrings;
|
||||
};
|
||||
CssPropertyList(const CssPropertyList &p, bool deep = false);
|
||||
~CssPropertyList ();
|
||||
|
||||
void set (CssPropertyName name, CssValueType type,
|
||||
CssPropertyValue value);
|
||||
void apply (CssPropertyList *props);
|
||||
bool isSafe () { return safe; };
|
||||
void print ();
|
||||
inline void ref () { refCount++; }
|
||||
inline void unref () { if (--refCount == 0) delete this; }
|
||||
};
|
||||
|
||||
class CssSimpleSelector {
|
||||
private:
|
||||
int element;
|
||||
char *pseudo, *id;
|
||||
lout::misc::SimpleVector <char *> klass;
|
||||
|
||||
public:
|
||||
enum {
|
||||
ELEMENT_NONE = -1,
|
||||
ELEMENT_ANY = -2
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
SELECT_NONE,
|
||||
SELECT_CLASS,
|
||||
SELECT_PSEUDO_CLASS,
|
||||
SELECT_ID
|
||||
} SelectType;
|
||||
|
||||
CssSimpleSelector ();
|
||||
~CssSimpleSelector ();
|
||||
inline void setElement (int e) { element = e; };
|
||||
void setSelect (SelectType t, const char *v);
|
||||
inline lout::misc::SimpleVector <char *> *getClass () { return &klass; };
|
||||
inline const char *getPseudoClass () { return pseudo; };
|
||||
inline const char *getId () { return id; };
|
||||
inline int getElement () { return element; };
|
||||
bool match (const DoctreeNode *node);
|
||||
int specificity ();
|
||||
void print ();
|
||||
};
|
||||
|
||||
class MatchCache : public lout::misc::SimpleVector <int> {
|
||||
public:
|
||||
MatchCache() : lout::misc::SimpleVector <int> (0) {};
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief CSS selector class.
|
||||
*
|
||||
* \todo Implement missing selector options.
|
||||
*/
|
||||
class CssSelector {
|
||||
public:
|
||||
typedef enum {
|
||||
COMB_NONE,
|
||||
COMB_DESCENDANT,
|
||||
COMB_CHILD,
|
||||
COMB_ADJACENT_SIBLING
|
||||
} Combinator;
|
||||
|
||||
private:
|
||||
struct CombinatorAndSelector {
|
||||
Combinator combinator;
|
||||
CssSimpleSelector *selector;
|
||||
};
|
||||
|
||||
int refCount, matchCacheOffset;
|
||||
lout::misc::SimpleVector <struct CombinatorAndSelector> selectorList;
|
||||
|
||||
bool match (Doctree *dt, const DoctreeNode *node, int i, Combinator comb,
|
||||
MatchCache *matchCache);
|
||||
|
||||
public:
|
||||
CssSelector ();
|
||||
~CssSelector ();
|
||||
void addSimpleSelector (Combinator c);
|
||||
inline CssSimpleSelector *top () {
|
||||
return selectorList.getRef (selectorList.size () - 1)->selector;
|
||||
}
|
||||
inline int size () { return selectorList.size (); };
|
||||
inline bool match (Doctree *dt, const DoctreeNode *node,
|
||||
MatchCache *matchCache) {
|
||||
return match (dt, node, selectorList.size () - 1, COMB_NONE,
|
||||
matchCache);
|
||||
}
|
||||
inline void setMatchCacheOffset (int mo) {
|
||||
if (matchCacheOffset == -1)
|
||||
matchCacheOffset = mo;
|
||||
}
|
||||
inline int getRequiredMatchCache () {
|
||||
return matchCacheOffset + size ();
|
||||
}
|
||||
int specificity ();
|
||||
bool checksPseudoClass ();
|
||||
void print ();
|
||||
inline void ref () { refCount++; }
|
||||
inline void unref () { if (--refCount == 0) delete this; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief A CssSelector CssPropertyList pair.
|
||||
*
|
||||
* The CssPropertyList is applied if the CssSelector matches.
|
||||
*/
|
||||
class CssRule {
|
||||
private:
|
||||
CssPropertyList *props;
|
||||
int spec, pos;
|
||||
|
||||
public:
|
||||
CssSelector *selector;
|
||||
|
||||
CssRule (CssSelector *selector, CssPropertyList *props, int pos);
|
||||
~CssRule ();
|
||||
|
||||
void apply (CssPropertyList *props, Doctree *docTree,
|
||||
const DoctreeNode *node, MatchCache *matchCache) const;
|
||||
inline bool isSafe () {
|
||||
return !selector->checksPseudoClass () || props->isSafe ();
|
||||
};
|
||||
inline int specificity () { return spec; };
|
||||
inline int position () { return pos; };
|
||||
void print ();
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief A list of CssRules.
|
||||
*
|
||||
* In apply () all matching rules are applied.
|
||||
*/
|
||||
class CssStyleSheet {
|
||||
private:
|
||||
class RuleList : public lout::misc::SimpleVector <CssRule*>,
|
||||
public lout::object::Object {
|
||||
public:
|
||||
RuleList () : lout::misc::SimpleVector <CssRule*> (1) {};
|
||||
~RuleList () {
|
||||
for (int i = 0; i < size (); i++)
|
||||
delete get (i);
|
||||
};
|
||||
|
||||
void insert (CssRule *rule);
|
||||
inline bool equals (lout::object::Object *other) {
|
||||
return this == other;
|
||||
};
|
||||
inline int hashValue () { return (intptr_t) this; };
|
||||
};
|
||||
|
||||
class RuleMap : public lout::container::typed::HashTable
|
||||
<lout::object::ConstString, RuleList > {
|
||||
public:
|
||||
RuleMap () : lout::container::typed::HashTable
|
||||
<lout::object::ConstString, RuleList > (true, true, 256) {};
|
||||
};
|
||||
|
||||
static const int ntags = HTML_NTAGS;
|
||||
|
||||
RuleList elementTable[ntags], anyTable;
|
||||
RuleMap idTable, classTable;
|
||||
int requiredMatchCache;
|
||||
|
||||
public:
|
||||
CssStyleSheet () { requiredMatchCache = 0; }
|
||||
void addRule (CssRule *rule);
|
||||
void apply (CssPropertyList *props, Doctree *docTree,
|
||||
const DoctreeNode *node, MatchCache *matchCache) const;
|
||||
int getRequiredMatchCache () { return requiredMatchCache; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief A set of CssStyleSheets.
|
||||
*/
|
||||
class CssContext {
|
||||
private:
|
||||
static CssStyleSheet userAgentSheet;
|
||||
CssStyleSheet sheet[CSS_PRIMARY_USER_IMPORTANT + 1];
|
||||
MatchCache matchCache;
|
||||
int pos;
|
||||
|
||||
public:
|
||||
CssContext ();
|
||||
|
||||
void addRule (CssSelector *sel, CssPropertyList *props,
|
||||
CssPrimaryOrder order);
|
||||
void apply (CssPropertyList *props,
|
||||
Doctree *docTree, DoctreeNode *node,
|
||||
CssPropertyList *tagStyle, CssPropertyList *tagStyleImportant,
|
||||
CssPropertyList *nonCssHints);
|
||||
};
|
||||
|
||||
#endif
|
||||
1815
src/cssparser.cc
Normal file
1815
src/cssparser.cc
Normal file
File diff suppressed because it is too large
Load Diff
61
src/cssparser.hh
Normal file
61
src/cssparser.hh
Normal file
@ -0,0 +1,61 @@
|
||||
#ifndef __CSSPARSER_HH__
|
||||
#define __CSSPARSER_HH__
|
||||
|
||||
#include "css.hh"
|
||||
|
||||
class DilloHtml;
|
||||
|
||||
class CssParser {
|
||||
private:
|
||||
typedef enum {
|
||||
CSS_TK_DECINT, CSS_TK_FLOAT, CSS_TK_COLOR, CSS_TK_SYMBOL,
|
||||
CSS_TK_STRING, CSS_TK_CHAR, CSS_TK_END
|
||||
} CssTokenType;
|
||||
|
||||
static const int maxStrLen = 256;
|
||||
CssContext *context;
|
||||
CssOrigin origin;
|
||||
const DilloUrl *baseUrl;
|
||||
|
||||
const char *buf;
|
||||
int buflen, bufptr;
|
||||
|
||||
CssTokenType ttype;
|
||||
char tval[maxStrLen];
|
||||
bool withinBlock;
|
||||
bool spaceSeparated; /* used when parsing CSS selectors */
|
||||
|
||||
CssParser(CssContext *context, CssOrigin origin, const DilloUrl *baseUrl,
|
||||
const char *buf, int buflen);
|
||||
int getChar();
|
||||
void ungetChar();
|
||||
void nextToken();
|
||||
bool skipString(int c, const char *string);
|
||||
bool tokenMatchesProperty(CssPropertyName prop, CssValueType * type);
|
||||
bool parseValue(CssPropertyName prop, CssValueType type,
|
||||
CssPropertyValue * val);
|
||||
bool parseWeight();
|
||||
bool parseRgbColorComponent(int32_t *cc, int *percentage);
|
||||
bool parseRgbColor(int32_t *c);
|
||||
void parseDeclaration(CssPropertyList * props,
|
||||
CssPropertyList * importantProps);
|
||||
bool parseSimpleSelector(CssSimpleSelector *selector);
|
||||
char *parseUrl();
|
||||
void parseImport(DilloHtml *html);
|
||||
void parseMedia();
|
||||
CssSelector *parseSelector();
|
||||
void parseRuleset();
|
||||
void ignoreBlock();
|
||||
void ignoreStatement();
|
||||
|
||||
public:
|
||||
static void parseDeclarationBlock(const DilloUrl *baseUrl,
|
||||
const char *buf, int buflen,
|
||||
CssPropertyList *props,
|
||||
CssPropertyList *propsImortant);
|
||||
static void parse(DilloHtml *html, const DilloUrl *baseUrl, CssContext *context,
|
||||
const char *buf, int buflen, CssOrigin origin);
|
||||
static const char *propertyNameString(CssPropertyName name);
|
||||
};
|
||||
|
||||
#endif
|
||||
406
src/decode.c
Normal file
406
src/decode.c
Normal file
@ -0,0 +1,406 @@
|
||||
/*
|
||||
* File: decode.c
|
||||
*
|
||||
* Copyright 2007-2008 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.
|
||||
*/
|
||||
|
||||
#include <zlib.h>
|
||||
#include <iconv.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h> /* strtol */
|
||||
|
||||
#include "decode.h"
|
||||
#include "utf8.hh"
|
||||
#include "msg.h"
|
||||
|
||||
static const int bufsize = 8*1024;
|
||||
|
||||
/**
|
||||
* Decode 'Transfer-Encoding: chunked' data
|
||||
*/
|
||||
Dstr *a_Decode_transfer_process(DecodeTransfer *dc, const char *instr,
|
||||
int inlen)
|
||||
{
|
||||
char *inputPtr, *eol;
|
||||
int inputRemaining;
|
||||
int chunkRemaining = *((int *)dc->state);
|
||||
Dstr *output = dStr_sized_new(inlen);
|
||||
|
||||
dStr_append_l(dc->leftover, instr, inlen);
|
||||
inputPtr = dc->leftover->str;
|
||||
inputRemaining = dc->leftover->len;
|
||||
|
||||
while (inputRemaining > 0) {
|
||||
if (chunkRemaining > 2) {
|
||||
/* chunk body to copy */
|
||||
int copylen = MIN(chunkRemaining - 2, inputRemaining);
|
||||
dStr_append_l(output, inputPtr, copylen);
|
||||
chunkRemaining -= copylen;
|
||||
inputRemaining -= copylen;
|
||||
inputPtr += copylen;
|
||||
}
|
||||
|
||||
if ((chunkRemaining == 2) && (inputRemaining > 0)) {
|
||||
/* CR to discard */
|
||||
chunkRemaining--;
|
||||
inputRemaining--;
|
||||
inputPtr++;
|
||||
}
|
||||
if ((chunkRemaining == 1) && (inputRemaining > 0)) {
|
||||
/* LF to discard */
|
||||
chunkRemaining--;
|
||||
inputRemaining--;
|
||||
inputPtr++;
|
||||
}
|
||||
|
||||
/*
|
||||
* A chunk has a one-line header that begins with the chunk length
|
||||
* in hexadecimal.
|
||||
*/
|
||||
if (!(eol = (char *)memchr(inputPtr, '\n', inputRemaining))) {
|
||||
break; /* We don't have the whole line yet. */
|
||||
}
|
||||
|
||||
if (!(chunkRemaining = strtol(inputPtr, NULL, 0x10))) {
|
||||
dc->finished = TRUE;
|
||||
break; /* A chunk length of 0 means we're done! */
|
||||
}
|
||||
inputRemaining -= (eol - inputPtr) + 1;
|
||||
inputPtr = eol + 1;
|
||||
chunkRemaining += 2; /* CRLF at the end of every chunk */
|
||||
}
|
||||
|
||||
/* If we have a partial chunk header, save it for next time. */
|
||||
dStr_erase(dc->leftover, 0, inputPtr - dc->leftover->str);
|
||||
|
||||
*(int *)dc->state = chunkRemaining;
|
||||
return output;
|
||||
}
|
||||
|
||||
bool_t a_Decode_transfer_finished(DecodeTransfer *dc)
|
||||
{
|
||||
return dc->finished;
|
||||
}
|
||||
|
||||
void a_Decode_transfer_free(DecodeTransfer *dc)
|
||||
{
|
||||
dFree(dc->state);
|
||||
dStr_free(dc->leftover, 1);
|
||||
dFree(dc);
|
||||
}
|
||||
|
||||
static void Decode_compression_free(Decode *dc)
|
||||
{
|
||||
(void)inflateEnd((z_stream *)dc->state);
|
||||
|
||||
dFree(dc->state);
|
||||
dFree(dc->buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* BUG: A fair amount of duplicated code exists in the gzip/deflate decoding,
|
||||
* but an attempt to pull out the common code left everything too contorted
|
||||
* for what it accomplished.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Decode gzipped data
|
||||
*/
|
||||
static Dstr *Decode_gzip(Decode *dc, const char *instr, int inlen)
|
||||
{
|
||||
int rc = Z_OK;
|
||||
|
||||
z_stream *zs = (z_stream *)dc->state;
|
||||
|
||||
int inputConsumed = 0;
|
||||
Dstr *output = dStr_new("");
|
||||
|
||||
while ((rc == Z_OK) && (inputConsumed < inlen)) {
|
||||
zs->next_in = (Bytef *)instr + inputConsumed;
|
||||
zs->avail_in = inlen - inputConsumed;
|
||||
|
||||
zs->next_out = (Bytef *)dc->buffer;
|
||||
zs->avail_out = bufsize;
|
||||
|
||||
rc = inflate(zs, Z_SYNC_FLUSH);
|
||||
|
||||
dStr_append_l(output, dc->buffer, zs->total_out);
|
||||
|
||||
if ((rc == Z_OK) || (rc == Z_STREAM_END)) {
|
||||
// Z_STREAM_END at end of file
|
||||
|
||||
inputConsumed += zs->total_in;
|
||||
zs->total_out = 0;
|
||||
zs->total_in = 0;
|
||||
} else if (rc == Z_DATA_ERROR) {
|
||||
MSG_ERR("gzip decompression error\n");
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode (raw) deflated data
|
||||
*/
|
||||
static Dstr *Decode_raw_deflate(Decode *dc, const char *instr, int inlen)
|
||||
{
|
||||
int rc = Z_OK;
|
||||
|
||||
z_stream *zs = (z_stream *)dc->state;
|
||||
|
||||
int inputConsumed = 0;
|
||||
Dstr *output = dStr_new("");
|
||||
|
||||
while ((rc == Z_OK) && (inputConsumed < inlen)) {
|
||||
zs->next_in = (Bytef *)instr + inputConsumed;
|
||||
zs->avail_in = inlen - inputConsumed;
|
||||
|
||||
zs->next_out = (Bytef *)dc->buffer;
|
||||
zs->avail_out = bufsize;
|
||||
|
||||
rc = inflate(zs, Z_SYNC_FLUSH);
|
||||
|
||||
dStr_append_l(output, dc->buffer, zs->total_out);
|
||||
|
||||
if ((rc == Z_OK) || (rc == Z_STREAM_END)) {
|
||||
// Z_STREAM_END at end of file
|
||||
|
||||
inputConsumed += zs->total_in;
|
||||
zs->total_out = 0;
|
||||
zs->total_in = 0;
|
||||
} else if (rc == Z_DATA_ERROR) {
|
||||
MSG_ERR("raw deflate decompression also failed\n");
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode deflated data, initially presuming that the required zlib wrapper
|
||||
* is there. On data error, switch to Decode_raw_deflate().
|
||||
*/
|
||||
static Dstr *Decode_deflate(Decode *dc, const char *instr, int inlen)
|
||||
{
|
||||
int rc = Z_OK;
|
||||
|
||||
z_stream *zs = (z_stream *)dc->state;
|
||||
|
||||
int inputConsumed = 0;
|
||||
Dstr *output = dStr_new("");
|
||||
|
||||
while ((rc == Z_OK) && (inputConsumed < inlen)) {
|
||||
zs->next_in = (Bytef *)instr + inputConsumed;
|
||||
zs->avail_in = inlen - inputConsumed;
|
||||
|
||||
zs->next_out = (Bytef *)dc->buffer;
|
||||
zs->avail_out = bufsize;
|
||||
|
||||
rc = inflate(zs, Z_SYNC_FLUSH);
|
||||
|
||||
dStr_append_l(output, dc->buffer, zs->total_out);
|
||||
|
||||
if ((rc == Z_OK) || (rc == Z_STREAM_END)) {
|
||||
// Z_STREAM_END at end of file
|
||||
|
||||
inputConsumed += zs->total_in;
|
||||
zs->total_out = 0;
|
||||
zs->total_in = 0;
|
||||
} else if (rc == Z_DATA_ERROR) {
|
||||
MSG_WARN("Deflate decompression error. Certain servers illegally fail"
|
||||
" to send data in a zlib wrapper. Let's try raw deflate.\n");
|
||||
dStr_free(output, 1);
|
||||
(void)inflateEnd(zs);
|
||||
dFree(dc->state);
|
||||
dc->state = zs = dNew(z_stream, 1);
|
||||
zs->zalloc = NULL;
|
||||
zs->zfree = NULL;
|
||||
zs->next_in = NULL;
|
||||
zs->avail_in = 0;
|
||||
dc->decode = Decode_raw_deflate;
|
||||
|
||||
// Negative value means that we want raw deflate.
|
||||
inflateInit2(zs, -MAX_WBITS);
|
||||
|
||||
return Decode_raw_deflate(dc, instr, inlen);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate to desired character set (UTF-8)
|
||||
*/
|
||||
static Dstr *Decode_charset(Decode *dc, const char *instr, int inlen)
|
||||
{
|
||||
inbuf_t *inPtr;
|
||||
char *outPtr;
|
||||
size_t inLeft, outRoom;
|
||||
|
||||
Dstr *output = dStr_new("");
|
||||
int rc = 0;
|
||||
|
||||
dStr_append_l(dc->leftover, instr, inlen);
|
||||
inPtr = dc->leftover->str;
|
||||
inLeft = dc->leftover->len;
|
||||
|
||||
while ((rc != EINVAL) && (inLeft > 0)) {
|
||||
|
||||
outPtr = dc->buffer;
|
||||
outRoom = bufsize;
|
||||
|
||||
rc = iconv((iconv_t)dc->state, &inPtr, &inLeft, &outPtr, &outRoom);
|
||||
|
||||
// iconv() on success, number of bytes converted
|
||||
// -1, errno == EILSEQ illegal byte sequence found
|
||||
// EINVAL partial character ends source buffer
|
||||
// E2BIG destination buffer is full
|
||||
|
||||
dStr_append_l(output, dc->buffer, bufsize - outRoom);
|
||||
|
||||
if (rc == -1)
|
||||
rc = errno;
|
||||
if (rc == EILSEQ){
|
||||
inPtr++;
|
||||
inLeft--;
|
||||
dStr_append_l(output, utf8_replacement_char,
|
||||
sizeof(utf8_replacement_char) - 1);
|
||||
}
|
||||
}
|
||||
dStr_erase(dc->leftover, 0, dc->leftover->len - inLeft);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static void Decode_charset_free(Decode *dc)
|
||||
{
|
||||
/* iconv_close() frees dc->state */
|
||||
(void)iconv_close((iconv_t)(dc->state));
|
||||
|
||||
dFree(dc->buffer);
|
||||
dStr_free(dc->leftover, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize transfer decoder. Currently handles "chunked".
|
||||
*/
|
||||
DecodeTransfer *a_Decode_transfer_init(const char *format)
|
||||
{
|
||||
DecodeTransfer *dc = NULL;
|
||||
|
||||
if (format && !dStrAsciiCasecmp(format, "chunked")) {
|
||||
int *chunk_remaining = dNew(int, 1);
|
||||
*chunk_remaining = 0;
|
||||
dc = dNew(DecodeTransfer, 1);
|
||||
dc->leftover = dStr_new("");
|
||||
dc->state = chunk_remaining;
|
||||
dc->finished = FALSE;
|
||||
_MSG("chunked!\n");
|
||||
}
|
||||
return dc;
|
||||
}
|
||||
|
||||
static Decode *Decode_content_init_common(void)
|
||||
{
|
||||
z_stream *zs = dNew(z_stream, 1);
|
||||
Decode *dc = dNew(Decode, 1);
|
||||
|
||||
zs->zalloc = NULL;
|
||||
zs->zfree = NULL;
|
||||
zs->next_in = NULL;
|
||||
zs->avail_in = 0;
|
||||
dc->state = zs;
|
||||
dc->buffer = dNew(char, bufsize);
|
||||
|
||||
dc->free = Decode_compression_free;
|
||||
dc->leftover = NULL; /* not used */
|
||||
return dc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize content decoder. Currently handles 'gzip' and 'deflate'.
|
||||
*/
|
||||
Decode *a_Decode_content_init(const char *format)
|
||||
{
|
||||
z_stream *zs;
|
||||
Decode *dc = NULL;
|
||||
|
||||
if (format && *format) {
|
||||
if (!dStrAsciiCasecmp(format, "gzip") ||
|
||||
!dStrAsciiCasecmp(format, "x-gzip")) {
|
||||
_MSG("gzipped data!\n");
|
||||
|
||||
dc = Decode_content_init_common();
|
||||
zs = (z_stream *)dc->state;
|
||||
/* 16 is a magic number for gzip decoding */
|
||||
inflateInit2(zs, MAX_WBITS+16);
|
||||
|
||||
dc->decode = Decode_gzip;
|
||||
} else if (!dStrAsciiCasecmp(format, "deflate")) {
|
||||
_MSG("deflated data!\n");
|
||||
|
||||
dc = Decode_content_init_common();
|
||||
zs = (z_stream *)dc->state;
|
||||
inflateInit(zs);
|
||||
|
||||
dc->decode = Decode_deflate;
|
||||
} else {
|
||||
MSG("Content-Encoding '%s' not recognized.\n", format);
|
||||
}
|
||||
}
|
||||
return dc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize decoder to translate from any character set known to iconv()
|
||||
* to UTF-8.
|
||||
*
|
||||
* GNU iconv(1) will provide a list of known character sets if invoked with
|
||||
* the "--list" flag.
|
||||
*/
|
||||
Decode *a_Decode_charset_init(const char *format)
|
||||
{
|
||||
Decode *dc = NULL;
|
||||
|
||||
if (format &&
|
||||
strlen(format) &&
|
||||
dStrAsciiCasecmp(format,"UTF-8")) {
|
||||
|
||||
iconv_t ic = iconv_open("UTF-8", format);
|
||||
if (ic != (iconv_t) -1) {
|
||||
dc = dNew(Decode, 1);
|
||||
dc->state = ic;
|
||||
dc->buffer = dNew(char, bufsize);
|
||||
dc->leftover = dStr_new("");
|
||||
|
||||
dc->decode = Decode_charset;
|
||||
dc->free = Decode_charset_free;
|
||||
} else {
|
||||
MSG_WARN("Unable to convert from character encoding: '%s'\n", format);
|
||||
}
|
||||
}
|
||||
return dc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode data.
|
||||
*/
|
||||
Dstr *a_Decode_process(Decode *dc, const char *instr, int inlen)
|
||||
{
|
||||
return dc->decode(dc, instr, inlen);
|
||||
}
|
||||
|
||||
/*
|
||||
* Free the decoder.
|
||||
*/
|
||||
void a_Decode_free(Decode *dc)
|
||||
{
|
||||
if (dc) {
|
||||
dc->free(dc);
|
||||
dFree(dc);
|
||||
}
|
||||
}
|
||||
42
src/decode.h
Normal file
42
src/decode.h
Normal file
@ -0,0 +1,42 @@
|
||||
#ifndef __DECODE_H__
|
||||
#define __DECODE_H__
|
||||
|
||||
#include "../dlib/dlib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef struct Decode {
|
||||
char *buffer;
|
||||
Dstr *leftover;
|
||||
void *state;
|
||||
Dstr *(*decode) (struct Decode *dc, const char *instr, int inlen);
|
||||
void (*free) (struct Decode *dc);
|
||||
} Decode;
|
||||
|
||||
/* I'm not going to shoehorn the decoders into the same form anymore. They
|
||||
* can evolve independently.
|
||||
*/
|
||||
typedef struct DecodeTransfer {
|
||||
Dstr *leftover;
|
||||
void *state;
|
||||
bool_t finished; /**< has the terminating chunk been seen? */
|
||||
} DecodeTransfer;
|
||||
|
||||
DecodeTransfer *a_Decode_transfer_init(const char *format);
|
||||
Dstr *a_Decode_transfer_process(DecodeTransfer *dc, const char *instr,
|
||||
int inlen);
|
||||
bool_t a_Decode_transfer_finished(DecodeTransfer *dc);
|
||||
void a_Decode_transfer_free(DecodeTransfer *dc);
|
||||
|
||||
Decode *a_Decode_content_init(const char *format);
|
||||
Decode *a_Decode_charset_init(const char *format);
|
||||
Dstr *a_Decode_process(Decode *dc, const char *instr, int inlen);
|
||||
void a_Decode_free(Decode *dc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __DECODE_H__ */
|
||||
19
src/dgif.h
Normal file
19
src/dgif.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef __GIF_H__
|
||||
#define __GIF_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#include "url.h"
|
||||
#include "image.hh"
|
||||
|
||||
|
||||
void *a_Gif_new(DilloImage *Image, DilloUrl *url, int version);
|
||||
void a_Gif_callback(int Op, void *data);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* !__GIF_H__ */
|
||||
494
src/dialog.cc
Normal file
494
src/dialog.cc
Normal file
@ -0,0 +1,494 @@
|
||||
/*
|
||||
* File: dialog.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.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* UI dialogs
|
||||
*/
|
||||
|
||||
#include <math.h> // for rint()
|
||||
#include <string.h>
|
||||
|
||||
#include <FL/fl_ask.H>
|
||||
#include <FL/Fl_Window.H>
|
||||
#include <FL/Fl_File_Chooser.H>
|
||||
#include <FL/Fl_Return_Button.H>
|
||||
#include <FL/Fl_Text_Display.H>
|
||||
#include <FL/Fl_Button.H>
|
||||
#include <FL/Fl_Return_Button.H>
|
||||
#include <FL/Fl_Output.H>
|
||||
#include <FL/Fl_Input.H>
|
||||
#include <FL/Fl_Secret_Input.H>
|
||||
#include <FL/Fl_Choice.H>
|
||||
#include <FL/Fl_Menu_Item.H>
|
||||
|
||||
#include "msg.h"
|
||||
#include "dialog.hh"
|
||||
#include "misc.h"
|
||||
#include "prefs.h"
|
||||
#include "dlib/dlib.h"
|
||||
|
||||
/*
|
||||
* Local Data
|
||||
*/
|
||||
static int input_answer;
|
||||
static char *input_str = NULL;
|
||||
static int choice_answer;
|
||||
|
||||
|
||||
/*
|
||||
* Local sub classes
|
||||
*/
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/**
|
||||
* Used to enable CTRL+{a,e,d,k} in search dialog (for start,end,del,cut).
|
||||
* TODO: bind down arrow to a search engine selection list.
|
||||
*/
|
||||
class CustInput3 : public Fl_Input {
|
||||
public:
|
||||
CustInput3 (int x, int y, int w, int h, const char* l=0) :
|
||||
Fl_Input(x,y,w,h,l) {};
|
||||
int handle(int e);
|
||||
};
|
||||
|
||||
int CustInput3::handle(int e)
|
||||
{
|
||||
int k = Fl::event_key();
|
||||
|
||||
_MSG("CustInput3::handle event=%d\n", e);
|
||||
|
||||
// We're only interested in some flags
|
||||
unsigned modifier = Fl::event_state() & (FL_SHIFT | FL_CTRL | FL_ALT);
|
||||
|
||||
if (e == FL_KEYBOARD && modifier == FL_CTRL) {
|
||||
if (k == 'a' || k == 'e') {
|
||||
position(k == 'a' ? 0 : size());
|
||||
return 1;
|
||||
} else if (k == 'k') {
|
||||
cut(position(), size());
|
||||
return 1;
|
||||
} else if (k == 'd') {
|
||||
cut(position(), position()+1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return Fl_Input::handle(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to make the ENTER key activate the CustChoice
|
||||
*/
|
||||
class CustChoice2 : public Fl_Choice {
|
||||
public:
|
||||
CustChoice2 (int x, int y, int w, int h, const char* l=0) :
|
||||
Fl_Choice(x,y,w,h,l) {};
|
||||
int handle(int e) {
|
||||
if (e == FL_KEYBOARD &&
|
||||
(Fl::event_key() == FL_Enter || Fl::event_key() == FL_Down) &&
|
||||
(Fl::event_state() & (FL_SHIFT|FL_CTRL|FL_ALT|FL_META)) == 0) {
|
||||
return Fl_Choice::handle(FL_PUSH);
|
||||
}
|
||||
return Fl_Choice::handle(e);
|
||||
};
|
||||
};
|
||||
|
||||
class EnterButton : public Fl_Button {
|
||||
public:
|
||||
EnterButton (int x,int y,int w,int h, const char* label = 0) :
|
||||
Fl_Button (x,y,w,h,label) {};
|
||||
int handle(int e);
|
||||
};
|
||||
|
||||
int EnterButton::handle(int e)
|
||||
{
|
||||
if (e == FL_KEYBOARD && Fl::focus() == this && Fl::event_key() == FL_Enter){
|
||||
set_changed();
|
||||
simulate_key_action();
|
||||
do_callback();
|
||||
return 1;
|
||||
}
|
||||
return Fl_Button::handle(e);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
/**
|
||||
* Display a message in a popup window.
|
||||
*/
|
||||
void a_Dialog_msg(const char *title, const char *msg)
|
||||
{
|
||||
if (!(title && *title))
|
||||
title = "Dillo: Message";
|
||||
fl_message_title(title);
|
||||
fl_message("%s", msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback for a_Dialog_input()
|
||||
*/
|
||||
static void input_cb(Fl_Widget *button, void *number)
|
||||
{
|
||||
input_answer = VOIDP2INT(number);
|
||||
button->window()->hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dialog for one line of Input with a message.
|
||||
* avoids the sound bell in fl_input(), and allows customization
|
||||
*
|
||||
* @return string on success, NULL upon Cancel or Close window
|
||||
*/
|
||||
const char *a_Dialog_input(const char *title, const char *msg)
|
||||
{
|
||||
static Fl_Menu_Item *pm = 0;
|
||||
int ww = 450, wh = 130, gap = 10, ih = 60, bw = 80, bh = 30;
|
||||
|
||||
input_answer = 0;
|
||||
|
||||
if (!(title && *title))
|
||||
title = "Dillo: Input";
|
||||
|
||||
Fl_Window *window = new Fl_Window(ww,wh,title);
|
||||
window->set_modal();
|
||||
window->begin();
|
||||
Fl_Group* ib = new Fl_Group(0,0,window->w(),window->h());
|
||||
ib->begin();
|
||||
window->resizable(ib);
|
||||
|
||||
/* '?' Icon */
|
||||
Fl_Box* o = new Fl_Box(gap, gap, ih, ih);
|
||||
o->box(FL_THIN_UP_BOX);
|
||||
o->labelfont(FL_TIMES_BOLD);
|
||||
o->labelsize(34);
|
||||
o->label("?");
|
||||
o->show();
|
||||
|
||||
Fl_Box *box = new Fl_Box(ih+2*gap,gap,ww-(ih+3*gap),ih/2, msg);
|
||||
box->labelfont(FL_HELVETICA);
|
||||
box->labelsize(14);
|
||||
box->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_CLIP|FL_ALIGN_WRAP);
|
||||
|
||||
CustInput3 *c_inp = new CustInput3(ih+2*gap,gap+ih/2+gap,ww-(ih+3*gap),24);
|
||||
c_inp->labelsize(14);
|
||||
c_inp->textsize(14);
|
||||
|
||||
CustChoice2 *ch = new CustChoice2(1*gap,ih+3*gap,180,24);
|
||||
if (!pm) {
|
||||
int n_it = dList_length(prefs.search_urls);
|
||||
pm = new Fl_Menu_Item[n_it+1];
|
||||
memset(pm, '\0', (n_it + 1) * sizeof(Fl_Menu_Item));
|
||||
for (int i = 0, j = 0; i < n_it; i++) {
|
||||
char *label, *url, *source;
|
||||
source = (char *)dList_nth_data(prefs.search_urls, i);
|
||||
if (!source || a_Misc_parse_search_url(source, &label, &url) < 0)
|
||||
continue;
|
||||
pm[j++].label(FL_NORMAL_LABEL, dStrdup(label));
|
||||
}
|
||||
}
|
||||
ch->tooltip("Select search engine");
|
||||
ch->menu(pm);
|
||||
ch->value(prefs.search_url_idx);
|
||||
|
||||
int xpos = ww-2*(gap+bw), ypos = ih+3*gap;
|
||||
Fl_Return_Button *rb = new Fl_Return_Button(xpos, ypos, bw, bh, "OK");
|
||||
rb->align(FL_ALIGN_INSIDE|FL_ALIGN_CLIP);
|
||||
rb->box(FL_UP_BOX);
|
||||
rb->callback(input_cb, INT2VOIDP(1));
|
||||
|
||||
xpos = ww-(gap+bw);
|
||||
Fl_Button *b = new Fl_Button(xpos, ypos, bw, bh, "Cancel");
|
||||
b->align(FL_ALIGN_INSIDE|FL_ALIGN_CLIP);
|
||||
b->box(FL_UP_BOX);
|
||||
b->callback(input_cb, INT2VOIDP(2));
|
||||
|
||||
window->end();
|
||||
|
||||
window->show();
|
||||
while (window->shown())
|
||||
Fl::wait();
|
||||
if (input_answer == 1) {
|
||||
/* we have a string, save it */
|
||||
dFree(input_str);
|
||||
input_str = dStrdup(c_inp->value());
|
||||
prefs.search_url_idx = ch->value();
|
||||
}
|
||||
delete window;
|
||||
|
||||
return (input_answer == 1) ? input_str : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dialog for password
|
||||
*/
|
||||
const char *a_Dialog_passwd(const char *title, const char *msg)
|
||||
{
|
||||
if (!(title && *title))
|
||||
title = "Dillo: Password";
|
||||
fl_message_title(title);
|
||||
return fl_password("%s", "", msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the save file dialog.
|
||||
*
|
||||
* @return pointer to chosen filename, or NULL on Cancel.
|
||||
*/
|
||||
const char *a_Dialog_save_file(const char *title,
|
||||
const char *pattern, const char *fname)
|
||||
{
|
||||
return fl_file_chooser(title, pattern, fname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the select file dialog.
|
||||
*
|
||||
* @return pointer to chosen filename, or NULL on Cancel.
|
||||
*/
|
||||
const char *a_Dialog_select_file(const char *title,
|
||||
const char *pattern, const char *fname)
|
||||
{
|
||||
/*
|
||||
* FileChooser::type(MULTI) appears to allow multiple files to be selected,
|
||||
* but just follow save_file's path for now.
|
||||
*/
|
||||
return a_Dialog_save_file(title, pattern, fname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the open file dialog.
|
||||
*
|
||||
* @return pointer to chosen filename, or NULL on Cancel.
|
||||
*/
|
||||
char *a_Dialog_open_file(const char *title,
|
||||
const char *pattern, const char *fname)
|
||||
{
|
||||
const char *fc_name;
|
||||
|
||||
fc_name = fl_file_chooser(title, pattern, fname);
|
||||
return (fc_name) ? a_Misc_escape_chars(fc_name, "% #") : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close text window.
|
||||
*/
|
||||
static void text_window_close_cb(Fl_Widget *, void *vtd)
|
||||
{
|
||||
Fl_Text_Display *td = (Fl_Text_Display *)vtd;
|
||||
Fl_Text_Buffer *buf = td->buffer();
|
||||
|
||||
delete (Fl_Window*)td->window();
|
||||
delete buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a new window with the provided text
|
||||
*/
|
||||
void a_Dialog_text_window(const char *title, const char *txt)
|
||||
{
|
||||
int wh = prefs.height, ww = prefs.width, bh = 30;
|
||||
|
||||
if (!(title && *title))
|
||||
title = "Dillo: Text";
|
||||
|
||||
Fl_Window *window = new Fl_Window(ww, wh, title);
|
||||
Fl_Group::current(0);
|
||||
|
||||
|
||||
Fl_Text_Buffer *buf = new Fl_Text_Buffer();
|
||||
buf->text(txt);
|
||||
Fl_Text_Display *td = new Fl_Text_Display(0,0,ww, wh-bh);
|
||||
td->buffer(buf);
|
||||
td->textsize((int) rint(14.0 * prefs.font_factor));
|
||||
|
||||
/* enable wrapping lines; text uses entire width of window */
|
||||
td->wrap_mode(Fl_Text_Display::WRAP_AT_BOUNDS, 0);
|
||||
window->add(td);
|
||||
|
||||
Fl_Return_Button *b = new Fl_Return_Button (0, wh-bh, ww, bh, "Close");
|
||||
b->callback(text_window_close_cb, td);
|
||||
window->add(b);
|
||||
|
||||
window->callback(text_window_close_cb, td);
|
||||
window->resizable(td);
|
||||
window->show();
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
static void choice_cb(Fl_Widget *button, void *number)
|
||||
{
|
||||
choice_answer = VOIDP2INT(number);
|
||||
_MSG("choice_cb: %d\n", choice_answer);
|
||||
|
||||
button->window()->hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a question-dialog with a question and alternatives.
|
||||
* Last parameter must be NULL.
|
||||
*
|
||||
* @return 0 = dialog was cancelled, >0 = selected alternative.
|
||||
*/
|
||||
int a_Dialog_choice(const char *title, const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i, n;
|
||||
|
||||
if (title == NULL || *title == '\0')
|
||||
title = "Dillo: Choice";
|
||||
|
||||
va_start(ap, msg);
|
||||
for (n = 0; va_arg(ap, char *) != NULL; n++);
|
||||
va_end(ap);
|
||||
|
||||
if (n == 0) {
|
||||
MSG_ERR("Dialog_choice: no alternatives.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gap = 8;
|
||||
int ww = 140 + n * 60, wh = 120;
|
||||
int bw = (ww - gap) / n - gap, bh = 45;
|
||||
|
||||
Fl_Window *window = new Fl_Window(ww, wh, title);
|
||||
window->set_modal();
|
||||
window->begin();
|
||||
|
||||
Fl_Text_Buffer *buf = new Fl_Text_Buffer();
|
||||
buf->text(msg);
|
||||
Fl_Text_Display *td = new Fl_Text_Display(0, 0, ww, wh - bh);
|
||||
td->buffer(buf);
|
||||
td->textsize((int) rint(14.0 * prefs.font_factor));
|
||||
td->wrap_mode(Fl_Text_Display::WRAP_AT_BOUNDS, 0);
|
||||
|
||||
window->resizable(td);
|
||||
|
||||
int xpos = gap;
|
||||
va_start(ap, msg);
|
||||
for (i = 1; i <= n; i++) {
|
||||
Fl_Button *b = new EnterButton(xpos, wh-bh, bw, bh, va_arg(ap, char *));
|
||||
b->align(FL_ALIGN_WRAP | FL_ALIGN_CLIP);
|
||||
b->box(FL_UP_BOX);
|
||||
b->callback(choice_cb, INT2VOIDP(i));
|
||||
xpos += bw + gap;
|
||||
/* TODO: set focus to the *-prefixed alternative */
|
||||
}
|
||||
va_end(ap);
|
||||
window->end();
|
||||
|
||||
choice_answer = 0;
|
||||
|
||||
window->show();
|
||||
while (window->shown())
|
||||
Fl::wait();
|
||||
_MSG("Dialog_choice answer = %d\n", answer);
|
||||
td->buffer(NULL);
|
||||
delete buf;
|
||||
delete window;
|
||||
|
||||
return choice_answer;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
static void Dialog_user_password_cb(Fl_Widget *button, void *)
|
||||
{
|
||||
button->window()->user_data(button);
|
||||
button->window()->hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a user/password dialog.
|
||||
* Call the callback with the result (OK or not) and the given user and
|
||||
* password if OK.
|
||||
*/
|
||||
int a_Dialog_user_password(const char *title, const char *msg,
|
||||
UserPasswordCB cb, void *vp)
|
||||
{
|
||||
int ok = 0, window_h = 280, y, msg_w, msg_h;
|
||||
const int window_w = 300, input_x = 80, input_w = 200, input_h = 30,
|
||||
button_h = 30;
|
||||
|
||||
/* window is resized below */
|
||||
if (!(title && *title))
|
||||
title = "Dillo: User/Password";
|
||||
Fl_Window *window = new Fl_Window(window_w,window_h,title);
|
||||
Fl_Group::current(0);
|
||||
window->user_data(NULL);
|
||||
|
||||
/* message */
|
||||
y = 20;
|
||||
msg_w = window_w - 40;
|
||||
Fl_Box *msg_box = new Fl_Box(20, y, msg_w, 100); /* resized below */
|
||||
msg_box->label(msg);
|
||||
msg_box->labelfont(FL_HELVETICA);
|
||||
msg_box->labelsize(14);
|
||||
msg_box->align(FL_ALIGN_INSIDE | FL_ALIGN_TOP_LEFT | FL_ALIGN_WRAP);
|
||||
|
||||
fl_font(msg_box->labelfont(), msg_box->labelsize());
|
||||
msg_w -= 6; /* The label doesn't fill the entire box. */
|
||||
fl_measure(msg_box->label(), msg_w, msg_h, 0); // fl_measure wraps at msg_w
|
||||
msg_box->size(msg_box->w(), msg_h);
|
||||
window->add(msg_box);
|
||||
|
||||
/* inputs */
|
||||
y += msg_h + 20;
|
||||
Fl_Input *user_input = new Fl_Input(input_x, y, input_w, input_h, "User");
|
||||
user_input->labelsize(14);
|
||||
user_input->textsize(14);
|
||||
window->add(user_input);
|
||||
y += input_h + 10;
|
||||
Fl_Secret_Input *password_input =
|
||||
new Fl_Secret_Input(input_x, y, input_w, input_h, "Password");
|
||||
password_input->labelsize(14);
|
||||
password_input->textsize(14);
|
||||
window->add(password_input);
|
||||
|
||||
/* "OK" button */
|
||||
y += input_h + 20;
|
||||
Fl_Button *ok_button = new EnterButton(200, y, 50, button_h, "OK");
|
||||
ok_button->labelsize(14);
|
||||
ok_button->callback(Dialog_user_password_cb);
|
||||
window->add(ok_button);
|
||||
|
||||
/* "Cancel" button */
|
||||
Fl_Button *cancel_button =
|
||||
new EnterButton(50, y, 100, button_h, "Cancel");
|
||||
cancel_button->labelsize(14);
|
||||
cancel_button->callback(Dialog_user_password_cb);
|
||||
window->add(cancel_button);
|
||||
|
||||
y += button_h + 20;
|
||||
window_h = y;
|
||||
window->size(window_w, window_h);
|
||||
window->size_range(window_w, window_h, window_w, window_h);
|
||||
window->resizable(window);
|
||||
|
||||
window->show();
|
||||
while (window->shown())
|
||||
Fl::wait();
|
||||
|
||||
ok = ((Fl_Widget *)window->user_data()) == ok_button ? 1 : 0;
|
||||
|
||||
if (ok) {
|
||||
/* call the callback */
|
||||
const char *user, *password;
|
||||
user = user_input->value();
|
||||
password = password_input->value();
|
||||
_MSG("a_Dialog_user_passwd: ok = %d\n", ok);
|
||||
(*cb)(user, password, vp);
|
||||
}
|
||||
delete window;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
29
src/dialog.hh
Normal file
29
src/dialog.hh
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef __DIALOG_HH__
|
||||
#define __DIALOG_HH__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef void (*UserPasswordCB)(const char *user, const char *password,
|
||||
void *vp);
|
||||
|
||||
void a_Dialog_msg(const char *title, const char *msg);
|
||||
int a_Dialog_choice(const char *title, const char *msg, ...);
|
||||
int a_Dialog_user_password(const char *title, const char *msg,
|
||||
UserPasswordCB cb, void *vp);
|
||||
const char *a_Dialog_input(const char *title, const char *msg);
|
||||
const char *a_Dialog_passwd(const char *title, const char *msg);
|
||||
const char *a_Dialog_save_file(const char *title,
|
||||
const char *pattern, const char *fname);
|
||||
const char *a_Dialog_select_file(const char *title,
|
||||
const char *pattern, const char *fname);
|
||||
char *a_Dialog_open_file(const char *title,
|
||||
const char *pattern, const char *fname);
|
||||
void a_Dialog_text_window(const char *title, const char *txt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif // __DIALOG_HH__
|
||||
601
src/dicache.c
Normal file
601
src/dicache.c
Normal file
@ -0,0 +1,601 @@
|
||||
/*
|
||||
* File: dicache.c
|
||||
*
|
||||
* Copyright 2000-2007 Jorge Arellano Cid <jcid@dillo.org>
|
||||
* Copyright 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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 <string.h> /* for memset */
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "msg.h"
|
||||
#include "image.hh"
|
||||
#include "imgbuf.hh"
|
||||
#include "web.hh"
|
||||
#include "dicache.h"
|
||||
#include "dpng.h"
|
||||
#include "dwebp.h"
|
||||
#include "dgif.h"
|
||||
#include "djpeg.h"
|
||||
#include "dsvg.h"
|
||||
|
||||
enum {
|
||||
DIC_Gif = 0,
|
||||
DIC_Png,
|
||||
DIC_Webp,
|
||||
DIC_Jpeg,
|
||||
DIC_Svg,
|
||||
DIC_MAX
|
||||
};
|
||||
|
||||
static const char *format_name[DIC_MAX] = {
|
||||
[DIC_Gif] = "gif",
|
||||
[DIC_Png] = "png",
|
||||
[DIC_Webp] = "webp",
|
||||
[DIC_Jpeg] = "jpeg",
|
||||
[DIC_Svg] = "svg"
|
||||
};
|
||||
|
||||
static int disabled_formats[DIC_MAX] = { 0 };
|
||||
|
||||
/**
|
||||
* List of DICacheEntry. May hold several versions of the same image,
|
||||
* although most of the time it holds just one.
|
||||
*/
|
||||
static Dlist *CachedIMGs = NULL;
|
||||
|
||||
static uint_t dicache_size_total; /* invariant: dicache_size_total is
|
||||
* the sum of the image sizes (3*w*h)
|
||||
* of all the images in the dicache. */
|
||||
|
||||
/**
|
||||
* Compare function for image entries
|
||||
*/
|
||||
static int Dicache_entry_cmp(const void *v1, const void *v2)
|
||||
{
|
||||
const DICacheEntry *e1 = v1, *e2 = v2;
|
||||
|
||||
int st = a_Url_cmp(e1->url, e2->url);
|
||||
if (st == 0) {
|
||||
if (e2->version == DIC_Last)
|
||||
st = (e1->Flags & DIF_Last ? 0 : -1);
|
||||
else
|
||||
st = (e1->version - e2->version);
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize dicache data
|
||||
*/
|
||||
void a_Dicache_init(void)
|
||||
{
|
||||
CachedIMGs = dList_new(256);
|
||||
dicache_size_total = 0;
|
||||
|
||||
if (prefs.ignore_image_formats) {
|
||||
for (int i = 0; i < DIC_MAX; i++) {
|
||||
if (dStriAsciiStr(prefs.ignore_image_formats, format_name[i])) {
|
||||
disabled_formats[i] = 1;
|
||||
_MSG("Image format %s disabled\n", format_name[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create, and initialize a new, empty, dicache entry
|
||||
*/
|
||||
static DICacheEntry *Dicache_entry_new(void)
|
||||
{
|
||||
DICacheEntry *entry = dNew(DICacheEntry, 1);
|
||||
|
||||
entry->width = 0;
|
||||
entry->height = 0;
|
||||
entry->Flags = DIF_Valid;
|
||||
entry->SurvCleanup = 0;
|
||||
entry->type = DILLO_IMG_TYPE_NOTSET;
|
||||
entry->cmap = NULL;
|
||||
entry->v_imgbuf = NULL;
|
||||
entry->RefCount = 1;
|
||||
entry->TotalSize = 0;
|
||||
entry->ScanNumber = 0;
|
||||
entry->BitVec = NULL;
|
||||
entry->State = DIC_Empty;
|
||||
entry->version = 1;
|
||||
|
||||
entry->Decoder = NULL;
|
||||
entry->DecoderData = NULL;
|
||||
entry->DecodedSize = 0;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new entry in the dicache
|
||||
* (a single URL may have several entries)
|
||||
*/
|
||||
static DICacheEntry *Dicache_add_entry(const DilloUrl *Url)
|
||||
{
|
||||
DICacheEntry e, *entry, *last;
|
||||
|
||||
entry = Dicache_entry_new();
|
||||
e.url = (DilloUrl*)Url;
|
||||
e.version = DIC_Last;
|
||||
last = dList_find_sorted(CachedIMGs, &e, Dicache_entry_cmp);
|
||||
if (last) {
|
||||
/* URL is already in CachedIMGs, make a new version */
|
||||
last->Flags &= ~DIF_Last;
|
||||
entry->version = last->version + 1;
|
||||
}
|
||||
entry->url = a_Url_dup(Url);
|
||||
entry->Flags |= DIF_Last;
|
||||
dList_insert_sorted(CachedIMGs, entry, Dicache_entry_cmp);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search a particular version of a URL in the Dicache.
|
||||
* @return a pointer to the entry if found; NULL otherwise.
|
||||
*
|
||||
* Notes: DIC_Last means last version of the image.
|
||||
* version zero is not allowed.
|
||||
*/
|
||||
DICacheEntry *a_Dicache_get_entry(const DilloUrl *Url, int version)
|
||||
{
|
||||
DICacheEntry e;
|
||||
DICacheEntry *entry = NULL;
|
||||
|
||||
dReturn_val_if_fail(version != 0, NULL);
|
||||
e.url = (DilloUrl*)Url;
|
||||
e.version = version;
|
||||
entry = dList_find_sorted(CachedIMGs, &e, Dicache_entry_cmp);
|
||||
if (entry && !(entry->Flags & DIF_Valid) && version == DIC_Last)
|
||||
entry = NULL;
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually free a dicache entry, given the URL and the version number.
|
||||
*/
|
||||
static void Dicache_remove(const DilloUrl *Url, int version)
|
||||
{
|
||||
DICacheEntry e, *entry;
|
||||
|
||||
_MSG("Dicache_remove url=%s\n", URL_STR(Url));
|
||||
e.url = (DilloUrl*)Url;
|
||||
e.version = version;
|
||||
entry = dList_find_sorted(CachedIMGs, &e, Dicache_entry_cmp);
|
||||
dReturn_if (entry == NULL);
|
||||
|
||||
_MSG("Dicache_remove Imgbuf=%p Decoder=%p DecoderData=%p\n",
|
||||
entry->v_imgbuf, entry->Decoder, entry->DecoderData);
|
||||
/* Eliminate this dicache entry */
|
||||
dList_remove(CachedIMGs, entry);
|
||||
dicache_size_total -= entry->TotalSize;
|
||||
|
||||
/* entry cleanup */
|
||||
a_Url_free(entry->url);
|
||||
dFree(entry->cmap);
|
||||
a_Bitvec_free(entry->BitVec);
|
||||
a_Imgbuf_unref(entry->v_imgbuf);
|
||||
if (entry->Decoder) {
|
||||
entry->Decoder(CA_Abort, entry->DecoderData);
|
||||
}
|
||||
dFree(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unrefs the counter of a dicache entry (it counts cache clients).
|
||||
* If there're no clients and no imgbuf, remove the entry.
|
||||
* Otherwise, let a_Dicache_cleanup() do the job later
|
||||
* (keeping it cached meanwhile for e.g. reload, repush, back/fwd).
|
||||
*/
|
||||
void a_Dicache_unref(const DilloUrl *Url, int version)
|
||||
{
|
||||
DICacheEntry *entry;
|
||||
|
||||
if ((entry = a_Dicache_get_entry(Url, version))) {
|
||||
_MSG("a_Dicache_unref: RefCount=%d State=%d ImgbufLastRef=%d\n",
|
||||
entry->RefCount, entry->State,
|
||||
entry->v_imgbuf ? a_Imgbuf_last_reference(entry->v_imgbuf) : -1);
|
||||
if (entry->RefCount > 0) --entry->RefCount;
|
||||
if (entry->RefCount == 0 && entry->v_imgbuf == NULL)
|
||||
Dicache_remove(Url, version);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refs the counter of a dicache entry.
|
||||
*/
|
||||
DICacheEntry* a_Dicache_ref(const DilloUrl *Url, int version)
|
||||
{
|
||||
DICacheEntry *entry;
|
||||
|
||||
if ((entry = a_Dicache_get_entry(Url, version))) {
|
||||
++entry->RefCount;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate this entry. This is used for the reloading mechanism.
|
||||
* Can't erase current versions, but a_Dicache_get_entry(url, DIC_Last)
|
||||
* must return NULL.
|
||||
*/
|
||||
void a_Dicache_invalidate_entry(const DilloUrl *Url)
|
||||
{
|
||||
DICacheEntry *entry = a_Dicache_get_entry(Url, DIC_Last);
|
||||
if (entry)
|
||||
entry->Flags &= ~DIF_Valid;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Set image's width, height & type
|
||||
* - 'width' and 'height' come from the image data.
|
||||
* - HTML width and height attrs are handled with setNonCssHint.
|
||||
* - CSS sizing is handled by the CSS engine.
|
||||
*/
|
||||
void a_Dicache_set_parms(DilloUrl *url, int version, DilloImage *Image,
|
||||
uint_t width, uint_t height, DilloImgType type,
|
||||
double gamma)
|
||||
{
|
||||
DICacheEntry *DicEntry;
|
||||
|
||||
_MSG("a_Dicache_set_parms (%s)\n", URL_STR(url));
|
||||
dReturn_if_fail ( Image != NULL && width && height );
|
||||
/* Find the DicEntry for this Image */
|
||||
DicEntry = a_Dicache_get_entry(url, version);
|
||||
dReturn_if_fail ( DicEntry != NULL );
|
||||
/* Parameters already set? Don't do it twice. */
|
||||
dReturn_if_fail ( DicEntry->State < DIC_SetParms );
|
||||
|
||||
_MSG(" RefCount=%d version=%d\n", DicEntry->RefCount, DicEntry->version);
|
||||
|
||||
/* BUG: there's just one image-type now */
|
||||
#define I_RGB 0
|
||||
DicEntry->v_imgbuf =
|
||||
a_Imgbuf_new(Image->layout, I_RGB, width, height, gamma);
|
||||
|
||||
DicEntry->TotalSize = width * height * 3;
|
||||
DicEntry->width = width;
|
||||
DicEntry->height = height;
|
||||
DicEntry->type = type;
|
||||
DicEntry->BitVec = a_Bitvec_new((int)height);
|
||||
DicEntry->State = DIC_SetParms;
|
||||
|
||||
dicache_size_total += DicEntry->TotalSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement the set_cmap method for the Image
|
||||
*/
|
||||
void a_Dicache_set_cmap(DilloUrl *url, int version, int bg_color,
|
||||
const uchar_t *cmap, uint_t num_colors,
|
||||
int num_colors_max, int bg_index)
|
||||
{
|
||||
DICacheEntry *DicEntry = a_Dicache_get_entry(url, version);
|
||||
|
||||
_MSG("a_Dicache_set_cmap\n");
|
||||
dReturn_if_fail ( DicEntry != NULL );
|
||||
|
||||
dFree(DicEntry->cmap);
|
||||
DicEntry->cmap = dNew0(uchar_t, 3 * num_colors_max);
|
||||
memcpy(DicEntry->cmap, cmap, 3 * num_colors);
|
||||
if (bg_index >= 0 && (uint_t)bg_index < num_colors) {
|
||||
DicEntry->cmap[bg_index * 3] = (bg_color >> 16) & 0xff;
|
||||
DicEntry->cmap[bg_index * 3 + 1] = (bg_color >> 8) & 0xff;
|
||||
DicEntry->cmap[bg_index * 3 + 2] = (bg_color) & 0xff;
|
||||
}
|
||||
|
||||
DicEntry->State = DIC_SetCmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset for a new scan from a multiple-scan image.
|
||||
*/
|
||||
void a_Dicache_new_scan(const DilloUrl *url, int version)
|
||||
{
|
||||
DICacheEntry *DicEntry;
|
||||
|
||||
_MSG("a_Dicache_new_scan\n");
|
||||
dReturn_if_fail ( url != NULL );
|
||||
DicEntry = a_Dicache_get_entry(url, version);
|
||||
dReturn_if_fail ( DicEntry != NULL );
|
||||
if (DicEntry->State < DIC_SetParms) {
|
||||
MSG("a_Dicache_new_scan before DIC_SetParms\n");
|
||||
exit(1);
|
||||
}
|
||||
a_Bitvec_clear(DicEntry->BitVec);
|
||||
DicEntry->ScanNumber++;
|
||||
a_Imgbuf_new_scan(DicEntry->v_imgbuf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement the write method
|
||||
* (Write a scan line into the Dicache entry)
|
||||
* buf: row buffer
|
||||
* Y : row number
|
||||
*/
|
||||
void a_Dicache_write(DilloUrl *url, int version, const uchar_t *buf, uint_t Y)
|
||||
{
|
||||
DICacheEntry *DicEntry;
|
||||
|
||||
_MSG("a_Dicache_write\n");
|
||||
DicEntry = a_Dicache_get_entry(url, version);
|
||||
dReturn_if_fail ( DicEntry != NULL );
|
||||
dReturn_if_fail ( DicEntry->width > 0 && DicEntry->height > 0 );
|
||||
|
||||
/* update the common buffer in the imgbuf */
|
||||
a_Imgbuf_update(DicEntry->v_imgbuf, buf, DicEntry->type,
|
||||
DicEntry->cmap, DicEntry->width, DicEntry->height, Y);
|
||||
|
||||
a_Bitvec_set_bit(DicEntry->BitVec, (int)Y);
|
||||
DicEntry->State = DIC_Write;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement the close method of the decoding process
|
||||
*/
|
||||
void a_Dicache_close(DilloUrl *url, int version, CacheClient_t *Client)
|
||||
{
|
||||
DilloWeb *Web = Client->Web;
|
||||
DICacheEntry *DicEntry = a_Dicache_get_entry(url, version);
|
||||
|
||||
dReturn_if_fail ( DicEntry != NULL );
|
||||
|
||||
/* a_Dicache_unref() may free DicEntry */
|
||||
_MSG("a_Dicache_close RefCount=%d\n", DicEntry->RefCount - 1);
|
||||
_MSG("a_Dicache_close DIC_Close=%d State=%d\n", DIC_Close, DicEntry->State);
|
||||
_MSG(" a_Dicache_close imgbuf=%p Decoder=%p DecoderData=%p\n",
|
||||
DicEntry->v_imgbuf, DicEntry->Decoder, DicEntry->DecoderData);
|
||||
|
||||
if (DicEntry->State < DIC_Close) {
|
||||
DicEntry->State = DIC_Close;
|
||||
dFree(DicEntry->cmap);
|
||||
DicEntry->cmap = NULL;
|
||||
DicEntry->Decoder = NULL;
|
||||
DicEntry->DecoderData = NULL;
|
||||
}
|
||||
a_Dicache_unref(url, version);
|
||||
|
||||
a_Bw_close_client(Web->bw, Client->Key);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Generic MIME handler for GIF, JPEG, PNG and SVG.
|
||||
* Sets a_Dicache_callback as the cache-client,
|
||||
* and also sets the image decoder.
|
||||
*
|
||||
* @param Type MIME type
|
||||
* @param Ptr points to a \ref DilloWeb structure
|
||||
* @param Call Dillo calls this with more data/eod
|
||||
* @param Data Decoding data structure
|
||||
*/
|
||||
static void *Dicache_image(int ImgType, const char *MimeType, void *Ptr,
|
||||
CA_Callback_t *Call, void **Data)
|
||||
{
|
||||
DilloWeb *web = Ptr;
|
||||
DICacheEntry *DicEntry;
|
||||
|
||||
dReturn_val_if_fail(MimeType && Ptr, NULL);
|
||||
|
||||
if (ImgType >= 0 && ImgType < DIC_MAX && disabled_formats[ImgType]) {
|
||||
_MSG("Ignoring image format %s\n", format_name[ImgType]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!web->Image) {
|
||||
web->Image =
|
||||
a_Image_new_with_dw(web->bw->render_layout, NULL, web->bgColor, 0);
|
||||
a_Image_ref(web->Image);
|
||||
}
|
||||
|
||||
DicEntry = a_Dicache_get_entry(web->url, DIC_Last);
|
||||
if (!DicEntry) {
|
||||
/* Create an entry for this image... */
|
||||
DicEntry = Dicache_add_entry(web->url);
|
||||
/* Attach a decoder */
|
||||
if (ImgType == DIC_Jpeg) {
|
||||
DicEntry->Decoder = (CA_Callback_t)a_Jpeg_callback;
|
||||
DicEntry->DecoderData =
|
||||
a_Jpeg_new(web->Image, DicEntry->url, DicEntry->version);
|
||||
} else if (ImgType == DIC_Gif) {
|
||||
DicEntry->Decoder = (CA_Callback_t)a_Gif_callback;
|
||||
DicEntry->DecoderData =
|
||||
a_Gif_new(web->Image, DicEntry->url, DicEntry->version);
|
||||
} else if (ImgType == DIC_Webp) {
|
||||
DicEntry->Decoder = (CA_Callback_t)a_Webp_callback;
|
||||
DicEntry->DecoderData =
|
||||
a_Webp_new(web->Image, DicEntry->url, DicEntry->version);
|
||||
} else if (ImgType == DIC_Png) {
|
||||
DicEntry->Decoder = (CA_Callback_t)a_Png_callback;
|
||||
DicEntry->DecoderData =
|
||||
a_Png_new(web->Image, DicEntry->url, DicEntry->version);
|
||||
} else if (ImgType == DIC_Svg) {
|
||||
DicEntry->Decoder = (CA_Callback_t)a_Svg_callback;
|
||||
DicEntry->DecoderData =
|
||||
a_Svg_new(web->Image, DicEntry->url, DicEntry->version);
|
||||
}
|
||||
} else {
|
||||
/* Repeated image */
|
||||
a_Dicache_ref(DicEntry->url, DicEntry->version);
|
||||
}
|
||||
/* Survive three cleanup passes (set to zero = old behaviour). */
|
||||
DicEntry->SurvCleanup = 3;
|
||||
|
||||
*Data = DicEntry->DecoderData;
|
||||
*Call = (CA_Callback_t) a_Dicache_callback;
|
||||
|
||||
return (a_Image_get_dw (web->Image));
|
||||
}
|
||||
|
||||
/**
|
||||
* PNG wrapper for Dicache_image()
|
||||
*/
|
||||
void *a_Dicache_png_image(const char *Type, void *Ptr, CA_Callback_t *Call,
|
||||
void **Data)
|
||||
{
|
||||
return Dicache_image(DIC_Png, Type, Ptr, Call, Data);
|
||||
}
|
||||
|
||||
/**
|
||||
* WEBP wrapper for Dicache_image()
|
||||
*/
|
||||
void *a_Dicache_webp_image(const char *Type, void *Ptr, CA_Callback_t *Call,
|
||||
void **Data)
|
||||
{
|
||||
return Dicache_image(DIC_Webp, Type, Ptr, Call, Data);
|
||||
}
|
||||
|
||||
/**
|
||||
* GIF wrapper for Dicache_image()
|
||||
*/
|
||||
void *a_Dicache_gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
|
||||
void **Data)
|
||||
{
|
||||
return Dicache_image(DIC_Gif, Type, Ptr, Call, Data);
|
||||
}
|
||||
|
||||
/**
|
||||
* JPEG wrapper for Dicache_image()
|
||||
*/
|
||||
void *a_Dicache_jpeg_image(const char *Type, void *Ptr, CA_Callback_t *Call,
|
||||
void **Data)
|
||||
{
|
||||
return Dicache_image(DIC_Jpeg, Type, Ptr, Call, Data);
|
||||
}
|
||||
|
||||
/**
|
||||
* SVG wrapper for Dicache_image()
|
||||
*/
|
||||
void *a_Dicache_svg_image(const char *Type, void *Ptr, CA_Callback_t *Call,
|
||||
void **Data)
|
||||
{
|
||||
return Dicache_image(DIC_Svg, Type, Ptr, Call, Data);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is a cache client; (but feeds its clients from dicache)
|
||||
*/
|
||||
void a_Dicache_callback(int Op, CacheClient_t *Client)
|
||||
{
|
||||
uint_t i;
|
||||
DilloWeb *Web = Client->Web;
|
||||
DilloImage *Image = Web->Image;
|
||||
DICacheEntry *DicEntry = a_Dicache_get_entry(Web->url, DIC_Last);
|
||||
|
||||
dReturn_if_fail ( DicEntry != NULL );
|
||||
|
||||
/* Copy the version number in the Client */
|
||||
if (Client->Version == 0)
|
||||
Client->Version = DicEntry->version;
|
||||
|
||||
/* Only call the decoder when necessary */
|
||||
if (Op == CA_Send && DicEntry->State < DIC_Close &&
|
||||
DicEntry->DecodedSize < Client->BufSize) {
|
||||
DicEntry->Decoder(Op, Client);
|
||||
DicEntry->DecodedSize = Client->BufSize;
|
||||
} else if (Op == CA_Close || Op == CA_Abort) {
|
||||
if (DicEntry->State < DIC_Close) {
|
||||
DicEntry->Decoder(Op, Client);
|
||||
} else {
|
||||
a_Dicache_close(DicEntry->url, DicEntry->version, Client);
|
||||
}
|
||||
}
|
||||
|
||||
/* when the data stream is not an image 'v_imgbuf' remains NULL */
|
||||
if (Op == CA_Send && DicEntry->v_imgbuf) {
|
||||
if (Image->height == 0 && DicEntry->State >= DIC_SetParms) {
|
||||
/* Set parms */
|
||||
a_Image_set_parms(
|
||||
Image, DicEntry->v_imgbuf, DicEntry->url,
|
||||
DicEntry->version, DicEntry->width, DicEntry->height,
|
||||
DicEntry->type);
|
||||
}
|
||||
if (DicEntry->State == DIC_Write) {
|
||||
if (DicEntry->ScanNumber == Image->ScanNumber) {
|
||||
for (i = 0; i < DicEntry->height; ++i)
|
||||
if (a_Bitvec_get_bit(DicEntry->BitVec, (int)i) &&
|
||||
!a_Bitvec_get_bit(Image->BitVec, (int)i) )
|
||||
a_Image_write(Image, i);
|
||||
} else {
|
||||
for (i = 0; i < DicEntry->height; ++i) {
|
||||
if (a_Bitvec_get_bit(DicEntry->BitVec, (int)i) ||
|
||||
!a_Bitvec_get_bit(Image->BitVec, (int)i) ||
|
||||
DicEntry->ScanNumber > Image->ScanNumber + 1) {
|
||||
a_Image_write(Image, i);
|
||||
}
|
||||
if (!a_Bitvec_get_bit(DicEntry->BitVec, (int)i))
|
||||
a_Bitvec_clear_bit(Image->BitVec, (int)i);
|
||||
}
|
||||
Image->ScanNumber = DicEntry->ScanNumber;
|
||||
}
|
||||
}
|
||||
} else if (Op == CA_Close) {
|
||||
a_Image_close(Image);
|
||||
a_Bw_close_client(Web->bw, Client->Key);
|
||||
} else if (Op == CA_Abort) {
|
||||
a_Image_abort(Image);
|
||||
a_Bw_close_client(Web->bw, Client->Key);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Free the imgbuf (RGB data) of unused entries.
|
||||
*/
|
||||
void a_Dicache_cleanup(void)
|
||||
{
|
||||
int i;
|
||||
DICacheEntry *entry;
|
||||
|
||||
for (i = 0; (entry = dList_nth_data(CachedIMGs, i)); ++i) {
|
||||
_MSG(" SurvCleanup = %d\n", entry->SurvCleanup);
|
||||
if (entry->RefCount == 0 &&
|
||||
(!entry->v_imgbuf || a_Imgbuf_last_reference(entry->v_imgbuf))) {
|
||||
if (--entry->SurvCleanup >= 0)
|
||||
continue; /* keep the entry one more pass */
|
||||
|
||||
/* free this unused entry */
|
||||
Dicache_remove(entry->url, entry->version);
|
||||
--i; /* adjust counter */
|
||||
}
|
||||
}
|
||||
_MSG("a_Dicache_cleanup: length = %d\n", dList_length(CachedIMGs));
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Deallocate memory used by dicache module
|
||||
* (Call this one at exit time, with no cache clients queued)
|
||||
*/
|
||||
void a_Dicache_freeall(void)
|
||||
{
|
||||
DICacheEntry *entry;
|
||||
|
||||
/* Remove all the dicache entries */
|
||||
while ((entry = dList_nth_data(CachedIMGs, dList_length(CachedIMGs)-1))) {
|
||||
dList_remove_fast(CachedIMGs, entry);
|
||||
a_Url_free(entry->url);
|
||||
dFree(entry->cmap);
|
||||
a_Bitvec_free(entry->BitVec);
|
||||
a_Imgbuf_unref(entry->v_imgbuf);
|
||||
dicache_size_total -= entry->TotalSize;
|
||||
dFree(entry);
|
||||
}
|
||||
dList_free(CachedIMGs);
|
||||
}
|
||||
84
src/dicache.h
Normal file
84
src/dicache.h
Normal file
@ -0,0 +1,84 @@
|
||||
#ifndef __DICACHE_H__
|
||||
#define __DICACHE_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#include "bitvec.h"
|
||||
#include "image.hh"
|
||||
#include "cache.h"
|
||||
|
||||
/** Symbolic name to request the last version of an image */
|
||||
#define DIC_Last -1
|
||||
/** Flags: Last version, Valid entry */
|
||||
#define DIF_Last 1
|
||||
#define DIF_Valid 2
|
||||
|
||||
|
||||
/* These will reflect the entry's "state" */
|
||||
typedef enum {
|
||||
DIC_Empty, /**< Just created the entry */
|
||||
DIC_SetParms, /**< Parameters set */
|
||||
DIC_SetCmap, /**< Color map set */
|
||||
DIC_Write, /**< Feeding the entry */
|
||||
DIC_Close, /**< Whole image got! */
|
||||
DIC_Abort /**< Image transfer aborted */
|
||||
} DicEntryState;
|
||||
|
||||
typedef struct DICacheEntry {
|
||||
DilloUrl *url; /**< Image URL for this entry */
|
||||
DilloImgType type; /**< Image type */
|
||||
uint_t width, height; /**< As taken from image data */
|
||||
short Flags; /**< See Flags */
|
||||
short SurvCleanup; /**< Cleanup-pass survival for unused images */
|
||||
uchar_t *cmap; /**< Color map */
|
||||
void *v_imgbuf; /**< Void pointer to an Imgbuf object */
|
||||
uint_t TotalSize; /**< Amount of memory the image takes up */
|
||||
uint_t ScanNumber; /**< Current decoding scan */
|
||||
bitvec_t *BitVec; /**< Bit vector for decoded rows */
|
||||
DicEntryState State; /**< Current status for this entry */
|
||||
int RefCount; /**< Reference Counter */
|
||||
int version; /**< Version number, used for different
|
||||
versions of the same URL image */
|
||||
|
||||
uint_t DecodedSize; /**< Size of already decoded data */
|
||||
CA_Callback_t Decoder; /**< Client function */
|
||||
void *DecoderData; /**< Client function data */
|
||||
} DICacheEntry;
|
||||
|
||||
|
||||
void a_Dicache_init (void);
|
||||
|
||||
DICacheEntry *a_Dicache_get_entry(const DilloUrl *Url, int version);
|
||||
|
||||
void *a_Dicache_png_image(const char *Type, void *Ptr, CA_Callback_t *Call,
|
||||
void **Data);
|
||||
void *a_Dicache_gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
|
||||
void **Data);
|
||||
void *a_Dicache_jpeg_image(const char *Type, void *Ptr, CA_Callback_t *Call,
|
||||
void **Data);
|
||||
void a_Dicache_callback(int Op, CacheClient_t *Client);
|
||||
|
||||
void a_Dicache_set_parms(DilloUrl *url, int version, DilloImage *Image,
|
||||
uint_t width, uint_t height, DilloImgType type,
|
||||
double gamma);
|
||||
void a_Dicache_set_cmap(DilloUrl *url, int version, int bg_color,
|
||||
const uchar_t *cmap, uint_t num_colors,
|
||||
int num_colors_max, int bg_index);
|
||||
void a_Dicache_new_scan(const DilloUrl *url, int version);
|
||||
void a_Dicache_write(DilloUrl *url, int version, const uchar_t *buf, uint_t Y);
|
||||
void a_Dicache_close(DilloUrl *url, int version, CacheClient_t *Client);
|
||||
|
||||
void a_Dicache_invalidate_entry(const DilloUrl *Url);
|
||||
DICacheEntry* a_Dicache_ref(const DilloUrl *Url, int version);
|
||||
void a_Dicache_unref(const DilloUrl *Url, int version);
|
||||
void a_Dicache_cleanup(void);
|
||||
void a_Dicache_freeall(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* __DICACHE_H__ */
|
||||
204
src/digest.c
Normal file
204
src/digest.c
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* File: digest.c
|
||||
*
|
||||
* Copyright 2009 Justus Winter <4winter@informatik.uni-hamburg.de>
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include "digest.h"
|
||||
#include "md5.h"
|
||||
#include "msg.h"
|
||||
#include "../dlib/dlib.h"
|
||||
|
||||
static const char *ALGORITHM2STR[] = { NULL, "MD5", "MD5-sess" };
|
||||
static const char *QOP2STR[] = { NULL, "auth", "auth-int" };
|
||||
static const char hexchars[] = "0123456789abcdef";
|
||||
|
||||
static Dstr *md5hexdigest(const Dstr *data)
|
||||
{
|
||||
md5_state_t state;
|
||||
md5_byte_t digest[16];
|
||||
Dstr *result = dStr_sized_new(33);
|
||||
int i;
|
||||
|
||||
md5_init(&state);
|
||||
md5_append(&state, (const md5_byte_t *)data->str, data->len);
|
||||
md5_finish(&state, digest);
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
dStr_sprintfa(result, "%02x", digest[i]);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pointer to a newly allocated string containing a cnonce
|
||||
*/
|
||||
char *a_Digest_create_cnonce(void)
|
||||
{
|
||||
int i;
|
||||
char *result = dNew(char, 33);
|
||||
for (i = 0; i < 32; i++)
|
||||
result[i] = hexchars[rand() % 16];
|
||||
result[32] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This portion only has to be calculated once.
|
||||
*/
|
||||
int a_Digest_compute_digest(AuthRealm_t *realm, const char *username,
|
||||
const char *passwd)
|
||||
{
|
||||
Dstr *a1;
|
||||
Dstr *digest;
|
||||
|
||||
if (realm->algorithm == MD5 || realm->algorithm == ALGORITHMNOTSET) {
|
||||
/* A1 = unq(username-value) ":" unq(realm-value) ":" passwd */
|
||||
a1 = dStr_new(NULL);
|
||||
dStr_sprintf(a1, "%s:%s:%s", username, realm->name, passwd);
|
||||
} else if (realm->algorithm == MD5SESS) {
|
||||
/* A1 = H( unq(username-value) ":" unq(realm-value)
|
||||
** ":" passwd )
|
||||
** ":" unq(nonce-value) ":" unq(cnonce-value)
|
||||
*/
|
||||
Dstr *a0 = dStr_new(NULL);
|
||||
dStr_sprintf(a0, "%s:%s:%s", username, realm->name, passwd);
|
||||
Dstr *ha0 = md5hexdigest(a0);
|
||||
a1 = dStr_new(NULL);
|
||||
dStr_sprintf(a1, "%s:%s:%s", ha0, realm->nonce, realm->cnonce);
|
||||
dStr_free(a0, 1);
|
||||
dStr_free(ha0, 1);
|
||||
} else {
|
||||
MSG("a_Digest_create_auth: Unknown algorithm.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
digest = md5hexdigest(a1);
|
||||
realm->authorization = digest->str;
|
||||
dStr_shred(a1);
|
||||
dStr_free(a1, 1);
|
||||
dStr_free(digest, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* This portion is calculated for each request.
|
||||
*/
|
||||
static Dstr *Digest_create_response(AuthRealm_t *realm, const char *method,
|
||||
const char *digest_uri,
|
||||
const Dstr *entity_body)
|
||||
{
|
||||
Dstr *ha2;
|
||||
Dstr *result;
|
||||
|
||||
if (realm->qop == QOPNOTSET || realm->qop == AUTH) {
|
||||
/* A2 = Method ":" digest-uri-value */
|
||||
Dstr *a2 = dStr_new(NULL);
|
||||
dStr_sprintf(a2, "%s:%s", method, digest_uri);
|
||||
ha2 = md5hexdigest(a2);
|
||||
dStr_free(a2, 1);
|
||||
} else if (realm->qop == AUTHINT) {
|
||||
/* A2 = Method ":" digest-uri-value ":" H(entity-body) */
|
||||
Dstr *hentity = md5hexdigest(entity_body);
|
||||
Dstr *a2 = dStr_new(NULL);
|
||||
dStr_sprintf(a2, "%s:%s:%s", method, digest_uri, hentity->str);
|
||||
ha2 = md5hexdigest(a2);
|
||||
dStr_free(hentity, 1);
|
||||
dStr_free(a2, 1);
|
||||
} else {
|
||||
MSG("a_Digest_create_auth: Unknown qop value.\n");
|
||||
return NULL;
|
||||
}
|
||||
result = dStr_new(NULL);
|
||||
|
||||
if (realm->qop == AUTH || realm->qop == AUTHINT) {
|
||||
dStr_sprintf(result,
|
||||
"%s:%s:%08x:%s:%s:%s",
|
||||
realm->authorization,
|
||||
realm->nonce,
|
||||
realm->nonce_count,
|
||||
realm->cnonce,
|
||||
QOP2STR[realm->qop],
|
||||
ha2->str);
|
||||
} else {
|
||||
dStr_sprintf(result,
|
||||
"%s:%s:%s",
|
||||
realm->authorization,
|
||||
realm->nonce,
|
||||
ha2->str);
|
||||
}
|
||||
|
||||
Dstr *request_digest = md5hexdigest(result);
|
||||
dStr_free(result, 1);
|
||||
dStr_free(ha2, 1);
|
||||
return request_digest;
|
||||
}
|
||||
|
||||
static void Digest_Dstr_append_token_value(Dstr *str, int delimiter,
|
||||
const char *token,
|
||||
const char *value, int quoted)
|
||||
{
|
||||
char c;
|
||||
dStr_sprintfa(str, "%s%s=", (delimiter ? ", " : ""), token);
|
||||
if (quoted) {
|
||||
dStr_append_c(str, '"');
|
||||
while ((c = *value++)) {
|
||||
if (c == '"')
|
||||
dStr_append_c(str, '\\');
|
||||
dStr_append_c(str, c);
|
||||
}
|
||||
dStr_append_c(str, '"');
|
||||
} else {
|
||||
dStr_append(str, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct Digest Authorization header.
|
||||
*
|
||||
* Field ordering: furaisanjin reports that his DVD recorder requires the
|
||||
* order that IE happens to use: "username, realm, nonce, uri, cnonce, nc,
|
||||
* algorithm, response, qop". It apparently doesn't use "opaque", so that's
|
||||
* been left where it already was.
|
||||
*/
|
||||
char *a_Digest_authorization_hdr(AuthRealm_t *realm, const DilloUrl *url,
|
||||
const char *digest_uri)
|
||||
{
|
||||
char *ret;
|
||||
Dstr *response, *result;
|
||||
const char *method = URL_FLAGS(url) & URL_Post ? "POST" : "GET";
|
||||
|
||||
realm->nonce_count++;
|
||||
response = Digest_create_response(realm, method, digest_uri, URL_DATA(url));
|
||||
if (!response)
|
||||
return NULL;
|
||||
result = dStr_new("Authorization: Digest ");
|
||||
Digest_Dstr_append_token_value(result, 0, "username", realm->username, 1);
|
||||
Digest_Dstr_append_token_value(result, 1, "realm", realm->name, 1);
|
||||
Digest_Dstr_append_token_value(result, 1, "nonce", realm->nonce, 1);
|
||||
Digest_Dstr_append_token_value(result, 1, "uri", digest_uri, 1);
|
||||
if (realm->qop != QOPNOTSET) {
|
||||
Digest_Dstr_append_token_value(result, 1, "cnonce", realm->cnonce, 1);
|
||||
dStr_sprintfa(result, ", nc=%08x", realm->nonce_count);
|
||||
}
|
||||
if (realm->algorithm != ALGORITHMNOTSET) {
|
||||
Digest_Dstr_append_token_value(result, 1, "algorithm",
|
||||
ALGORITHM2STR[realm->algorithm], 0);
|
||||
}
|
||||
Digest_Dstr_append_token_value(result, 1, "response", response->str, 1);
|
||||
if (realm->opaque)
|
||||
Digest_Dstr_append_token_value(result, 1, "opaque", realm->opaque, 1);
|
||||
if (realm->qop != QOPNOTSET)
|
||||
Digest_Dstr_append_token_value(result, 1, "qop", QOP2STR[realm->qop], 1);
|
||||
dStr_sprintfa(result, "\r\n");
|
||||
|
||||
dStr_free(response, 1);
|
||||
ret = result->str;
|
||||
dStr_free(result, 0);
|
||||
return ret;
|
||||
}
|
||||
16
src/digest.h
Normal file
16
src/digest.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef __DIGEST_H__
|
||||
#define __DIGEST_H__
|
||||
|
||||
#include "auth.h"
|
||||
#include "../dlib/dlib.h"
|
||||
|
||||
|
||||
char *a_Digest_create_cnonce(void);
|
||||
int a_Digest_compute_digest(AuthRealm_t *realm,
|
||||
const char *username,
|
||||
const char *passwd);
|
||||
char *a_Digest_authorization_hdr(AuthRealm_t *realm,
|
||||
const DilloUrl *url,
|
||||
const char *uri);
|
||||
|
||||
#endif /* !__DIGEST_H__ */
|
||||
633
src/dillo.cc
Normal file
633
src/dillo.cc
Normal file
@ -0,0 +1,633 @@
|
||||
/*
|
||||
* Dillo web browser
|
||||
*
|
||||
* Copyright 1999-2007 Jorge Arellano Cid <jcid@dillo.org>
|
||||
* Copyright 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* Dillo web browser.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <locale.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <FL/Fl.H>
|
||||
#include <FL/Fl_Window.H>
|
||||
#include <FL/fl_ask.H>
|
||||
#include <FL/fl_draw.H>
|
||||
|
||||
#include "msg.h"
|
||||
#include "paths.hh"
|
||||
#include "uicmd.hh"
|
||||
|
||||
#include "prefs.h"
|
||||
#include "prefsparser.hh"
|
||||
#include "keys.hh"
|
||||
#include "bw.h"
|
||||
#include "misc.h"
|
||||
#include "history.h"
|
||||
#include "version.hh"
|
||||
|
||||
#include "dns.h"
|
||||
#include "web.hh"
|
||||
#include "IO/tls.h"
|
||||
#include "IO/Url.h"
|
||||
#include "IO/mime.h"
|
||||
#include "capi.h"
|
||||
#include "dicache.h"
|
||||
#include "cookies.h"
|
||||
#include "actions.h"
|
||||
#include "hsts.h"
|
||||
#include "domain.h"
|
||||
#include "auth.h"
|
||||
#include "styleengine.hh"
|
||||
|
||||
#include "dw/fltkcore.hh"
|
||||
#include "dw/widget.hh"
|
||||
#include "dw/textblock.hh"
|
||||
#include "dw/table.hh"
|
||||
|
||||
static volatile sig_atomic_t sig_reload = 0;
|
||||
|
||||
/**
|
||||
* Command line options structure
|
||||
*/
|
||||
typedef enum {
|
||||
DILLO_CLI_NONE = 0,
|
||||
DILLO_CLI_XID = 1 << 0,
|
||||
DILLO_CLI_FULLWINDOW = 1 << 1,
|
||||
DILLO_CLI_HELP = 1 << 2,
|
||||
DILLO_CLI_VERSION = 1 << 3,
|
||||
DILLO_CLI_LOCAL = 1 << 4,
|
||||
DILLO_CLI_GEOMETRY = 1 << 5,
|
||||
DILLO_CLI_ERROR = 1 << 15
|
||||
} OptID;
|
||||
|
||||
typedef struct {
|
||||
const char *shortopt;
|
||||
const char *longopt;
|
||||
int opt_argc; /* positive: mandatory, negative: optional */
|
||||
OptID id;
|
||||
const char *help;
|
||||
} CLI_options;
|
||||
|
||||
static const CLI_options Options[] = {
|
||||
{"-f", "--fullwindow", 0, DILLO_CLI_FULLWINDOW,
|
||||
" -f, --fullwindow Start in full window mode: hide address bar,\n"
|
||||
" navigation buttons, menu, and status bar."},
|
||||
{"-g", "-geometry", 1, DILLO_CLI_GEOMETRY,
|
||||
" -g, -geometry GEO Set initial window position where GEO is\n"
|
||||
" WxH[{+-}X{+-}Y]"},
|
||||
{"-h", "--help", 0, DILLO_CLI_HELP,
|
||||
" -h, --help Display this help text and exit."},
|
||||
{"-l", "--local", 0, DILLO_CLI_LOCAL,
|
||||
" -l, --local Don't load images or stylesheets, or follow\n"
|
||||
" redirections, for these FILEs or URLs."},
|
||||
{"-v", "--version", 0, DILLO_CLI_VERSION,
|
||||
" -v, --version Display version info and exit."},
|
||||
{"-x", "--xid", 1, DILLO_CLI_XID,
|
||||
" -x, --xid XID Open first Dillo window in an existing\n"
|
||||
" window whose window ID is XID."},
|
||||
{NULL, NULL, 0, DILLO_CLI_NONE, NULL}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* SIGCHLD handling ----------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Avoid our children (Dpid) to become zombies. :-)
|
||||
* Notes:
|
||||
* . We let sigaction block SIGCHLD while in the handler.
|
||||
* . Portability is not simple. e.g.
|
||||
* http://www.faqs.org/faqs/unix-faq/faq/part3/section-13.html
|
||||
* man sigaction, waitpid
|
||||
*/
|
||||
static void raw_sigchld2(int signum)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
|
||||
(void) signum; /* Unused */
|
||||
|
||||
while (1) {
|
||||
pid = waitpid(-1, &status, WNOHANG);
|
||||
if (pid > 0) {
|
||||
if (WIFEXITED(status)) /* normal exit */
|
||||
printf("[dpid]: terminated normally (%d)\n", WEXITSTATUS(status));
|
||||
else if (WIFSIGNALED(status)) /* terminated by signal */
|
||||
printf("[dpid]: terminated by signal %d\n", WTERMSIG(status));
|
||||
} else if (pid == 0 || errno == ECHILD) {
|
||||
break;
|
||||
} else {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
perror("waitpid");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void handler_usr1(int signum)
|
||||
{
|
||||
sig_reload = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish SIGCHLD handler
|
||||
*/
|
||||
static void est_sigchld(void)
|
||||
{
|
||||
struct sigaction sigact;
|
||||
sigset_t set;
|
||||
|
||||
(void) sigemptyset(&set);
|
||||
sigact.sa_handler = raw_sigchld2; /* our custom handler */
|
||||
sigact.sa_mask = set; /* no additional signal blocks */
|
||||
sigact.sa_flags = SA_NOCLDSTOP; /* ignore stop/resume states */
|
||||
if (sigaction(SIGCHLD, &sigact, NULL) == -1) {
|
||||
perror("sigaction");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (signal(SIGUSR1, handler_usr1) == SIG_ERR) {
|
||||
perror("signal failed");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Print help text generated from the options structure
|
||||
*/
|
||||
static void printHelp(const char *cmdname, const CLI_options *options)
|
||||
{
|
||||
printf("Usage: %s [OPTION]... [--] [URL|FILE]...\n"
|
||||
"Options:\n", cmdname);
|
||||
while (options && options->help) {
|
||||
printf("%s\n", options->help);
|
||||
options++;
|
||||
}
|
||||
printf(" URL URL to browse.\n"
|
||||
" FILE Local FILE to view.\n"
|
||||
"\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum number of option arguments
|
||||
*/
|
||||
static int numOptions(const CLI_options *options)
|
||||
{
|
||||
int i, max;
|
||||
|
||||
for (i = 0, max = 0; options[i].shortopt; i++)
|
||||
if (abs(options[i].opt_argc) > max)
|
||||
max = abs(options[i].opt_argc);
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next command line option.
|
||||
*/
|
||||
static OptID getCmdOption(const CLI_options *options, int argc, char **argv,
|
||||
char **opt_argv, int *idx)
|
||||
{
|
||||
typedef enum { O_SEARCH, O_FOUND, O_NOTFOUND, O_DONE } State;
|
||||
OptID opt_id = DILLO_CLI_NONE;
|
||||
int i = 0;
|
||||
State state = O_SEARCH;
|
||||
|
||||
if (*idx >= argc) {
|
||||
state = O_DONE;
|
||||
} else {
|
||||
state = O_NOTFOUND;
|
||||
for (i = 0; options[i].shortopt; i++) {
|
||||
if (strcmp(options[i].shortopt, argv[*idx]) == 0 ||
|
||||
strcmp(options[i].longopt, argv[*idx]) == 0) {
|
||||
state = O_FOUND;
|
||||
++*idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state == O_FOUND) {
|
||||
int n_arg = options[i].opt_argc;
|
||||
opt_id = options[i].id;
|
||||
/* Find the required/optional arguments of the option */
|
||||
for (i = 0; *idx < argc && i < abs(n_arg) && argv[*idx][0] != '-'; i++)
|
||||
opt_argv[i] = argv[(*idx)++];
|
||||
opt_argv[i] = NULL;
|
||||
|
||||
/* Optional arguments have opt_argc < 0 */
|
||||
if (i < n_arg) {
|
||||
fprintf(stderr, "Option %s requires %d argument%s\n",
|
||||
argv[*idx-i-1], n_arg, (n_arg == 1) ? "" : "s");
|
||||
opt_id = DILLO_CLI_ERROR;
|
||||
}
|
||||
}
|
||||
if (state == O_NOTFOUND) {
|
||||
if (strcmp(argv[*idx], "--") == 0)
|
||||
(*idx)++;
|
||||
else if (argv[*idx][0] == '-') {
|
||||
fprintf(stderr, "Command line option \"%s\" not recognized.\n",
|
||||
argv[*idx]);
|
||||
opt_id = DILLO_CLI_ERROR;
|
||||
}
|
||||
}
|
||||
return opt_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set FL_NORMAL_LABEL to interpret neither symbols (@) nor shortcuts (&),
|
||||
* and FL_FREE_LABELTYPE to interpret shortcuts.
|
||||
*/
|
||||
static void custLabelDraw(const Fl_Label* o, int X, int Y, int W, int H,
|
||||
Fl_Align align)
|
||||
{
|
||||
const int interpret_symbols = 0;
|
||||
|
||||
fl_draw_shortcut = 0;
|
||||
fl_font(o->font, o->size);
|
||||
fl_color((Fl_Color)o->color);
|
||||
fl_draw(o->value, X, Y, W, H, align, o->image, interpret_symbols);
|
||||
}
|
||||
|
||||
static void custLabelMeasure(const Fl_Label* o, int& W, int& H)
|
||||
{
|
||||
const int interpret_symbols = 0;
|
||||
|
||||
fl_draw_shortcut = 0;
|
||||
fl_font(o->font, o->size);
|
||||
fl_measure(o->value, W, H, interpret_symbols);
|
||||
}
|
||||
|
||||
static void custMenuLabelDraw(const Fl_Label* o, int X, int Y, int W, int H,
|
||||
Fl_Align align)
|
||||
{
|
||||
const int interpret_symbols = 0;
|
||||
|
||||
fl_draw_shortcut = 1;
|
||||
fl_font(o->font, o->size);
|
||||
fl_color((Fl_Color)o->color);
|
||||
fl_draw(o->value, X, Y, W, H, align, o->image, interpret_symbols);
|
||||
}
|
||||
|
||||
static void custMenuLabelMeasure(const Fl_Label* o, int& W, int& H)
|
||||
{
|
||||
const int interpret_symbols = 0;
|
||||
|
||||
fl_draw_shortcut = 1;
|
||||
fl_font(o->font, o->size);
|
||||
fl_measure(o->value, W, H, interpret_symbols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the user if default/pref fonts can't be found.
|
||||
*/
|
||||
static void checkFont(const char *name, const char *type)
|
||||
{
|
||||
if (! dw::fltk::FltkFont::fontExists(name))
|
||||
MSG_WARN("preferred %s font \"%s\" not found.\n", type, name);
|
||||
}
|
||||
|
||||
static void checkPreferredFonts()
|
||||
{
|
||||
checkFont(prefs.font_sans_serif, "sans-serif");
|
||||
checkFont(prefs.font_serif, "serif");
|
||||
checkFont(prefs.font_monospace, "monospace");
|
||||
checkFont(prefs.font_cursive, "cursive");
|
||||
checkFont(prefs.font_fantasy, "fantasy");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set UI color. 'color' is an 0xrrggbb value, whereas 'default_val' is a fltk
|
||||
* color (index 0-0xFF, or 0xrrggbb00).
|
||||
*/
|
||||
static void setUIColorWdef(Fl_Color idx, int32_t color, Fl_Color default_val)
|
||||
{
|
||||
if (color != -1)
|
||||
Fl::set_color(idx, color << 8);
|
||||
else if (default_val != 0xFFFFFFFF)
|
||||
Fl::set_color(idx, default_val);
|
||||
}
|
||||
|
||||
static void setColors()
|
||||
{
|
||||
/* The main background is a special case because Fl::background() will
|
||||
* set the "gray ramp", which is a set of lighter and darker colors based
|
||||
* on the main background and used for box edges and such.
|
||||
*/
|
||||
if (prefs.ui_main_bg_color != -1) {
|
||||
uchar r = prefs.ui_main_bg_color >> 16,
|
||||
g = prefs.ui_main_bg_color >> 8 & 0xff,
|
||||
b = prefs.ui_main_bg_color & 0xff;
|
||||
|
||||
Fl::background(r, g, b);
|
||||
}
|
||||
|
||||
setUIColorWdef(FL_BACKGROUND2_COLOR, prefs.ui_text_bg_color, 0xFFFFFFFF);
|
||||
setUIColorWdef(FL_FOREGROUND_COLOR, prefs.ui_fg_color, 0xFFFFFFFF);
|
||||
setUIColorWdef(FL_SELECTION_COLOR, prefs.ui_selection_color,
|
||||
fl_contrast(FL_SELECTION_COLOR, FL_BACKGROUND2_COLOR));
|
||||
setUIColorWdef(PREFS_UI_BUTTON_HIGHLIGHT_COLOR,
|
||||
prefs.ui_button_highlight_color,
|
||||
fl_lighter(FL_BACKGROUND_COLOR));
|
||||
setUIColorWdef(PREFS_UI_TAB_ACTIVE_BG_COLOR, prefs.ui_tab_active_bg_color,
|
||||
Fl::get_color(FL_BACKGROUND2_COLOR));
|
||||
setUIColorWdef(PREFS_UI_TAB_BG_COLOR, prefs.ui_tab_bg_color,
|
||||
Fl::get_color(FL_BACKGROUND_COLOR));
|
||||
setUIColorWdef(PREFS_UI_TAB_ACTIVE_FG_COLOR, prefs.ui_tab_active_fg_color,
|
||||
Fl::get_color(FL_FOREGROUND_COLOR));
|
||||
setUIColorWdef(PREFS_UI_TAB_FG_COLOR, prefs.ui_tab_fg_color,
|
||||
Fl::get_color(FL_FOREGROUND_COLOR));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a command line argument, build a DilloUrl for it.
|
||||
*/
|
||||
static DilloUrl *makeStartUrl(char *str, bool local)
|
||||
{
|
||||
char *url_str, *p;
|
||||
DilloUrl *start_url;
|
||||
|
||||
/* Relative path to a local file? */
|
||||
p = (*str == '/') ? dStrdup(str) :
|
||||
dStrconcat(Paths::getOldWorkingDir(), "/", str, NULL);
|
||||
|
||||
if (access(p, F_OK) == 0) {
|
||||
/* absolute path may have non-URL characters */
|
||||
url_str = a_Misc_escape_chars(p, "% #");
|
||||
start_url = a_Url_new(url_str + 1, "file:/");
|
||||
} else {
|
||||
/* Not a file, filter URL string */
|
||||
url_str = a_Url_string_strip_delimiters(str);
|
||||
start_url = a_Url_new(url_str, NULL);
|
||||
}
|
||||
dFree(p);
|
||||
dFree(url_str);
|
||||
|
||||
if (local)
|
||||
a_Url_set_flags(start_url, URL_FLAGS(start_url) | URL_SpamSafe);
|
||||
|
||||
return start_url;
|
||||
}
|
||||
|
||||
/*
|
||||
* MAIN
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
uint_t opt_id;
|
||||
uint_t options_got = 0;
|
||||
uint32_t xid = 0;
|
||||
int idx = 1;
|
||||
int xpos = PREFS_GEOMETRY_DEFAULT_XPOS, ypos = PREFS_GEOMETRY_DEFAULT_YPOS,
|
||||
width = PREFS_GEOMETRY_DEFAULT_WIDTH,
|
||||
height = PREFS_GEOMETRY_DEFAULT_HEIGHT;
|
||||
char **opt_argv;
|
||||
FILE *fp;
|
||||
|
||||
srand((uint_t)(time(0) ^ getpid()));
|
||||
|
||||
// Some OSes exit dillo without this (not GNU/Linux).
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
// Establish our custom SIGCHLD handler
|
||||
est_sigchld();
|
||||
|
||||
/* Handle command line options */
|
||||
opt_argv = dNew0(char*, numOptions(Options) + 1);
|
||||
while ((opt_id = getCmdOption(Options, argc, argv, opt_argv, &idx))) {
|
||||
options_got |= opt_id;
|
||||
switch (opt_id) {
|
||||
case DILLO_CLI_FULLWINDOW:
|
||||
case DILLO_CLI_LOCAL:
|
||||
break;
|
||||
case DILLO_CLI_XID:
|
||||
{
|
||||
char *end;
|
||||
xid = strtol(opt_argv[0], &end, 0);
|
||||
if (*end) {
|
||||
fprintf(stderr, "XID argument \"%s\" not valid.\n",opt_argv[0]);
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DILLO_CLI_GEOMETRY:
|
||||
if (!a_Misc_parse_geometry(opt_argv[0],&xpos,&ypos,&width,&height)){
|
||||
fprintf(stderr, "geometry argument \"%s\" not valid. Must be of "
|
||||
"the form WxH[{+-}X{+-}Y].\n", opt_argv[0]);
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
case DILLO_CLI_VERSION:
|
||||
a_Version_print_info();
|
||||
return 0;
|
||||
case DILLO_CLI_HELP:
|
||||
printHelp(argv[0], Options);
|
||||
return 0;
|
||||
default:
|
||||
printHelp(argv[0], Options);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
dFree(opt_argv);
|
||||
|
||||
// set the default values for the preferences
|
||||
a_Prefs_init();
|
||||
|
||||
// create ~/.dillo if not present
|
||||
Paths::init();
|
||||
|
||||
// initialize default key bindings
|
||||
Keys::init();
|
||||
|
||||
// parse dillorc
|
||||
if ((fp = Paths::getPrefsFP(PATHS_RC_PREFS))) {
|
||||
PrefsParser::parse(fp);
|
||||
}
|
||||
// parse keysrc
|
||||
if ((fp = Paths::getPrefsFP(PATHS_RC_KEYS))) {
|
||||
Keys::parse(fp);
|
||||
}
|
||||
// parse domainrc
|
||||
if ((fp = Paths::getPrefsFP(PATHS_RC_DOMAIN))) {
|
||||
a_Domain_parse(fp);
|
||||
fclose(fp);
|
||||
}
|
||||
dLib_show_messages(prefs.show_msg);
|
||||
|
||||
// initialize internal modules
|
||||
a_Dpi_init();
|
||||
a_Dns_init();
|
||||
a_Web_init();
|
||||
a_Http_init();
|
||||
a_Tls_init();
|
||||
a_Mime_init();
|
||||
a_Capi_init();
|
||||
a_Dicache_init();
|
||||
a_Bw_init();
|
||||
a_Cookies_init();
|
||||
a_Actions_init();
|
||||
a_Hsts_init(Paths::getPrefsFP(PATHS_HSTS_PRELOAD));
|
||||
a_Auth_init();
|
||||
a_UIcmd_init();
|
||||
StyleEngine::init();
|
||||
|
||||
dw::core::Widget::setAdjustMinWidth (prefs.adjust_min_width);
|
||||
dw::Table::setAdjustTableMinWidth (prefs.adjust_table_min_width);
|
||||
dw::Textblock::setPenaltyHyphen (prefs.penalty_hyphen);
|
||||
dw::Textblock::setPenaltyHyphen2 (prefs.penalty_hyphen_2);
|
||||
dw::Textblock::setPenaltyEmDashLeft (prefs.penalty_em_dash_left);
|
||||
dw::Textblock::setPenaltyEmDashRight (prefs.penalty_em_dash_right);
|
||||
dw::Textblock::setPenaltyEmDashRight2 (prefs.penalty_em_dash_right_2);
|
||||
dw::Textblock::setStretchabilityFactor (prefs.stretchability_factor);
|
||||
|
||||
/* command line options override preferences */
|
||||
if (options_got & DILLO_CLI_FULLWINDOW)
|
||||
prefs.fullwindow_start = TRUE;
|
||||
if (options_got & DILLO_CLI_GEOMETRY) {
|
||||
prefs.width = width;
|
||||
prefs.height = height;
|
||||
prefs.xpos = xpos;
|
||||
prefs.ypos = ypos;
|
||||
}
|
||||
|
||||
// Sets WM_CLASS hint on X11
|
||||
Fl_Window::default_xclass("dillo");
|
||||
|
||||
Fl::scheme(prefs.theme);
|
||||
|
||||
// Disable drag and drop as it crashes on MacOSX
|
||||
Fl::dnd_text_ops(0);
|
||||
|
||||
setColors();
|
||||
|
||||
if (!prefs.show_ui_tooltip) {
|
||||
Fl::option(Fl::OPTION_SHOW_TOOLTIPS, false);
|
||||
}
|
||||
|
||||
// Disable '@' and '&' interpretation in normal labels.
|
||||
Fl::set_labeltype(FL_NORMAL_LABEL, custLabelDraw, custLabelMeasure);
|
||||
|
||||
// Use to permit '&' interpretation.
|
||||
Fl::set_labeltype(FL_FREE_LABELTYPE,custMenuLabelDraw,custMenuLabelMeasure);
|
||||
|
||||
checkPreferredFonts();
|
||||
|
||||
/* use preferred font for UI */
|
||||
Fl_Font defaultFont = dw::fltk::FltkFont::get (prefs.font_sans_serif, 0);
|
||||
Fl::set_font(FL_HELVETICA, defaultFont); // this seems to be the
|
||||
// only way to set the
|
||||
// default font in fltk1.3
|
||||
|
||||
fl_message_title_default("Dillo: Message");
|
||||
|
||||
// Create a new UI/bw pair
|
||||
BrowserWindow *bw = a_UIcmd_browser_window_new(0, 0, xid, NULL);
|
||||
|
||||
/* We need this so that fl_text_extents() in dw/fltkplatform.cc can
|
||||
* work when FLTK is configured without XFT and Dillo is opening
|
||||
* immediately-available URLs from the cmdline (e.g. about:splash).
|
||||
*/
|
||||
((Fl_Widget *)bw->ui)->window()->make_current();
|
||||
|
||||
/* Proxy authentication */
|
||||
if (prefs.http_proxyuser && !a_Http_proxy_auth()) {
|
||||
const char *passwd = a_UIcmd_get_passwd(prefs.http_proxyuser);
|
||||
if (passwd) {
|
||||
a_Http_set_proxy_passwd(passwd);
|
||||
} else {
|
||||
MSG_WARN("Not using proxy authentication.\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Open URLs/files */
|
||||
const bool local = options_got & DILLO_CLI_LOCAL;
|
||||
|
||||
if (idx == argc) {
|
||||
/* No URLs/files on cmdline. Send startup screen */
|
||||
if (dStrAsciiCasecmp(URL_SCHEME(prefs.start_page), "about") == 0 &&
|
||||
strcmp(URL_PATH(prefs.start_page), "blank") == 0)
|
||||
a_UIcmd_open_url(bw, NULL); // NULL URL focuses location
|
||||
else {
|
||||
a_UIcmd_open_url(bw, prefs.start_page);
|
||||
a_UIcmd_set_location_text(bw, URL_STR(prefs.start_page));
|
||||
}
|
||||
} else {
|
||||
for (int i = idx; i < argc; i++) {
|
||||
DilloUrl *start_url = makeStartUrl(argv[i], local);
|
||||
|
||||
if (i > idx) {
|
||||
if (prefs.middle_click_opens_new_tab) {
|
||||
/* user must prefer tabs */
|
||||
const int focus = 1;
|
||||
a_UIcmd_open_url_nt(bw, start_url, focus);
|
||||
} else {
|
||||
a_UIcmd_open_url_nw(bw, start_url);
|
||||
}
|
||||
} else {
|
||||
a_UIcmd_open_url(bw, start_url);
|
||||
a_UIcmd_set_location_text(bw, URL_STR(start_url));
|
||||
}
|
||||
a_Url_free(start_url);
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't use, as it can be free()'d */
|
||||
bw = NULL;
|
||||
|
||||
while (Fl::wait() > 0) {
|
||||
if (sig_reload) {
|
||||
sig_reload = 0;
|
||||
a_UIcmd_reload_all_active();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Memory deallocating routines
|
||||
* (This can be left to the OS, but we'll do it, with a view to test
|
||||
* and fix our memory management)
|
||||
*/
|
||||
a_Domain_freeall();
|
||||
a_Cookies_freeall();
|
||||
a_Hsts_freeall();
|
||||
a_Cache_freeall();
|
||||
a_Dicache_freeall();
|
||||
a_Http_freeall();
|
||||
a_Tls_freeall();
|
||||
a_Dns_freeall();
|
||||
a_History_freeall();
|
||||
a_Prefs_freeall();
|
||||
Keys::free();
|
||||
Paths::free();
|
||||
dw::core::freeall();
|
||||
dw::fltk::freeall();
|
||||
/* TODO: auth, css */
|
||||
|
||||
//a_Dpi_dillo_exit();
|
||||
MSG("Dillo: normal exit!\n");
|
||||
return 0;
|
||||
}
|
||||
20
src/djpeg.h
Normal file
20
src/djpeg.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef __JPEG_H__
|
||||
#define __JPEG_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#include "url.h"
|
||||
#include "image.hh"
|
||||
|
||||
|
||||
void *a_Jpeg_new(DilloImage *Image, DilloUrl *url, int version);
|
||||
void a_Jpeg_callback(int Op, void *data);
|
||||
const char *a_Jpeg_version(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* !__JPEG_H__ */
|
||||
514
src/dns.c
Normal file
514
src/dns.c
Normal file
@ -0,0 +1,514 @@
|
||||
/*
|
||||
* File: dns.c
|
||||
*
|
||||
* Copyright (C) 1999-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.
|
||||
*/
|
||||
|
||||
/* @file
|
||||
* Non blocking pthread-handled Dns scheme
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Uncomment the following line for debugging or gprof profiling.
|
||||
*/
|
||||
/* #undef D_DNS_THREADED */
|
||||
|
||||
#ifdef D_DNS_THREADED
|
||||
# include <pthread.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "msg.h"
|
||||
#include "dns.h"
|
||||
#include "list.h"
|
||||
#include "IO/iowatch.hh"
|
||||
|
||||
|
||||
/* Maximum dns resolving threads */
|
||||
#ifdef D_DNS_THREADED
|
||||
# define D_DNS_MAX_SERVERS 4
|
||||
#else
|
||||
# define D_DNS_MAX_SERVERS 1
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
DNS_SERVER_IDLE,
|
||||
DNS_SERVER_PROCESSING,
|
||||
DNS_SERVER_RESOLVED,
|
||||
} DnsServerState_t;
|
||||
|
||||
typedef struct {
|
||||
int channel; /**< Index of this channel [0 based] */
|
||||
DnsServerState_t state;
|
||||
Dlist *addr_list; /**< IP address */
|
||||
char *hostname; /**< Address to resolve */
|
||||
int status; /**< errno code for resolving function */
|
||||
#ifdef D_DNS_THREADED
|
||||
pthread_t th1; /**< Thread id */
|
||||
#endif
|
||||
} DnsServer;
|
||||
|
||||
typedef struct {
|
||||
char *hostname; /**< host name for cache */
|
||||
Dlist *addr_list; /**< addresses of host */
|
||||
} GDnsCache;
|
||||
|
||||
typedef struct {
|
||||
int channel; /**< -2 if waiting, otherwise index to dns_server[] */
|
||||
char *hostname; /**< The one we're resolving */
|
||||
DnsCallback_t cb_func; /**< callback function */
|
||||
void *cb_data; /**< extra data for the callback function */
|
||||
} GDnsQueue;
|
||||
|
||||
|
||||
/*
|
||||
* Forward declarations
|
||||
*/
|
||||
static void Dns_timeout_client(int fd, void *data);
|
||||
|
||||
/*
|
||||
* Local Data
|
||||
*/
|
||||
static DnsServer dns_server[D_DNS_MAX_SERVERS];
|
||||
static int num_servers;
|
||||
static GDnsCache *dns_cache;
|
||||
static int dns_cache_size, dns_cache_size_max;
|
||||
static GDnsQueue *dns_queue;
|
||||
static int dns_queue_size, dns_queue_size_max;
|
||||
static int dns_notify_pipe[2];
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Dns queue functions
|
||||
*/
|
||||
static void Dns_queue_add(int channel, const char *hostname,
|
||||
DnsCallback_t cb_func, void *cb_data)
|
||||
{
|
||||
a_List_add(dns_queue, dns_queue_size, dns_queue_size_max);
|
||||
dns_queue[dns_queue_size].channel = channel;
|
||||
dns_queue[dns_queue_size].hostname = dStrdup(hostname);
|
||||
dns_queue[dns_queue_size].cb_func = cb_func;
|
||||
dns_queue[dns_queue_size].cb_data = cb_data;
|
||||
dns_queue_size++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find hostname index in dns_queue
|
||||
* (if found, returns queue index; -1 if not)
|
||||
*/
|
||||
static int Dns_queue_find(const char *hostname)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dns_queue_size; i++)
|
||||
if (!dStrAsciiCasecmp(hostname, dns_queue[i].hostname))
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an index, remove an entry from the dns_queue
|
||||
*/
|
||||
static void Dns_queue_remove(int index)
|
||||
{
|
||||
int i;
|
||||
|
||||
_MSG("Dns_queue_remove: deleting client [%d] [queue_size=%d]\n",
|
||||
index, dns_queue_size);
|
||||
|
||||
if (index < dns_queue_size) {
|
||||
dFree(dns_queue[index].hostname);
|
||||
--dns_queue_size; /* you'll find out why ;-) */
|
||||
for (i = index; i < dns_queue_size; i++)
|
||||
dns_queue[i] = dns_queue[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Debug function
|
||||
*
|
||||
void Dns_queue_print()
|
||||
{
|
||||
int i;
|
||||
|
||||
MSG("Queue: [");
|
||||
for (i = 0; i < dns_queue_size; i++)
|
||||
MSG("%d:%s ", dns_queue[i].channel, dns_queue[i].hostname);
|
||||
MSG("]\n");
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add an IP/hostname pair to Dns-cache
|
||||
*/
|
||||
static void Dns_cache_add(char *hostname, Dlist *addr_list)
|
||||
{
|
||||
a_List_add(dns_cache, dns_cache_size, dns_cache_size_max);
|
||||
dns_cache[dns_cache_size].hostname = dStrdup(hostname);
|
||||
dns_cache[dns_cache_size].addr_list = addr_list;
|
||||
++dns_cache_size;
|
||||
_MSG("Cache objects: %d\n", dns_cache_size);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializer function
|
||||
*/
|
||||
void a_Dns_init(void)
|
||||
{
|
||||
int res, i;
|
||||
|
||||
#ifdef D_DNS_THREADED
|
||||
MSG("dillo_dns_init: Here we go! (threaded)\n");
|
||||
#else
|
||||
MSG("dillo_dns_init: Here we go! (not threaded)\n");
|
||||
#endif
|
||||
|
||||
dns_queue_size = 0;
|
||||
dns_queue_size_max = 16;
|
||||
dns_queue = dNew(GDnsQueue, dns_queue_size_max);
|
||||
|
||||
dns_cache_size = 0;
|
||||
dns_cache_size_max = 16;
|
||||
dns_cache = dNew(GDnsCache, dns_cache_size_max);
|
||||
|
||||
num_servers = D_DNS_MAX_SERVERS;
|
||||
|
||||
res = pipe(dns_notify_pipe);
|
||||
assert(res == 0);
|
||||
fcntl(dns_notify_pipe[0], F_SETFL, O_NONBLOCK);
|
||||
a_IOwatch_add_fd(dns_notify_pipe[0], DIO_READ, Dns_timeout_client, NULL);
|
||||
|
||||
/* Initialize servers data */
|
||||
for (i = 0; i < num_servers; ++i) {
|
||||
dns_server[i].channel = i;
|
||||
dns_server[i].state = DNS_SERVER_IDLE;
|
||||
dns_server[i].addr_list = NULL;
|
||||
dns_server[i].hostname = NULL;
|
||||
dns_server[i].status = 0;
|
||||
#ifdef D_DNS_THREADED
|
||||
dns_server[i].th1 = (pthread_t) -1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a host structure and add it to the list
|
||||
*/
|
||||
|
||||
static void Dns_note_hosts(Dlist *list, struct addrinfo *res0)
|
||||
{
|
||||
struct addrinfo *res;
|
||||
DilloHost *dh;
|
||||
|
||||
for (res = res0; res; res = res->ai_next) {
|
||||
|
||||
if (res->ai_family == AF_INET) {
|
||||
struct sockaddr_in *in_addr;
|
||||
|
||||
if (res->ai_addrlen < sizeof(struct sockaddr_in)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dh = dNew0(DilloHost, 1);
|
||||
dh->af = AF_INET;
|
||||
|
||||
in_addr = (struct sockaddr_in*) res->ai_addr;
|
||||
dh->alen = sizeof (struct in_addr);
|
||||
memcpy(&dh->data[0], &in_addr->sin_addr.s_addr, dh->alen);
|
||||
|
||||
dList_append(list, dh);
|
||||
#ifdef ENABLE_IPV6
|
||||
} else if (res->ai_family == AF_INET6) {
|
||||
struct sockaddr_in6 *in6_addr;
|
||||
|
||||
if (res->ai_addrlen < sizeof(struct sockaddr_in6)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dh = dNew0(DilloHost, 1);
|
||||
dh->af = AF_INET6;
|
||||
|
||||
in6_addr = (struct sockaddr_in6*) res->ai_addr;
|
||||
dh->alen = sizeof (struct in6_addr);
|
||||
memcpy(&dh->data[0], &in6_addr->sin6_addr.s6_addr, dh->alen);
|
||||
|
||||
dList_append(list, dh);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Server function (runs on its own thread)
|
||||
*/
|
||||
static void *Dns_server(void *data)
|
||||
{
|
||||
int channel = VOIDP2INT(data);
|
||||
struct addrinfo hints, *res0;
|
||||
int error;
|
||||
Dlist *hosts;
|
||||
size_t length, i;
|
||||
char addr_string[40];
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
#ifdef ENABLE_IPV6
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
#else
|
||||
hints.ai_family = AF_INET;
|
||||
#endif
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
hosts = dList_new(2);
|
||||
|
||||
_MSG("Dns_server: starting...\n ch: %d host: %s\n",
|
||||
channel, dns_server[channel].hostname);
|
||||
|
||||
error = getaddrinfo(dns_server[channel].hostname, NULL, &hints, &res0);
|
||||
|
||||
if (error != 0) {
|
||||
dns_server[channel].status = error;
|
||||
MSG("DNS error: %s\n", gai_strerror(error));
|
||||
} else {
|
||||
Dns_note_hosts(hosts, res0);
|
||||
dns_server[channel].status = 0;
|
||||
freeaddrinfo(res0);
|
||||
}
|
||||
|
||||
if (dList_length(hosts) > 0) {
|
||||
dns_server[channel].status = 0;
|
||||
} else {
|
||||
dList_free(hosts);
|
||||
hosts = NULL;
|
||||
}
|
||||
|
||||
/* tell our findings */
|
||||
MSG("Dns_server [%d]: %s is", channel,
|
||||
dns_server[channel].hostname);
|
||||
if ((length = dList_length(hosts))) {
|
||||
for (i = 0; i < length; i++) {
|
||||
a_Dns_dillohost_to_string(dList_nth_data(hosts, i),
|
||||
addr_string, sizeof(addr_string));
|
||||
MSG(" %s", addr_string);
|
||||
}
|
||||
MSG("\n");
|
||||
} else {
|
||||
MSG(" (nil)\n");
|
||||
}
|
||||
dns_server[channel].addr_list = hosts;
|
||||
dns_server[channel].state = DNS_SERVER_RESOLVED;
|
||||
|
||||
write(dns_notify_pipe[1], ".", 1);
|
||||
|
||||
return NULL; /* (avoids a compiler warning) */
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request function (spawn a server and let it handle the request)
|
||||
*/
|
||||
static void Dns_server_req(int channel, const char *hostname)
|
||||
{
|
||||
#ifdef D_DNS_THREADED
|
||||
static pthread_attr_t thrATTR;
|
||||
static int thrATTRInitialized = 0;
|
||||
#endif
|
||||
|
||||
dns_server[channel].state = DNS_SERVER_PROCESSING;
|
||||
|
||||
dFree(dns_server[channel].hostname);
|
||||
dns_server[channel].hostname = dStrdup(hostname);
|
||||
|
||||
#ifdef D_DNS_THREADED
|
||||
/* set the thread attribute to the detached state */
|
||||
if (!thrATTRInitialized) {
|
||||
pthread_attr_init(&thrATTR);
|
||||
pthread_attr_setdetachstate(&thrATTR, PTHREAD_CREATE_DETACHED);
|
||||
thrATTRInitialized = 1;
|
||||
}
|
||||
/* Spawn thread */
|
||||
pthread_create(&dns_server[channel].th1, &thrATTR, Dns_server,
|
||||
INT2VOIDP(dns_server[channel].channel));
|
||||
#else
|
||||
Dns_server(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the IP for the given hostname using a callback.
|
||||
* Side effect: a thread is spawned when hostname is not cached.
|
||||
*/
|
||||
void a_Dns_resolve(const char *hostname, DnsCallback_t cb_func, void *cb_data)
|
||||
{
|
||||
int i, channel;
|
||||
|
||||
if (!hostname)
|
||||
return;
|
||||
|
||||
/* check for cache hit. */
|
||||
for (i = 0; i < dns_cache_size; i++)
|
||||
if (!dStrAsciiCasecmp(hostname, dns_cache[i].hostname))
|
||||
break;
|
||||
|
||||
if (i < dns_cache_size) {
|
||||
/* already resolved, call the Callback immediately. */
|
||||
cb_func(0, dns_cache[i].addr_list, cb_data);
|
||||
|
||||
} else if ((i = Dns_queue_find(hostname)) != -1) {
|
||||
/* hit in queue, but answer hasn't come back yet. */
|
||||
Dns_queue_add(dns_queue[i].channel, hostname, cb_func, cb_data);
|
||||
|
||||
} else {
|
||||
/* Never requested before -- we must resolve it! */
|
||||
|
||||
/* Find a channel we can send the request to */
|
||||
for (channel = 0; channel < num_servers; channel++)
|
||||
if (dns_server[channel].state == DNS_SERVER_IDLE)
|
||||
break;
|
||||
if (channel < num_servers) {
|
||||
/* Found a free channel! */
|
||||
Dns_queue_add(channel, hostname, cb_func, cb_data);
|
||||
Dns_server_req(channel, hostname);
|
||||
} else {
|
||||
/* We'll have to wait for a thread to finish... */
|
||||
Dns_queue_add(-2, hostname, cb_func, cb_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Give answer to all queued callbacks on this channel
|
||||
*/
|
||||
static void Dns_serve_channel(int channel)
|
||||
{
|
||||
int i;
|
||||
DnsServer *srv = &dns_server[channel];
|
||||
|
||||
for (i = 0; i < dns_queue_size; i++) {
|
||||
if (dns_queue[i].channel == channel) {
|
||||
dns_queue[i].cb_func(srv->status, srv->addr_list,
|
||||
dns_queue[i].cb_data);
|
||||
Dns_queue_remove(i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign free channels to waiting clients (-2)
|
||||
*/
|
||||
static void Dns_assign_channels(void)
|
||||
{
|
||||
int ch, i, j;
|
||||
|
||||
for (ch = 0; ch < num_servers; ++ch) {
|
||||
if (dns_server[ch].state == DNS_SERVER_IDLE) {
|
||||
/* Find the next query in the queue (we're a FIFO) */
|
||||
for (i = 0; i < dns_queue_size; i++)
|
||||
if (dns_queue[i].channel == -2)
|
||||
break;
|
||||
|
||||
if (i < dns_queue_size) {
|
||||
/* assign this channel to every queued request
|
||||
* with the same hostname*/
|
||||
for (j = i; j < dns_queue_size; j++)
|
||||
if (dns_queue[j].channel == -2 &&
|
||||
!dStrAsciiCasecmp(dns_queue[j].hostname,
|
||||
dns_queue[i].hostname)) {
|
||||
dns_queue[j].channel = ch;
|
||||
}
|
||||
Dns_server_req(ch, dns_queue[i].hostname);
|
||||
} else
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called on the main thread and
|
||||
* reads the DNS results.
|
||||
*/
|
||||
static void Dns_timeout_client(int fd, void *data)
|
||||
{
|
||||
int i;
|
||||
char buf[16];
|
||||
|
||||
while (read(dns_notify_pipe[0], buf, sizeof(buf)) > 0);
|
||||
|
||||
for (i = 0; i < num_servers; ++i) {
|
||||
DnsServer *srv = &dns_server[i];
|
||||
|
||||
if (srv->state == DNS_SERVER_RESOLVED) {
|
||||
if (srv->addr_list != NULL) {
|
||||
/* DNS succeeded, let's cache it */
|
||||
Dns_cache_add(srv->hostname, srv->addr_list);
|
||||
}
|
||||
Dns_serve_channel(i);
|
||||
srv->state = DNS_SERVER_IDLE;
|
||||
}
|
||||
}
|
||||
Dns_assign_channels();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dns memory-deallocation.
|
||||
* (Call this one at exit time)
|
||||
* The Dns_queue is deallocated at execution time (no need to do that here)
|
||||
* 'dns_cache' is the only one that grows dynamically
|
||||
*/
|
||||
void a_Dns_freeall(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for ( i = 0; i < dns_cache_size; ++i ){
|
||||
dFree(dns_cache[i].hostname);
|
||||
for ( j = 0; j < dList_length(dns_cache[i].addr_list); ++j)
|
||||
dFree(dList_nth_data(dns_cache[i].addr_list, j));
|
||||
dList_free(dns_cache[i].addr_list);
|
||||
}
|
||||
a_IOwatch_remove_fd(dns_notify_pipe[0], DIO_READ);
|
||||
dClose(dns_notify_pipe[0]);
|
||||
dClose(dns_notify_pipe[1]);
|
||||
dFree(dns_cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a string representation of the given DilloHost
|
||||
* into dst. dst will be \0 terminated.
|
||||
* Please note that dst must be at least 40 bytes long for IPv6
|
||||
* addresses.
|
||||
*/
|
||||
void a_Dns_dillohost_to_string(DilloHost *host, char *dst, size_t size)
|
||||
{
|
||||
if (!inet_ntop(host->af, host->data, dst, size)) {
|
||||
switch (errno) {
|
||||
case EAFNOSUPPORT:
|
||||
snprintf(dst, size, "Unknown address family");
|
||||
break;
|
||||
case ENOSPC:
|
||||
snprintf(dst, size, "Buffer too small");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
35
src/dns.h
Normal file
35
src/dns.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef __DNS_H__
|
||||
#define __DNS_H__
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
typedef void (*DnsCallback_t)(int status, Dlist *addr_list, void *data);
|
||||
|
||||
void a_Dns_init (void);
|
||||
void a_Dns_freeall(void);
|
||||
void a_Dns_resolve(const char *hostname, DnsCallback_t cb_func, void *cb_data);
|
||||
|
||||
#ifdef ENABLE_IPV6
|
||||
# define DILLO_ADDR_MAX sizeof(struct in6_addr)
|
||||
#else
|
||||
# define DILLO_ADDR_MAX sizeof(struct in_addr)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int af;
|
||||
int alen;
|
||||
char data[DILLO_ADDR_MAX];
|
||||
} DilloHost;
|
||||
|
||||
void a_Dns_dillohost_to_string(DilloHost *host, char *dst, size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __DNS_H__ */
|
||||
99
src/doctree.hh
Normal file
99
src/doctree.hh
Normal file
@ -0,0 +1,99 @@
|
||||
#ifndef __DOCTREE_HH__
|
||||
#define __DOCTREE_HH__
|
||||
|
||||
#include "lout/misc.hh"
|
||||
|
||||
class DoctreeNode {
|
||||
public:
|
||||
DoctreeNode *parent;
|
||||
DoctreeNode *sibling;
|
||||
DoctreeNode *lastChild;
|
||||
int num; // unique ascending id
|
||||
int element;
|
||||
lout::misc::SimpleVector<char*> *klass;
|
||||
const char *pseudo;
|
||||
const char *id;
|
||||
|
||||
DoctreeNode () {
|
||||
parent = NULL;
|
||||
sibling = NULL;
|
||||
lastChild = NULL;
|
||||
klass = NULL;
|
||||
pseudo = NULL;
|
||||
id = NULL;
|
||||
element = 0;
|
||||
};
|
||||
|
||||
~DoctreeNode () {
|
||||
dFree ((void*) id);
|
||||
while (lastChild) {
|
||||
DoctreeNode *n = lastChild;
|
||||
lastChild = lastChild->sibling;
|
||||
delete n;
|
||||
}
|
||||
if (klass) {
|
||||
for (int i = 0; i < klass->size (); i++)
|
||||
dFree (klass->get(i));
|
||||
delete klass;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief HTML document tree interface.
|
||||
*
|
||||
* The Doctree class defines the interface to the parsed HTML document tree
|
||||
* as it is used for CSS selector matching.
|
||||
*/
|
||||
class Doctree {
|
||||
private:
|
||||
DoctreeNode *topNode;
|
||||
DoctreeNode *rootNode;
|
||||
int num;
|
||||
|
||||
public:
|
||||
Doctree () {
|
||||
rootNode = new DoctreeNode;
|
||||
topNode = rootNode;
|
||||
num = 0;
|
||||
};
|
||||
|
||||
~Doctree () {
|
||||
delete rootNode;
|
||||
};
|
||||
|
||||
DoctreeNode *push () {
|
||||
DoctreeNode *dn = new DoctreeNode ();
|
||||
dn->parent = topNode;
|
||||
dn->sibling = dn->parent->lastChild;
|
||||
dn->parent->lastChild = dn;
|
||||
dn->num = num++;
|
||||
topNode = dn;
|
||||
return dn;
|
||||
};
|
||||
|
||||
void pop () {
|
||||
assert (topNode != rootNode); // never pop the root node
|
||||
topNode = topNode->parent;
|
||||
};
|
||||
|
||||
inline DoctreeNode *top () {
|
||||
if (topNode != rootNode)
|
||||
return topNode;
|
||||
else
|
||||
return NULL;
|
||||
};
|
||||
|
||||
inline DoctreeNode *parent (const DoctreeNode *node) {
|
||||
if (node->parent != rootNode)
|
||||
return node->parent;
|
||||
else
|
||||
return NULL;
|
||||
};
|
||||
|
||||
inline DoctreeNode *sibling (const DoctreeNode *node) {
|
||||
return node->sibling;
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
157
src/domain.c
Normal file
157
src/domain.c
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* File: domain.c
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
|
||||
#include "../dlib/dlib.h"
|
||||
#include "msg.h"
|
||||
#include "list.h"
|
||||
#include "domain.h"
|
||||
|
||||
typedef struct {
|
||||
char *origin;
|
||||
char *destination;
|
||||
} Rule;
|
||||
|
||||
static Rule *exceptions = NULL;
|
||||
static int num_exceptions = 0;
|
||||
static int num_exceptions_max = 1;
|
||||
|
||||
static bool_t default_deny = FALSE;
|
||||
|
||||
/**
|
||||
* Parse domainrc.
|
||||
*/
|
||||
void a_Domain_parse(FILE *fp)
|
||||
{
|
||||
char *line;
|
||||
uint_t lineno = 0;
|
||||
|
||||
_MSG("Reading domainrc...\n");
|
||||
|
||||
while ((line = dGetline(fp)) != NULL) {
|
||||
++lineno;
|
||||
|
||||
/* Remove leading and trailing whitespace */
|
||||
dStrstrip(line);
|
||||
|
||||
if (line[0] && line[0] != '#') {
|
||||
const char *delim = " \t";
|
||||
char *tok1 = strtok(line, delim);
|
||||
char *tok2 = strtok(NULL, delim);
|
||||
|
||||
if (strtok(NULL, delim) != NULL) {
|
||||
MSG("Domain: Ignoring extraneous text at end of line %u.\n",
|
||||
lineno);
|
||||
}
|
||||
if (!tok2) {
|
||||
MSG("Domain: Not enough fields in line %u.\n", lineno);
|
||||
} else {
|
||||
if (dStrAsciiCasecmp(tok1, "default") == 0) {
|
||||
if (dStrAsciiCasecmp(tok2, "deny") == 0) {
|
||||
default_deny = TRUE;
|
||||
MSG("Domain: Default deny.\n");
|
||||
} else if (dStrAsciiCasecmp(tok2, "accept") == 0) {
|
||||
default_deny = FALSE;
|
||||
MSG("Domain: Default accept.\n");
|
||||
} else {
|
||||
MSG("Domain: Default action \"%s\" not recognised.\n", tok2);
|
||||
}
|
||||
} else {
|
||||
a_List_add(exceptions, num_exceptions, num_exceptions_max);
|
||||
exceptions[num_exceptions].origin = dStrdup(tok1);
|
||||
exceptions[num_exceptions].destination = dStrdup(tok2);
|
||||
num_exceptions++;
|
||||
_MSG("Domain: Exception from %s to %s.\n", tok1, tok2);
|
||||
}
|
||||
}
|
||||
}
|
||||
dFree(line);
|
||||
}
|
||||
}
|
||||
|
||||
void a_Domain_freeall(void)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < num_exceptions; i++) {
|
||||
dFree(exceptions[i].origin);
|
||||
dFree(exceptions[i].destination);
|
||||
}
|
||||
dFree(exceptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wildcard ('*') pattern always matches.
|
||||
* "example.org" pattern matches "example.org".
|
||||
* ".example.org" pattern matches "example.org" and "sub.example.org".
|
||||
*/
|
||||
static bool_t Domain_match(const char *host, const char *pattern) {
|
||||
int cmp = strcmp(pattern, "*");
|
||||
|
||||
if (cmp) {
|
||||
if (pattern[0] != '.')
|
||||
cmp = dStrAsciiCasecmp(host, pattern);
|
||||
else {
|
||||
int diff = strlen(host) - strlen(pattern);
|
||||
|
||||
if (diff == -1)
|
||||
cmp = dStrAsciiCasecmp(host, pattern + 1);
|
||||
else if (diff >= 0)
|
||||
cmp = dStrAsciiCasecmp(host + diff, pattern);
|
||||
}
|
||||
}
|
||||
return cmp ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the resource at 'source' permitted to request the resource at 'dest'?
|
||||
*/
|
||||
bool_t a_Domain_permit(const DilloUrl *source, const DilloUrl *dest)
|
||||
{
|
||||
int i;
|
||||
bool_t ret;
|
||||
const char *source_host, *dest_host;
|
||||
|
||||
if (default_deny == FALSE && num_exceptions == 0)
|
||||
return TRUE;
|
||||
|
||||
source_host = URL_HOST(source);
|
||||
dest_host = URL_HOST(dest);
|
||||
|
||||
if (dest_host[0] == '\0') {
|
||||
ret = source_host[0] == '\0' ||
|
||||
!dStrAsciiCasecmp(URL_SCHEME(dest), "data");
|
||||
if (ret == FALSE)
|
||||
MSG("Domain: DENIED %s -> %s.\n", source_host, URL_STR(dest));
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (a_Url_same_organization(source, dest))
|
||||
return TRUE;
|
||||
|
||||
ret = default_deny ? FALSE : TRUE;
|
||||
|
||||
for (i = 0; i < num_exceptions; i++) {
|
||||
if (Domain_match(source_host, exceptions[i].origin) &&
|
||||
Domain_match(dest_host, exceptions[i].destination)) {
|
||||
ret = default_deny;
|
||||
_MSG("Domain: Matched rule from %s to %s.\n", exceptions[i].origin,
|
||||
exceptions[i].destination);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == FALSE) {
|
||||
const char *src = source_host[0] ? source_host : URL_STR(source);
|
||||
|
||||
MSG("Domain: DENIED %s -> %s.\n", src, dest_host);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
19
src/domain.h
Normal file
19
src/domain.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef __DOMAIN_H__
|
||||
#define __DOMAIN_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include "url.h"
|
||||
|
||||
void a_Domain_parse(FILE *fp);
|
||||
void a_Domain_freeall(void);
|
||||
bool_t a_Domain_permit(const DilloUrl *source, const DilloUrl *dest);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
39
src/domainrc
Normal file
39
src/domainrc
Normal file
@ -0,0 +1,39 @@
|
||||
# domainrc - Dillo cross-domain request rules file.
|
||||
#
|
||||
# Here you can tell Dillo what to do when one site wants to retrieve resources
|
||||
# (e.g., images, style sheets, redirection) from a different site.
|
||||
#
|
||||
# Lines that begin with a '#' are comments.
|
||||
|
||||
# Default rule can be "accept" or "deny".
|
||||
|
||||
default accept
|
||||
|
||||
|
||||
# Now we list exceptions to the default. The format is:
|
||||
#
|
||||
# source destination
|
||||
#
|
||||
# There are three ways that you can specify a source or destination domain:
|
||||
#
|
||||
# 1. * - wildcard will match any domain
|
||||
# 2. example.com - match the specific host example.com
|
||||
# 3. .example.com - match example.com and any of its subdomains
|
||||
|
||||
# Let's block some of the most notorious ad sites and trackers.
|
||||
|
||||
* .2o7.net
|
||||
* .admt.com
|
||||
* .adnxs.com
|
||||
* .atdmt.com
|
||||
* .collective-media.net
|
||||
* .crwdcntrl.com
|
||||
* .doubleclick.net
|
||||
* .effectivemeasure.net
|
||||
* .googleadservices.com
|
||||
* .imrworldwide.com
|
||||
* .quantserve.com
|
||||
* .revsci.net
|
||||
* .scorecardresearch.com
|
||||
* .webtrendslive.com
|
||||
* .yieldmanager.com
|
||||
81
src/dpiapi.c
Normal file
81
src/dpiapi.c
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* File: dpiapi.c
|
||||
*
|
||||
* Copyright (C) 2004-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.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* Support for dpi/dpip from Dillo's side
|
||||
*/
|
||||
|
||||
#include "msg.h"
|
||||
#include "bw.h"
|
||||
#include "capi.h"
|
||||
#include "dpiapi.h" /* for prototypes */
|
||||
#include "dialog.hh"
|
||||
#include "../dpip/dpip.h"
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Dialog interface
|
||||
//
|
||||
|
||||
/* This variable can be eliminated as a parameter with a cleaner API. */
|
||||
static char *dialog_server = NULL;
|
||||
|
||||
|
||||
/**
|
||||
* Generic callback function for dpip dialogs.
|
||||
*/
|
||||
static void Dpiapi_dialog_answer_cb(BrowserWindow *bw, int answer)
|
||||
{
|
||||
char *cmd, numstr[16];
|
||||
|
||||
/* make dpip tag with the answer */
|
||||
snprintf(numstr, 16, "%d", answer);
|
||||
cmd = a_Dpip_build_cmd("cmd=%s to_cmd=%s msg=%s",
|
||||
"answer", "dialog", numstr);
|
||||
|
||||
/* Send answer */
|
||||
a_Capi_dpi_send_cmd(NULL, bw, cmd, dialog_server, 0);
|
||||
dFree(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a dpip "dialog" command from any dpi.
|
||||
*/
|
||||
void a_Dpiapi_dialog(BrowserWindow *bw, char *server, char *dpip_tag)
|
||||
{
|
||||
char *title, *msg, *alt1, *alt2, *alt3, *alt4, *alt5;
|
||||
size_t dpip_tag_len;
|
||||
int ret;
|
||||
|
||||
_MSG("a_Dpiapi_dialog:\n");
|
||||
_MSG(" dpip_tag: %s\n", dpip_tag);
|
||||
|
||||
/* set the module scoped variable */
|
||||
dialog_server = server;
|
||||
|
||||
/* other options can be parsed the same way */
|
||||
dpip_tag_len = strlen(dpip_tag);
|
||||
title = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "title");
|
||||
msg = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "msg");
|
||||
alt1 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt1");
|
||||
alt2 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt2");
|
||||
alt3 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt3");
|
||||
alt4 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt4");
|
||||
alt5 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt5");
|
||||
|
||||
ret = a_Dialog_choice(title, msg, alt1, alt2, alt3, alt4, alt5, NULL);
|
||||
/* As choice is modal, call the callback function directly. */
|
||||
Dpiapi_dialog_answer_cb(bw, ret);
|
||||
|
||||
dFree(alt1); dFree(alt2); dFree(alt3); dFree(alt4); dFree(alt5);
|
||||
dFree(title); dFree(msg);
|
||||
}
|
||||
|
||||
3
src/dpiapi.h
Normal file
3
src/dpiapi.h
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
void a_Dpiapi_dialog(BrowserWindow *bw, char *server, char *dpip_tag);
|
||||
|
||||
21
src/dpng.h
Normal file
21
src/dpng.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef __PNG_H__
|
||||
#define __PNG_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#include "url.h"
|
||||
#include "image.hh"
|
||||
#include "cache.h"
|
||||
|
||||
|
||||
void *a_Png_new(DilloImage *Image, DilloUrl *url, int version);
|
||||
void a_Png_callback(int Op, CacheClient_t *Client);
|
||||
const char *a_Png_version(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* !__PNG_H__ */
|
||||
19
src/dsvg.h
Normal file
19
src/dsvg.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef __SVG_H__
|
||||
#define __SVG_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#include "url.h"
|
||||
#include "image.hh"
|
||||
|
||||
|
||||
void *a_Svg_new(DilloImage *Image, DilloUrl *url, int version);
|
||||
void a_Svg_callback(int Op, CacheClient_t *Client);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* !__SVG_H__ */
|
||||
20
src/dwebp.h
Normal file
20
src/dwebp.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef __WEBP_H__
|
||||
#define __WEBP_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#include "url.h"
|
||||
#include "image.hh"
|
||||
|
||||
|
||||
void *a_Webp_new(DilloImage *Image, DilloUrl *url, int version);
|
||||
void a_Webp_callback(int Op, void *data);
|
||||
const char *a_Webp_version(char *buf, int n);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* !__WEBP_H__ */
|
||||
210
src/findbar.cc
Normal file
210
src/findbar.cc
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* File: findbar.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.
|
||||
*/
|
||||
|
||||
#include <FL/Fl.H>
|
||||
#include <FL/Fl_Window.H>
|
||||
#include "findbar.hh"
|
||||
|
||||
#include "msg.h"
|
||||
#include "pixmaps.h"
|
||||
#include "uicmd.hh"
|
||||
#include "bw.h"
|
||||
|
||||
/*
|
||||
* Local sub class
|
||||
* (Used to handle escape in the findbar, may also avoid some shortcuts).
|
||||
*/
|
||||
class MyInput : public Fl_Input {
|
||||
public:
|
||||
MyInput (int x, int y, int w, int h, const char* l=0) :
|
||||
Fl_Input(x,y,w,h,l) {};
|
||||
int handle(int e);
|
||||
};
|
||||
|
||||
int MyInput::handle(int e)
|
||||
{
|
||||
_MSG("findbar MyInput::handle()\n");
|
||||
int ret = 1, k = Fl::event_key();
|
||||
unsigned modifier = Fl::event_state() & (FL_SHIFT| FL_CTRL| FL_ALT|FL_META);
|
||||
|
||||
if (e == FL_KEYBOARD) {
|
||||
if (k == FL_Page_Down || k == FL_Page_Up || k == FL_Up || k == FL_Down) {
|
||||
// Let them through for key commands and viewport motion.
|
||||
return 0;
|
||||
}
|
||||
if (modifier == FL_SHIFT) {
|
||||
if (k == FL_Left || k == FL_Right) {
|
||||
// Let these keys get to the UI
|
||||
return 0;
|
||||
}
|
||||
} else if (modifier == FL_CTRL) {
|
||||
if (k == 'a' || k == 'e') {
|
||||
position(k == 'a' ? 0 : size());
|
||||
return 1;
|
||||
} else if (k == 'k') {
|
||||
cut(position(), size());
|
||||
return 1;
|
||||
} else if (k == 'd') {
|
||||
cut(position(), position()+1);
|
||||
return 1;
|
||||
} else if (k == 'h' || k == 'i' || k == 'j' || k == 'l' || k == 'm') {
|
||||
// Fl_Input wants to use ^H as backspace, and also "insert a few
|
||||
// selected control characters literally", but this gets in the way
|
||||
// of key commands.
|
||||
return 0;
|
||||
}
|
||||
} else if (k == FL_Escape && modifier == 0) {
|
||||
// Avoid clearing the text with Esc, just hide the findbar.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret)
|
||||
ret = Fl_Input::handle(e);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find next occurrence of input key
|
||||
*/
|
||||
void Findbar::search_cb(Fl_Widget *, void *vfb)
|
||||
{
|
||||
Findbar *fb = (Findbar *)vfb;
|
||||
const char *key = fb->i->value();
|
||||
bool case_sens = fb->check_btn->value();
|
||||
|
||||
if (key[0] != '\0')
|
||||
a_UIcmd_findtext_search(a_UIcmd_get_bw_by_widget(fb),
|
||||
key, case_sens, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find previous occurrence of input key
|
||||
*/
|
||||
void Findbar::searchBackwards_cb(Fl_Widget *, void *vfb)
|
||||
{
|
||||
Findbar *fb = (Findbar *)vfb;
|
||||
const char *key = fb->i->value();
|
||||
bool case_sens = fb->check_btn->value();
|
||||
|
||||
if (key[0] != '\0') {
|
||||
a_UIcmd_findtext_search(a_UIcmd_get_bw_by_widget(fb),
|
||||
key, case_sens, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the search bar
|
||||
*/
|
||||
void Findbar::hide_cb(Fl_Widget *, void *vfb)
|
||||
{
|
||||
a_UIcmd_findbar_toggle(a_UIcmd_get_bw_by_widget(vfb), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct text search bar
|
||||
*/
|
||||
Findbar::Findbar(int width, int height) :
|
||||
Fl_Group(0, 0, width, height)
|
||||
{
|
||||
int button_width = 70;
|
||||
int gap = 2;
|
||||
int border = 2;
|
||||
int input_width = width - (2 * border + 4 * (button_width + gap));
|
||||
int x = 0;
|
||||
|
||||
Fl_Group::current(0);
|
||||
|
||||
height -= 2 * border;
|
||||
|
||||
box(FL_THIN_UP_BOX);
|
||||
|
||||
hide_btn = new CustButton(x, border, 16, height, 0);
|
||||
hideImg = new Fl_Pixmap(new_s_xpm);
|
||||
hide_btn->image(hideImg);
|
||||
x += 16 + gap;
|
||||
hide_btn->callback(hide_cb, this);
|
||||
hide_btn->clear_visible_focus();
|
||||
hide_btn->box(FL_THIN_UP_BOX);
|
||||
hide_btn->set_tooltip("Hide");
|
||||
add(hide_btn);
|
||||
|
||||
i = new MyInput(x, border, input_width, height);
|
||||
x += input_width + gap;
|
||||
resizable(i);
|
||||
i->when(FL_WHEN_NEVER);
|
||||
add(i);
|
||||
|
||||
next_btn = new CustButton(x, border, button_width, height, "Next");
|
||||
x += button_width + gap;
|
||||
next_btn->shortcut(FL_Enter);
|
||||
next_btn->callback(search_cb, this);
|
||||
next_btn->clear_visible_focus();
|
||||
next_btn->box(FL_THIN_UP_BOX);
|
||||
next_btn->set_tooltip("Find next occurrence of the search phrase\n"
|
||||
"shortcut: Enter");
|
||||
add(next_btn);
|
||||
|
||||
prev_btn= new CustButton(x, border, button_width, height, "Previous");
|
||||
x += button_width + gap;
|
||||
prev_btn->shortcut(FL_SHIFT+FL_Enter);
|
||||
prev_btn->callback(searchBackwards_cb, this);
|
||||
prev_btn->clear_visible_focus();
|
||||
prev_btn->box(FL_THIN_UP_BOX);
|
||||
prev_btn->set_tooltip("Find previous occurrence of the search phrase\n"
|
||||
"shortcut: Shift+Enter");
|
||||
add(prev_btn);
|
||||
|
||||
check_btn = new Fl_Check_Button(x, border, 2*button_width, height,
|
||||
"Case-sensitive");
|
||||
x += 2 * button_width + gap;
|
||||
check_btn->clear_visible_focus();
|
||||
add(check_btn);
|
||||
|
||||
}
|
||||
|
||||
Findbar::~Findbar()
|
||||
{
|
||||
delete hideImg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle events. Used to catch FL_Escape events.
|
||||
*/
|
||||
int Findbar::handle(int event)
|
||||
{
|
||||
int k = Fl::event_key();
|
||||
unsigned modifier = Fl::event_state() & (FL_SHIFT| FL_CTRL| FL_ALT|FL_META);
|
||||
|
||||
if (event == FL_KEYBOARD && modifier == 0 && k == FL_Escape) {
|
||||
/* let the UI handle it */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Fl_Group::handle(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the findbar and focus the input field
|
||||
*/
|
||||
void Findbar::show()
|
||||
{
|
||||
BrowserWindow *bw = a_UIcmd_get_bw_by_widget(this);
|
||||
dReturn_if (bw == NULL);
|
||||
|
||||
// It takes more than just calling show() to do the trick
|
||||
Fl_Group::show();
|
||||
|
||||
/* select text even if already focused */
|
||||
i->take_focus();
|
||||
i->position(i->size(), 0);
|
||||
}
|
||||
|
||||
33
src/findbar.hh
Normal file
33
src/findbar.hh
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef __FINDBAR_HH__
|
||||
#define __FINDBAR_HH__
|
||||
|
||||
#include <FL/Fl_Pixmap.H>
|
||||
#include <FL/Fl_Widget.H>
|
||||
#include <FL/Fl_Button.H>
|
||||
#include <FL/Fl_Input.H>
|
||||
#include <FL/Fl_Group.H>
|
||||
#include <FL/Fl_Check_Button.H>
|
||||
|
||||
#include "tipwin.hh"
|
||||
|
||||
/**
|
||||
* Searchbar to find text in page.
|
||||
*/
|
||||
class Findbar : public Fl_Group {
|
||||
CustButton *hide_btn, *next_btn, *prev_btn;
|
||||
Fl_Check_Button *check_btn;
|
||||
Fl_Pixmap *hideImg;
|
||||
Fl_Input *i;
|
||||
|
||||
static void search_cb (Fl_Widget *, void *);
|
||||
static void searchBackwards_cb (Fl_Widget *, void *);
|
||||
static void hide_cb (Fl_Widget *, void *);
|
||||
|
||||
public:
|
||||
Findbar(int width, int height);
|
||||
~Findbar();
|
||||
int handle(int event);
|
||||
void show();
|
||||
};
|
||||
|
||||
#endif // __FINDBAR_HH__
|
||||
2064
src/form.cc
Normal file
2064
src/form.cc
Normal file
File diff suppressed because it is too large
Load Diff
66
src/form.hh
Normal file
66
src/form.hh
Normal file
@ -0,0 +1,66 @@
|
||||
#ifndef __FORM_HH__
|
||||
#define __FORM_HH__
|
||||
|
||||
#include "url.h"
|
||||
|
||||
/*
|
||||
* Typedefs
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
DILLO_HTML_METHOD_UNKNOWN,
|
||||
DILLO_HTML_METHOD_GET,
|
||||
DILLO_HTML_METHOD_POST
|
||||
} DilloHtmlMethod;
|
||||
|
||||
typedef enum {
|
||||
DILLO_HTML_ENC_URLENCODED,
|
||||
DILLO_HTML_ENC_MULTIPART
|
||||
} DilloHtmlEnc;
|
||||
|
||||
/*
|
||||
* Classes
|
||||
*/
|
||||
|
||||
class DilloHtmlForm;
|
||||
class DilloHtmlInput;
|
||||
class DilloHtml;
|
||||
|
||||
/*
|
||||
* Form API
|
||||
*/
|
||||
|
||||
DilloHtmlForm *a_Html_form_new(DilloHtml *html,
|
||||
DilloHtmlMethod method,
|
||||
const DilloUrl *action,
|
||||
DilloHtmlEnc enc,
|
||||
const char *charset, bool enabled);
|
||||
|
||||
void a_Html_form_delete(DilloHtmlForm* form);
|
||||
void a_Html_input_delete(DilloHtmlInput* input);
|
||||
void a_Html_form_submit2(void *v_form);
|
||||
void a_Html_form_reset2(void *v_form);
|
||||
void a_Html_form_display_hiddens2(void *v_form, bool display);
|
||||
|
||||
|
||||
/*
|
||||
* Form parsing functions
|
||||
*/
|
||||
|
||||
void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize);
|
||||
void Html_tag_close_form(DilloHtml *html);
|
||||
void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize);
|
||||
void Html_tag_open_isindex(DilloHtml *html, const char *tag, int tagsize);
|
||||
void Html_tag_open_textarea(DilloHtml *html, const char *tag, int tagsize);
|
||||
void Html_tag_content_textarea(DilloHtml *html, const char *tag, int tagsize);
|
||||
void Html_tag_close_textarea(DilloHtml *html);
|
||||
void Html_tag_open_select(DilloHtml *html, const char *tag, int tagsize);
|
||||
void Html_tag_close_select(DilloHtml *html);
|
||||
void Html_tag_open_option(DilloHtml *html, const char *tag, int tagsize);
|
||||
void Html_tag_close_option(DilloHtml *html);
|
||||
void Html_tag_open_optgroup(DilloHtml *html, const char *tag, int tagsize);
|
||||
void Html_tag_close_optgroup(DilloHtml *html);
|
||||
void Html_tag_open_button(DilloHtml *html, const char *tag, int tagsize);
|
||||
void Html_tag_close_button(DilloHtml *html);
|
||||
|
||||
#endif /* __FORM_HH__ */
|
||||
161
src/history.c
Normal file
161
src/history.c
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* File: history.c
|
||||
*
|
||||
* Copyright (C) 2001-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.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* Linear history (it also provides indexes for the navigation stack)
|
||||
*/
|
||||
|
||||
#include "msg.h"
|
||||
#include "list.h"
|
||||
#include "history.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
DilloUrl *url;
|
||||
char *title;
|
||||
} H_Item;
|
||||
|
||||
|
||||
/* Global history list */
|
||||
static H_Item *history = NULL;
|
||||
static int history_size = 0; /* [1 based] */
|
||||
static int history_size_max = 16;
|
||||
|
||||
|
||||
/**
|
||||
* Debug procedure.
|
||||
*/
|
||||
void History_show(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
MSG(" {");
|
||||
for (i = 0; i < history_size; ++i)
|
||||
MSG(" %s", URL_STR(history[i].url));
|
||||
MSG(" }\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new H_Item at the end of the history list
|
||||
* (taking care of not making a duplicate entry)
|
||||
*/
|
||||
int a_History_add_url(DilloUrl *url)
|
||||
{
|
||||
int i, idx;
|
||||
|
||||
_MSG("a_History_add_url: '%s' ", URL_STR(url));
|
||||
for (i = 0; i < history_size; ++i)
|
||||
if (!a_Url_cmp(history[i].url, url) &&
|
||||
!strcmp(URL_FRAGMENT(history[i].url), URL_FRAGMENT(url)))
|
||||
break;
|
||||
|
||||
if (i < history_size) {
|
||||
idx = i;
|
||||
_MSG("FOUND at idx=%d\n", idx);
|
||||
} else {
|
||||
idx = history_size;
|
||||
a_List_add(history, history_size, history_size_max);
|
||||
history[idx].url = a_Url_dup(url);
|
||||
history[idx].title = NULL;
|
||||
++history_size;
|
||||
_MSG("ADDED at idx=%d\n", idx);
|
||||
}
|
||||
|
||||
/* History_show(); */
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DilloUrl field (by index)
|
||||
*/
|
||||
const DilloUrl *a_History_get_url(int idx)
|
||||
{
|
||||
_MSG("a_History_get_url: ");
|
||||
/* History_show(); */
|
||||
|
||||
dReturn_val_if_fail(idx >= 0 && idx < history_size, NULL);
|
||||
|
||||
return history[idx].url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the title field (by index)
|
||||
* ('force' returns URL_STR when there's no title)
|
||||
*/
|
||||
const char *a_History_get_title(int idx, int force)
|
||||
{
|
||||
dReturn_val_if_fail(idx >= 0 && idx < history_size, NULL);
|
||||
|
||||
if (history[idx].title)
|
||||
return history[idx].title;
|
||||
else if (force)
|
||||
return URL_STR(history[idx].url);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the title field (by url)
|
||||
* ('force' returns URL_STR when there's no title)
|
||||
*/
|
||||
const char *a_History_get_title_by_url(const DilloUrl *url, int force)
|
||||
{
|
||||
int i;
|
||||
|
||||
dReturn_val_if_fail(url != NULL, NULL);
|
||||
|
||||
for (i = 0; i < history_size; ++i)
|
||||
if (a_Url_cmp(url, history[i].url) == 0)
|
||||
break;
|
||||
|
||||
if (i < history_size && history[i].title)
|
||||
return history[i].title;
|
||||
else if (force)
|
||||
return URL_STR_(url);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the page-title for a given URL
|
||||
*/
|
||||
void a_History_set_title_by_url(const DilloUrl *url, const char *title)
|
||||
{
|
||||
int i;
|
||||
|
||||
dReturn_if (url == NULL);
|
||||
|
||||
for (i = history_size - 1; i >= 0; --i)
|
||||
if (a_Url_cmp(url, history[i].url) == 0)
|
||||
break;
|
||||
|
||||
if (i >= 0) {
|
||||
dFree(history[i].title);
|
||||
history[i].title = dStrdup(title);
|
||||
} else {
|
||||
MSG_ERR("a_History_set_title_by_url: %s not found\n", URL_STR(url));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Free all the memory used by this module
|
||||
*/
|
||||
void a_History_freeall(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < history_size; ++i) {
|
||||
a_Url_free(history[i].url);
|
||||
dFree(history[i].title);
|
||||
}
|
||||
dFree(history);
|
||||
}
|
||||
24
src/history.h
Normal file
24
src/history.h
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
#ifndef __DILLO_HISTORY_H__
|
||||
#define __DILLO_HISTORY_H__
|
||||
|
||||
#include "url.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int a_History_add_url(DilloUrl *url);
|
||||
void a_History_set_title_by_url(const DilloUrl *url, const char *title);
|
||||
const DilloUrl *a_History_get_url(int idx);
|
||||
const char *a_History_get_title(int idx, int force);
|
||||
const char *a_History_get_title_by_url(const DilloUrl *url, int force);
|
||||
void a_History_freeall(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __DILLO_HISTORY_H__ */
|
||||
371
src/hsts.c
Normal file
371
src/hsts.c
Normal file
@ -0,0 +1,371 @@
|
||||
/*
|
||||
* File: hsts.c
|
||||
* HTTP Strict Transport Security
|
||||
*
|
||||
* Copyright 2015 corvid
|
||||
* Copyright (C) 2023 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* HTTP Strict Transport Security
|
||||
*/
|
||||
|
||||
/* To preload hosts, as of 2015, chromium is the list keeper:
|
||||
* https://src.chromium.org/viewvc/chrome/trunk/src/net/http/transport_security_state_static.json
|
||||
* although mozilla's is easier to work from (and they trim it based on
|
||||
* criteria such as max-age must be at least some number of months)
|
||||
* https://mxr.mozilla.org/mozilla-central/source/security/manager/ssl/nsSTSPreloadList.inc?raw=1
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h> /* for INT_MAX */
|
||||
#include <ctype.h> /* for isspace */
|
||||
#include <stdlib.h> /* for strtol */
|
||||
|
||||
#include "hsts.h"
|
||||
#include "msg.h"
|
||||
#include "../dlib/dlib.h"
|
||||
#include "IO/tls.h"
|
||||
|
||||
typedef struct {
|
||||
char *host;
|
||||
time_t expires_at;
|
||||
bool_t subdomains;
|
||||
} HstsData_t;
|
||||
|
||||
/* When there is difficulty in representing future dates, use the (by far)
|
||||
* most likely latest representable time of January 19, 2038.
|
||||
*/
|
||||
static time_t hsts_latest_representable_time;
|
||||
static Dlist *domains;
|
||||
|
||||
static void Hsts_free_policy(HstsData_t *p)
|
||||
{
|
||||
dFree(p->host);
|
||||
dFree(p);
|
||||
}
|
||||
|
||||
void a_Hsts_freeall(void)
|
||||
{
|
||||
if (prefs.http_strict_transport_security) {
|
||||
HstsData_t *policy;
|
||||
int i, n = dList_length(domains);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
policy = dList_nth_data(domains, i);
|
||||
Hsts_free_policy(policy);
|
||||
}
|
||||
dList_free(domains);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare function for searching a domain node by domain string
|
||||
*/
|
||||
static int Domain_node_domain_str_cmp(const void *v1, const void *v2)
|
||||
{
|
||||
const HstsData_t *node = v1;
|
||||
const char *host = v2;
|
||||
|
||||
return dStrAsciiCasecmp(node->host, host);
|
||||
}
|
||||
|
||||
static HstsData_t *Hsts_get_policy(const char *host)
|
||||
{
|
||||
return dList_find_sorted(domains, host, Domain_node_domain_str_cmp);
|
||||
}
|
||||
|
||||
static void Hsts_remove_policy(HstsData_t *policy)
|
||||
{
|
||||
if (policy) {
|
||||
_MSG("HSTS: removed policy for %s\n", policy->host);
|
||||
Hsts_free_policy(policy);
|
||||
dList_remove(domains, policy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time_t for a future time.
|
||||
*/
|
||||
static time_t Hsts_future_time(long seconds_from_now)
|
||||
{
|
||||
time_t ret, now = time(NULL);
|
||||
struct tm *tm = gmtime(&now);
|
||||
|
||||
if (seconds_from_now > INT_MAX - tm->tm_sec)
|
||||
tm->tm_sec = INT_MAX;
|
||||
else
|
||||
tm->tm_sec += seconds_from_now;
|
||||
|
||||
ret = mktime(tm);
|
||||
if (ret == (time_t) -1)
|
||||
ret = hsts_latest_representable_time;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare function for searching domains.
|
||||
*/
|
||||
static int Domain_node_cmp(const void *v1, const void *v2)
|
||||
{
|
||||
const HstsData_t *node1 = v1, *node2 = v2;
|
||||
|
||||
return dStrAsciiCasecmp(node1->host, node2->host);
|
||||
}
|
||||
|
||||
static void Hsts_set_policy(const char *host, long max_age, bool_t subdomains)
|
||||
{
|
||||
time_t exp = Hsts_future_time(max_age);
|
||||
HstsData_t *policy = Hsts_get_policy(host);
|
||||
|
||||
_MSG("HSTS: %s %s%s: until %s", (policy ? "modify" : "add"), host,
|
||||
(subdomains ? " and subdomains" : ""), ctime(&exp));
|
||||
|
||||
if (policy == NULL) {
|
||||
policy = dNew0(HstsData_t, 1);
|
||||
policy->host = dStrdup(host);
|
||||
dList_insert_sorted(domains, policy, Domain_node_cmp);
|
||||
}
|
||||
policy->subdomains = subdomains;
|
||||
policy->expires_at = exp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the next attribute.
|
||||
*/
|
||||
static char *Hsts_parse_attr(const char **header_str)
|
||||
{
|
||||
const char *str;
|
||||
uint_t len;
|
||||
|
||||
while (dIsspace(**header_str))
|
||||
(*header_str)++;
|
||||
|
||||
str = *header_str;
|
||||
/* find '=' at end of attr, ';' after attr/val pair, '\0' end of string */
|
||||
len = strcspn(str, "=;");
|
||||
*header_str += len;
|
||||
|
||||
while (len && (str[len - 1] == ' ' || str[len - 1] == '\t'))
|
||||
len--;
|
||||
return dStrndup(str, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value in *header_str.
|
||||
*/
|
||||
static char *Hsts_parse_value(const char **header_str)
|
||||
{
|
||||
uint_t len;
|
||||
const char *str;
|
||||
|
||||
if (**header_str == '=') {
|
||||
(*header_str)++;
|
||||
while (dIsspace(**header_str))
|
||||
(*header_str)++;
|
||||
|
||||
str = *header_str;
|
||||
/* finds ';' after attr/val pair or '\0' at end of string */
|
||||
len = strcspn(str, ";");
|
||||
*header_str += len;
|
||||
|
||||
while (len && (str[len - 1] == ' ' || str[len - 1] == '\t'))
|
||||
len--;
|
||||
} else {
|
||||
str = *header_str;
|
||||
len = 0;
|
||||
}
|
||||
return dStrndup(str, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance past any value.
|
||||
*/
|
||||
static void Hsts_eat_value(const char **str)
|
||||
{
|
||||
if (**str == '=')
|
||||
*str += strcspn(*str, ";");
|
||||
}
|
||||
|
||||
/**
|
||||
* The response for this url had an HSTS header, so let's take action.
|
||||
*/
|
||||
void a_Hsts_set(const char *header, const DilloUrl *url)
|
||||
{
|
||||
long max_age = 0;
|
||||
const char *host = URL_HOST(url);
|
||||
bool_t max_age_valid = FALSE, subdomains = FALSE;
|
||||
|
||||
_MSG("HSTS header for %s: %s\n", host, header);
|
||||
|
||||
if (!a_Tls_certificate_is_clean(url)) {
|
||||
/* RFC 6797 gives rationale in section 14.3. */
|
||||
_MSG("But there were certificate warnings, so ignore it (!)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Iterate until there is nothing left of the string */
|
||||
while (*header) {
|
||||
char *attr;
|
||||
char *value;
|
||||
|
||||
/* Get attribute */
|
||||
attr = Hsts_parse_attr(&header);
|
||||
|
||||
/* Get the value for the attribute and store it */
|
||||
if (dStrAsciiCasecmp(attr, "max-age") == 0) {
|
||||
value = Hsts_parse_value(&header);
|
||||
if (isdigit(*value)) {
|
||||
errno = 0;
|
||||
max_age = strtol(value, NULL, 10);
|
||||
if (errno == ERANGE)
|
||||
max_age = INT_MAX;
|
||||
max_age_valid = TRUE;
|
||||
}
|
||||
dFree(value);
|
||||
} else if (dStrAsciiCasecmp(attr, "includeSubDomains") == 0) {
|
||||
subdomains = TRUE;
|
||||
Hsts_eat_value(&header);
|
||||
} else if (dStrAsciiCasecmp(attr, "preload") == 0) {
|
||||
/* 'preload' is not part of the RFC, but what does google care for
|
||||
* standards? They require that 'preload' be specified by a domain
|
||||
* in order to be added to their preload list.
|
||||
*/
|
||||
} else {
|
||||
MSG("HSTS: header contains unknown attribute: '%s'\n", attr);
|
||||
Hsts_eat_value(&header);
|
||||
}
|
||||
|
||||
dFree(attr);
|
||||
|
||||
if (*header == ';')
|
||||
header++;
|
||||
}
|
||||
if (max_age_valid) {
|
||||
if (max_age > 0)
|
||||
Hsts_set_policy(host, max_age, subdomains);
|
||||
else
|
||||
Hsts_remove_policy(Hsts_get_policy(host));
|
||||
}
|
||||
}
|
||||
|
||||
static bool_t Hsts_expired(HstsData_t *policy)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
bool_t ret = (now > policy->expires_at) ? TRUE : FALSE;
|
||||
|
||||
if (ret) {
|
||||
_MSG("HSTS: expired\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool_t a_Hsts_require_https(const char *host)
|
||||
{
|
||||
bool_t ret = FALSE;
|
||||
|
||||
if (host) {
|
||||
HstsData_t *policy = Hsts_get_policy(host);
|
||||
|
||||
if (policy) {
|
||||
_MSG("HSTS: matched host %s\n", host);
|
||||
if (Hsts_expired(policy))
|
||||
Hsts_remove_policy(policy);
|
||||
else
|
||||
ret = TRUE;
|
||||
}
|
||||
if (!ret) {
|
||||
const char *domain_str;
|
||||
|
||||
for (domain_str = strchr(host+1, '.');
|
||||
domain_str != NULL && *domain_str;
|
||||
domain_str = strchr(domain_str+1, '.')) {
|
||||
policy = Hsts_get_policy(domain_str+1);
|
||||
|
||||
if (policy && policy->subdomains) {
|
||||
_MSG("HSTS: matched %s under %s subdomain rule\n", host,
|
||||
policy->host);
|
||||
if (Hsts_expired(policy)) {
|
||||
Hsts_remove_policy(policy);
|
||||
} else {
|
||||
ret = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void Hsts_preload(FILE *stream)
|
||||
{
|
||||
const int LINE_MAXLEN = 4096;
|
||||
const long ONE_YEAR = 60 * 60 * 24 * 365;
|
||||
|
||||
char *rc, *subdomains;
|
||||
char line[LINE_MAXLEN];
|
||||
char domain[LINE_MAXLEN];
|
||||
|
||||
/* Get all lines in the file */
|
||||
while (!feof(stream)) {
|
||||
line[0] = '\0';
|
||||
rc = fgets(line, LINE_MAXLEN, stream);
|
||||
if (!rc && ferror(stream)) {
|
||||
MSG_WARN("HSTS: Error while reading preload entries: %s\n",
|
||||
dStrerror(errno));
|
||||
return; /* bail out */
|
||||
}
|
||||
|
||||
/* Remove leading and trailing whitespace */
|
||||
dStrstrip(line);
|
||||
|
||||
if (line[0] != '\0' && line[0] != '#') {
|
||||
int i = 0, j = 0;
|
||||
|
||||
/* Get the domain */
|
||||
while (line[i] != '\0' && !dIsspace(line[i]))
|
||||
domain[j++] = line[i++];
|
||||
domain[j] = '\0';
|
||||
|
||||
/* Skip past whitespace */
|
||||
while (dIsspace(line[i]))
|
||||
i++;
|
||||
|
||||
subdomains = line + i;
|
||||
|
||||
if (dStrAsciiCasecmp(subdomains, "true") == 0)
|
||||
Hsts_set_policy(domain, ONE_YEAR, TRUE);
|
||||
else if (dStrAsciiCasecmp(subdomains, "false") == 0)
|
||||
Hsts_set_policy(domain, ONE_YEAR, FALSE);
|
||||
else {
|
||||
MSG_WARN("HSTS: format of line not recognized. Ignoring '%s'.\n",
|
||||
line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void a_Hsts_init(FILE *preload_file)
|
||||
{
|
||||
if (prefs.http_strict_transport_security) {
|
||||
struct tm future_tm = {7, 14, 3, 19, 0, 138, 0, 0, 0, 0, 0};
|
||||
|
||||
hsts_latest_representable_time = mktime(&future_tm);
|
||||
domains = dList_new(32);
|
||||
|
||||
if (preload_file) {
|
||||
Hsts_preload(preload_file);
|
||||
fclose(preload_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
19
src/hsts.h
Normal file
19
src/hsts.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef __HSTS_H__
|
||||
#define __HSTS_H__
|
||||
|
||||
#include "d_size.h"
|
||||
#include "url.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void a_Hsts_init(FILE *fp);
|
||||
void a_Hsts_set(const char *header, const DilloUrl *url);
|
||||
bool_t a_Hsts_require_https(const char *host);
|
||||
void a_Hsts_freeall( void );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* !__HSTS_H__ */
|
||||
10050
src/hsts_preload
Normal file
10050
src/hsts_preload
Normal file
File diff suppressed because it is too large
Load Diff
4446
src/html.cc
Normal file
4446
src/html.cc
Normal file
File diff suppressed because it is too large
Load Diff
39
src/html.hh
Normal file
39
src/html.hh
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* File: html.hh
|
||||
*
|
||||
* Copyright (C) 2005-2009 Jorge Arellano Cid <jcid@dillo.org>
|
||||
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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 __HTML_HH__
|
||||
#define __HTML_HH__
|
||||
|
||||
#include "url.h" // for DilloUrl
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* Keep in sync with the length of the Tags array. It is protected by an
|
||||
* static assert in html.cc to prevent errors) */
|
||||
#define HTML_NTAGS 93
|
||||
|
||||
/*
|
||||
* Exported functions
|
||||
*/
|
||||
void a_Html_load_images(void *v_html, DilloUrl *pattern);
|
||||
void a_Html_form_submit(void *v_html, void *v_form);
|
||||
void a_Html_form_reset(void *v_html, void *v_form);
|
||||
void a_Html_form_display_hiddens(void *v_html, void *v_form, bool_t display);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#endif /* __HTML_HH__ */
|
||||
2138
src/html_charrefs.h
Normal file
2138
src/html_charrefs.h
Normal file
File diff suppressed because it is too large
Load Diff
292
src/html_common.hh
Normal file
292
src/html_common.hh
Normal file
@ -0,0 +1,292 @@
|
||||
/*
|
||||
* File: html_common.hh
|
||||
*
|
||||
* Copyright (C) 2008-2016 Jorge Arellano Cid <jcid@dillo.org>
|
||||
* Copyright (C) 2008-2014 Johannes Hofmann <Johannes.Hofmann@gmx.de>
|
||||
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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 __HTML_COMMON_HH__
|
||||
#define __HTML_COMMON_HH__
|
||||
|
||||
#include "url.h"
|
||||
#include "bw.h"
|
||||
|
||||
#include "lout/misc.hh"
|
||||
#include "dw/core.hh"
|
||||
#include "dw/image.hh"
|
||||
#include "dw/style.hh"
|
||||
|
||||
#include "image.hh"
|
||||
|
||||
#include "form.hh"
|
||||
|
||||
#include "styleengine.hh"
|
||||
|
||||
/*
|
||||
* Macros
|
||||
*/
|
||||
|
||||
/** "html struct" to Textblock */
|
||||
#define HT2TB(html) ((Textblock*)(html->dw))
|
||||
/** "html struct" to "Layout" */
|
||||
#define HT2LT(html) ((Layout*)html->bw->render_layout)
|
||||
/** "Image" to "Dw Widget" */
|
||||
#define IM2DW(Image) ((Widget*)Image->dw)
|
||||
/** Top of the parsing stack */
|
||||
#define S_TOP(html) (html->stack->getRef(html->stack->size()-1))
|
||||
|
||||
/** Add a bug-meter message. */
|
||||
#define BUG_MSG(...) \
|
||||
D_STMT_START { \
|
||||
html->bugMessage(__VA_ARGS__); \
|
||||
} D_STMT_END
|
||||
|
||||
|
||||
/*
|
||||
* Typedefs
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
DT_NONE,
|
||||
DT_UNRECOGNIZED,
|
||||
DT_HTML,
|
||||
DT_XHTML
|
||||
} DilloHtmlDocumentType;
|
||||
|
||||
typedef enum {
|
||||
DILLO_HTML_PARSE_MODE_INIT = 0,
|
||||
DILLO_HTML_PARSE_MODE_STASH,
|
||||
DILLO_HTML_PARSE_MODE_STASH_AND_BODY,
|
||||
DILLO_HTML_PARSE_MODE_VERBATIM,
|
||||
DILLO_HTML_PARSE_MODE_BODY,
|
||||
DILLO_HTML_PARSE_MODE_PRE
|
||||
} DilloHtmlParseMode;
|
||||
|
||||
typedef enum {
|
||||
DILLO_HTML_TABLE_MODE_NONE, /**< no table at all */
|
||||
DILLO_HTML_TABLE_MODE_TOP, /**< outside of <tr> */
|
||||
DILLO_HTML_TABLE_MODE_TR, /**< inside of <tr>, outside of <td> */
|
||||
DILLO_HTML_TABLE_MODE_TD /**< inside of <td> */
|
||||
} DilloHtmlTableMode;
|
||||
|
||||
typedef enum {
|
||||
DILLO_HTML_TABLE_BORDER_SEPARATE,
|
||||
DILLO_HTML_TABLE_BORDER_COLLAPSE
|
||||
} DilloHtmlTableBorderMode;
|
||||
|
||||
typedef enum {
|
||||
HTML_LIST_NONE,
|
||||
HTML_LIST_UNORDERED,
|
||||
HTML_LIST_ORDERED
|
||||
} DilloHtmlListMode;
|
||||
|
||||
typedef enum {
|
||||
IN_NONE = 0,
|
||||
IN_HTML = 1 << 0,
|
||||
IN_HEAD = 1 << 1,
|
||||
IN_BODY = 1 << 2,
|
||||
IN_FORM = 1 << 3,
|
||||
IN_SELECT = 1 << 4,
|
||||
IN_OPTION = 1 << 5,
|
||||
IN_OPTGROUP = 1 << 6,
|
||||
IN_TEXTAREA = 1 << 7,
|
||||
IN_BUTTON = 1 << 8,
|
||||
IN_MAP = 1 << 9,
|
||||
IN_PRE = 1 << 10,
|
||||
IN_LI = 1 << 11,
|
||||
IN_MEDIA = 1 << 12,
|
||||
IN_META_HACK = 1 << 13,
|
||||
IN_A = 1 << 14,
|
||||
IN_EOF = 1 << 15
|
||||
} DilloHtmlProcessingState;
|
||||
|
||||
/*
|
||||
* Data Structures
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
DilloUrl *url;
|
||||
DilloImage *image;
|
||||
} DilloHtmlImage;
|
||||
|
||||
typedef struct {
|
||||
DilloHtmlParseMode parse_mode;
|
||||
DilloHtmlTableMode table_mode;
|
||||
DilloHtmlTableBorderMode table_border_mode;
|
||||
bool cell_text_align_set;
|
||||
bool display_none;
|
||||
DilloHtmlListMode list_type;
|
||||
int list_number;
|
||||
|
||||
/** TagInfo index for the tag that's being processed */
|
||||
int tag_idx;
|
||||
|
||||
dw::core::Widget *textblock, *table;
|
||||
|
||||
/** This is used to align list items (especially in enumerated lists) */
|
||||
dw::core::Widget *ref_list_item;
|
||||
|
||||
/** This is used for list items etc. If it is set to TRUE, breaks
|
||||
have to be "handed over" (see Html_add_indented and
|
||||
Html_eventually_pop_dw). */
|
||||
bool hand_over_break;
|
||||
} DilloHtmlState;
|
||||
|
||||
/*
|
||||
* Classes
|
||||
*/
|
||||
|
||||
class DilloHtml {
|
||||
private:
|
||||
class HtmlLinkReceiver: public dw::core::Layout::LinkReceiver {
|
||||
public:
|
||||
DilloHtml *html;
|
||||
|
||||
bool enter (dw::core::Widget *widget, int link, int img, int x, int y);
|
||||
bool press (dw::core::Widget *widget, int link, int img, int x, int y,
|
||||
dw::core::EventButton *event);
|
||||
bool click (dw::core::Widget *widget, int link, int img, int x, int y,
|
||||
dw::core::EventButton *event);
|
||||
};
|
||||
HtmlLinkReceiver linkReceiver;
|
||||
|
||||
public: //BUG: for now everything is public
|
||||
|
||||
BrowserWindow *bw;
|
||||
DilloUrl *page_url, *base_url;
|
||||
dw::core::Widget *dw; /* this is duplicated in the stack */
|
||||
|
||||
/* -------------------------------------------------------------------*/
|
||||
/* Variables required at parsing time */
|
||||
/* -------------------------------------------------------------------*/
|
||||
char *Start_Buf;
|
||||
int Start_Ofs;
|
||||
char *content_type, *charset;
|
||||
bool stop_parser;
|
||||
|
||||
size_t CurrOfs, OldOfs, OldLine;
|
||||
|
||||
DilloHtmlDocumentType DocType; /* as given by DOCTYPE tag */
|
||||
float DocTypeVersion; /* HTML or XHTML version number */
|
||||
|
||||
/* vector of remote CSS resources, as given by the LINK element */
|
||||
lout::misc::SimpleVector<DilloUrl*> *cssUrls;
|
||||
|
||||
lout::misc::SimpleVector<DilloHtmlState> *stack;
|
||||
StyleEngine *styleEngine;
|
||||
|
||||
int InFlags; /**< tracks which elements we are in */
|
||||
|
||||
Dstr *Stash;
|
||||
bool StashSpace;
|
||||
|
||||
int pre_column; /**< current column, used in PRE tags with tabs */
|
||||
bool PreFirstChar; /**< used to skip the first CR or CRLF in PRE tags */
|
||||
bool PrevWasCR; /**< Flag to help parsing of "\r\n" in PRE tags */
|
||||
bool PrevWasOpenTag; /**< Flag to help deferred parsing of white space */
|
||||
bool InVisitedLink; /**< used to 'contrast_visited_colors' */
|
||||
bool ReqTagClose; /**< Flag to close the stack's top tag */
|
||||
bool TagSoup; /**< Flag to enable the parser's cleanup functions */
|
||||
bool loadCssFromStash; /**< current stash content should be loaded as CSS */
|
||||
bool PrevWasBodyClose; /**< set when </body> is found */
|
||||
bool PrevWasHtmlClose; /**< set when </html> is found */
|
||||
|
||||
/** element counters: used for validation purposes.
|
||||
* ATM they're used as three state flags {0,1,>1} */
|
||||
uchar_t Num_HTML, Num_HEAD, Num_BODY, Num_TITLE;
|
||||
|
||||
Dstr *attr_data; /**< Buffer for attribute value */
|
||||
|
||||
int32_t non_css_link_color; /**< as provided by link attribute in BODY */
|
||||
int32_t non_css_visited_color; /**< as provided by vlink attribute in BODY */
|
||||
int32_t visited_color; /**< as computed according to CSS */
|
||||
|
||||
/* -------------------------------------------------------------------*/
|
||||
/* Variables required after parsing (for page functionality) */
|
||||
/* -------------------------------------------------------------------*/
|
||||
lout::misc::SimpleVector<DilloHtmlForm*> *forms;
|
||||
lout::misc::SimpleVector<DilloHtmlInput*> *inputs_outside_form;
|
||||
lout::misc::SimpleVector<DilloUrl*> *links;
|
||||
lout::misc::SimpleVector<DilloHtmlImage*> *images;
|
||||
dw::ImageMapsList maps;
|
||||
|
||||
private:
|
||||
void freeParseData();
|
||||
void initDw(); /* Used by the constructor */
|
||||
|
||||
public:
|
||||
DilloHtml(BrowserWindow *bw, const DilloUrl *url, const char *content_type);
|
||||
~DilloHtml();
|
||||
void bugMessage(const char *format, ... );
|
||||
void connectSignals(dw::core::Widget *dw);
|
||||
void write(char *Buf, int BufSize, int Eof);
|
||||
int getCurrLineNumber();
|
||||
void finishParsing(int ClientKey);
|
||||
int formNew(DilloHtmlMethod method, const DilloUrl *action,
|
||||
DilloHtmlEnc enc, const char *charset);
|
||||
DilloHtmlForm *getCurrentForm ();
|
||||
bool_t unloadedImages();
|
||||
void loadImages (const DilloUrl *pattern);
|
||||
void addCssUrl(const DilloUrl *url);
|
||||
|
||||
// useful shortcuts
|
||||
inline void startElement (int tag)
|
||||
{ styleEngine->startElement (tag, bw); }
|
||||
inline void startElement (const char *tagname)
|
||||
{ styleEngine->startElement (tagname, bw); }
|
||||
|
||||
inline dw::core::style::Style *backgroundStyle ()
|
||||
{ return styleEngine->backgroundStyle (bw); }
|
||||
inline dw::core::style::Style *style ()
|
||||
{ return styleEngine->style (bw); }
|
||||
inline dw::core::style::Style *wordStyle ()
|
||||
{ return styleEngine->wordStyle (bw); }
|
||||
|
||||
inline void restyle () { styleEngine->restyle (bw); }
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Parser functions
|
||||
*/
|
||||
|
||||
int a_Html_tag_index(const char *tag);
|
||||
|
||||
const char *a_Html_get_attr(DilloHtml *html,
|
||||
const char *tag,
|
||||
int tagsize,
|
||||
const char *attrname);
|
||||
|
||||
char *a_Html_get_attr_wdef(DilloHtml *html,
|
||||
const char *tag,
|
||||
int tagsize,
|
||||
const char *attrname,
|
||||
const char *def);
|
||||
|
||||
DilloUrl *a_Html_url_new(DilloHtml *html,
|
||||
const char *url_str, const char *base_url,
|
||||
int use_base_url);
|
||||
|
||||
void a_Html_common_image_attrs(DilloHtml *html, const char *tag, int tagsize);
|
||||
DilloImage *a_Html_image_new(DilloHtml *html, const char *tag, int tagsize);
|
||||
|
||||
char *a_Html_parse_entities(DilloHtml *html, const char *token, int toksize);
|
||||
void a_Html_pop_tag(DilloHtml *html, int TagIdx);
|
||||
void a_Html_stash_init(DilloHtml *html);
|
||||
int32_t a_Html_color_parse(DilloHtml *html, const char *str,
|
||||
int32_t default_color);
|
||||
CssLength a_Html_parse_length (DilloHtml *html,
|
||||
const char *attr);
|
||||
void a_Html_tag_set_align_attr(DilloHtml *html, const char *tag, int tagsize);
|
||||
bool a_Html_tag_set_valign_attr(DilloHtml *html,
|
||||
const char *tag, int tagsize);
|
||||
|
||||
void a_Html_load_stylesheet(DilloHtml *html, DilloUrl *url);
|
||||
|
||||
#endif /* __HTML_COMMON_HH__ */
|
||||
158
src/image.cc
Normal file
158
src/image.cc
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* File: image.cc
|
||||
*
|
||||
* Copyright (C) 2005-2007 Jorge Arellano Cid <jcid@dillo.org>,
|
||||
* Sebastian Geerken <sgeerken@dillo.org>
|
||||
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* @file
|
||||
* Implements image data transfer methods. It handles the transfer
|
||||
* of data from an Image to a DwImage widget.
|
||||
*/
|
||||
|
||||
#include "msg.h"
|
||||
|
||||
#include "image.hh"
|
||||
#include "dw/core.hh"
|
||||
#include "dw/image.hh"
|
||||
|
||||
using namespace dw::core;
|
||||
|
||||
/** Image to Object-ImgRenderer macro */
|
||||
#define I2IR(Image) ((dw::core::ImgRenderer*)(Image->img_rndr))
|
||||
|
||||
|
||||
/**
|
||||
* Create and initialize a new image structure.
|
||||
*/
|
||||
DilloImage *a_Image_new(void *layout, void *img_rndr,
|
||||
int32_t bg_color, int32_t fg_color)
|
||||
{
|
||||
DilloImage *Image;
|
||||
|
||||
Image = dNew(DilloImage, 1);
|
||||
Image->layout = layout;
|
||||
Image->img_rndr = img_rndr;
|
||||
Image->width = 0;
|
||||
Image->height = 0;
|
||||
Image->dpi = ((Layout *) layout)->dpiX();
|
||||
Image->bg_color = bg_color;
|
||||
Image->fg_color = fg_color;
|
||||
Image->ScanNumber = 0;
|
||||
Image->BitVec = NULL;
|
||||
Image->State = IMG_Empty;
|
||||
|
||||
Image->RefCount = 0;
|
||||
|
||||
return Image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and initialize a new image structure with an image widget.
|
||||
*/
|
||||
DilloImage *a_Image_new_with_dw(void *layout, const char *alt_text,
|
||||
int32_t bg_color, int32_t fg_color)
|
||||
{
|
||||
dw::Image *dw = new dw::Image(alt_text);
|
||||
return a_Image_new(layout, (void*)(dw::core::ImgRenderer*)dw, bg_color, fg_color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the image renderer as a widget. This is somewhat tricky,
|
||||
* since simple casting leads to wrong (and hard to debug) results,
|
||||
* because of multiple inheritance. This function can be used from C
|
||||
* code, where only access to void* is possible.
|
||||
*/
|
||||
void *a_Image_get_dw(DilloImage *Image)
|
||||
{
|
||||
return (dw::Image*)(dw::core::ImgRenderer*)Image->img_rndr;
|
||||
}
|
||||
/**
|
||||
* Deallocate an Image structure
|
||||
*/
|
||||
static void Image_free(DilloImage *Image)
|
||||
{
|
||||
a_Bitvec_free(Image->BitVec);
|
||||
dFree(Image);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unref and free if necessary
|
||||
* Do nothing if the argument is NULL
|
||||
*/
|
||||
void a_Image_unref(DilloImage *Image)
|
||||
{
|
||||
_MSG(" %d ", Image->RefCount);
|
||||
if (Image && --Image->RefCount == 0)
|
||||
Image_free(Image);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a reference to an Image struct
|
||||
* Do nothing if the argument is NULL
|
||||
*/
|
||||
void a_Image_ref(DilloImage *Image)
|
||||
{
|
||||
if (Image)
|
||||
++Image->RefCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set initial parameters of the image
|
||||
*/
|
||||
void a_Image_set_parms(DilloImage *Image, void *v_imgbuf, DilloUrl *url,
|
||||
int version, uint_t width, uint_t height,
|
||||
DilloImgType type)
|
||||
{
|
||||
_MSG("a_Image_set_parms: width=%d height=%d iw=%d ih=%d\n",
|
||||
width, height, Image->width, Image->height);
|
||||
|
||||
/* Resize from 0,0 to width,height */
|
||||
bool resize = true;
|
||||
I2IR(Image)->setBuffer((Imgbuf*)v_imgbuf, resize);
|
||||
|
||||
if (!Image->BitVec)
|
||||
Image->BitVec = a_Bitvec_new(height);
|
||||
Image->width = width;
|
||||
Image->height = height;
|
||||
Image->State = IMG_SetParms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement the write method
|
||||
*/
|
||||
void a_Image_write(DilloImage *Image, uint_t y)
|
||||
{
|
||||
_MSG("a_Image_write\n");
|
||||
dReturn_if_fail ( y < Image->height );
|
||||
|
||||
/* Update the row in DwImage */
|
||||
I2IR(Image)->drawRow(y);
|
||||
a_Bitvec_set_bit(Image->BitVec, y);
|
||||
Image->State = IMG_Write;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement the close method
|
||||
*/
|
||||
void a_Image_close(DilloImage *Image)
|
||||
{
|
||||
_MSG("a_Image_close\n");
|
||||
I2IR(Image)->finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement the abort method
|
||||
*/
|
||||
void a_Image_abort(DilloImage *Image)
|
||||
{
|
||||
_MSG("a_Image_abort\n");
|
||||
I2IR(Image)->fatal();
|
||||
}
|
||||
|
||||
102
src/image.hh
Normal file
102
src/image.hh
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* File: image.hh
|
||||
*
|
||||
* Copyright (C) 2005-2007 Jorge Arellano Cid <jcid@dillo.org>,
|
||||
* Sebastian Geerken <sgeerken@dillo.org>
|
||||
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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 __IMAGE_HH__
|
||||
#define __IMAGE_HH__
|
||||
|
||||
/** @file
|
||||
* The DilloImage data-structure and methods */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#include "bitvec.h"
|
||||
#include "url.h"
|
||||
|
||||
/*
|
||||
* Defines
|
||||
*/
|
||||
|
||||
/** Arbitrary maximum for image size. To avoid image size-crafting attacks. */
|
||||
#define IMAGE_MAX_AREA (6000 * 6000)
|
||||
|
||||
/*
|
||||
* Types
|
||||
*/
|
||||
|
||||
typedef struct _DilloImage DilloImage;
|
||||
|
||||
typedef enum {
|
||||
DILLO_IMG_TYPE_INDEXED,
|
||||
DILLO_IMG_TYPE_RGB,
|
||||
DILLO_IMG_TYPE_GRAY,
|
||||
DILLO_IMG_TYPE_CMYK_INV,
|
||||
DILLO_IMG_TYPE_NOTSET /* Initial value */
|
||||
} DilloImgType;
|
||||
|
||||
/* These will reflect the Image's "state" */
|
||||
typedef enum {
|
||||
IMG_Empty, /**< Just created the entry */
|
||||
IMG_SetParms, /**< Parameters set */
|
||||
IMG_SetCmap, /**< Color map set */
|
||||
IMG_Write, /**< Feeding the entry */
|
||||
IMG_Close, /**< Whole image got! */
|
||||
IMG_Abort /**< Image transfer aborted */
|
||||
} ImageState;
|
||||
|
||||
struct _DilloImage {
|
||||
void *layout, *img_rndr;
|
||||
|
||||
/* Parameters as told by image data */
|
||||
uint_t width;
|
||||
uint_t height;
|
||||
|
||||
float dpi; /**< Dots per inch */
|
||||
int32_t bg_color; /**< Background color */
|
||||
int32_t fg_color; /**< Foreground color */
|
||||
bitvec_t *BitVec; /**< Bit vector for decoded rows */
|
||||
uint_t ScanNumber; /**< Current decoding scan */
|
||||
ImageState State; /**< Processing status */
|
||||
|
||||
int RefCount; /**< Reference counter */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Function prototypes
|
||||
*/
|
||||
DilloImage *a_Image_new(void *layout, void *img_rndr,
|
||||
int32_t bg_color, int32_t fg_color);
|
||||
DilloImage *a_Image_new_with_dw(void *layout, const char *alt_text,
|
||||
int32_t bg_color, int32_t fg_color);
|
||||
void *a_Image_get_dw(DilloImage *Image);
|
||||
void a_Image_ref(DilloImage *Image);
|
||||
void a_Image_unref(DilloImage *Image);
|
||||
|
||||
void a_Image_set_parms(DilloImage *Image, void *v_imgbuf, DilloUrl *url,
|
||||
int version, uint_t width, uint_t height,
|
||||
DilloImgType type);
|
||||
void a_Image_write(DilloImage *Image, uint_t y);
|
||||
void a_Image_close(DilloImage *Image);
|
||||
void a_Image_abort(DilloImage *Image);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __IMAGE_HH__ */
|
||||
|
||||
139
src/imgbuf.cc
Normal file
139
src/imgbuf.cc
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* File: imgbuf.cc
|
||||
*
|
||||
* Copyright (C) 2008 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.
|
||||
*/
|
||||
|
||||
#include "msg.h"
|
||||
#include "imgbuf.hh"
|
||||
#include "dw/core.hh"
|
||||
#include "dw/image.hh"
|
||||
|
||||
using namespace dw::core;
|
||||
|
||||
/*
|
||||
* Local data
|
||||
*/
|
||||
static size_t linebuf_size = 0;
|
||||
static uchar_t *linebuf = NULL;
|
||||
|
||||
|
||||
/*
|
||||
* Decode 'buf' (an image line) into RGB format.
|
||||
*/
|
||||
static uchar_t *Imgbuf_rgb_line(const uchar_t *buf,
|
||||
DilloImgType type, uchar_t *cmap,
|
||||
uint_t width, uint_t y)
|
||||
{
|
||||
uint_t x;
|
||||
|
||||
switch (type) {
|
||||
case DILLO_IMG_TYPE_INDEXED:
|
||||
if (cmap) {
|
||||
for (x = 0; x < width; x++)
|
||||
memcpy(linebuf + x * 3, cmap + buf[x] * 3, 3);
|
||||
} else {
|
||||
MSG_WARN("Gif:: image lacks a color map\n");
|
||||
}
|
||||
break;
|
||||
case DILLO_IMG_TYPE_GRAY:
|
||||
for (x = 0; x < width; x++)
|
||||
memset(linebuf + x * 3, buf[x], 3);
|
||||
break;
|
||||
case DILLO_IMG_TYPE_CMYK_INV:
|
||||
/*
|
||||
* We treat CMYK as if it were "RGBW", and it works. Everyone who is
|
||||
* trying to handle CMYK jpegs is confused by this, and supposedly
|
||||
* the issue is that Adobe CMYK is "wrong" but ubiquitous.
|
||||
*/
|
||||
for (x = 0; x < width; x++) {
|
||||
uint_t white = buf[x * 4 + 3];
|
||||
linebuf[x * 3] = buf[x * 4] * white / 0x100;
|
||||
linebuf[x * 3 + 1] = buf[x * 4 + 1] * white / 0x100;
|
||||
linebuf[x * 3 + 2] = buf[x * 4 + 2] * white / 0x100;
|
||||
}
|
||||
break;
|
||||
case DILLO_IMG_TYPE_RGB:
|
||||
/* avoid a memcpy here! --Jcid */
|
||||
return (uchar_t *)buf;
|
||||
case DILLO_IMG_TYPE_NOTSET:
|
||||
MSG_ERR("Imgbuf_rgb_line: type not set...\n");
|
||||
break;
|
||||
}
|
||||
return linebuf;
|
||||
}
|
||||
|
||||
// Wrappers for Imgbuf -------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Increment reference count for an Imgbuf
|
||||
*/
|
||||
void a_Imgbuf_ref(void *v_imgbuf)
|
||||
{
|
||||
((Imgbuf*)v_imgbuf)->ref();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement reference count for an Imgbuf
|
||||
*/
|
||||
void a_Imgbuf_unref(void *v_imgbuf)
|
||||
{
|
||||
if (v_imgbuf)
|
||||
((Imgbuf*)v_imgbuf)->unref();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Imgbuf
|
||||
*/
|
||||
void *a_Imgbuf_new(void *layout, int img_type, uint_t width, uint_t height,
|
||||
double gamma)
|
||||
{
|
||||
if (!layout) {
|
||||
MSG_ERR("a_Imgbuf_new: layout is NULL.\n");
|
||||
exit(1);
|
||||
}
|
||||
// Assert linebuf is wide enough.
|
||||
if (3 * width > linebuf_size) {
|
||||
linebuf_size = 3 * width;
|
||||
linebuf = (uchar_t*) dRealloc(linebuf, linebuf_size);
|
||||
}
|
||||
|
||||
return (void*)((Layout*)layout)->createImgbuf(Imgbuf::RGB, width, height,
|
||||
gamma);
|
||||
}
|
||||
|
||||
/**
|
||||
* Last reference for this Imgbuf?
|
||||
*/
|
||||
int a_Imgbuf_last_reference(void *v_imgbuf)
|
||||
{
|
||||
return ((Imgbuf*)v_imgbuf)->lastReference () ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the root buffer of an imgbuf.
|
||||
*/
|
||||
void a_Imgbuf_update(void *v_imgbuf, const uchar_t *buf, DilloImgType type,
|
||||
uchar_t *cmap, uint_t width, uint_t height, uint_t y)
|
||||
|
||||
{
|
||||
dReturn_if_fail ( y < height );
|
||||
|
||||
/* Decode 'buf' and copy it into the imgbuf */
|
||||
uchar_t *newbuf = Imgbuf_rgb_line(buf, type, cmap, width, y);
|
||||
((Imgbuf*)v_imgbuf)->copyRow(y, (byte *)newbuf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset for a new scan from a multiple-scan image.
|
||||
*/
|
||||
void a_Imgbuf_new_scan(void *v_imgbuf)
|
||||
{
|
||||
((Imgbuf*)v_imgbuf)->newScan();
|
||||
}
|
||||
|
||||
31
src/imgbuf.hh
Normal file
31
src/imgbuf.hh
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef __IMGBUF_HH__
|
||||
#define __IMGBUF_HH__
|
||||
|
||||
// Imgbuf wrappers
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#include "image.hh"
|
||||
|
||||
/*
|
||||
* Function prototypes
|
||||
*/
|
||||
void a_Imgbuf_ref(void *v_imgbuf);
|
||||
void a_Imgbuf_unref(void *v_imgbuf);
|
||||
void *a_Imgbuf_new(void *v_ir, int img_type, uint_t width, uint_t height,
|
||||
double gamma);
|
||||
int a_Imgbuf_last_reference(void *v_imgbuf);
|
||||
void a_Imgbuf_update(void *v_imgbuf, const uchar_t *buf, DilloImgType type,
|
||||
uchar_t *cmap, uint_t width, uint_t height, uint_t y);
|
||||
void a_Imgbuf_new_scan(void *v_imgbuf);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __IMGBUF_HH__ */
|
||||
|
||||
426
src/jpeg.c
Normal file
426
src/jpeg.c
Normal file
@ -0,0 +1,426 @@
|
||||
/*
|
||||
* File: jpeg.c
|
||||
*
|
||||
* Copyright (C) 2000-2007 Jorge Arellano Cid <jcid@dillo.org>
|
||||
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* The jpeg decoder for dillo. It is responsible for decoding JPEG data
|
||||
* and transferring it to the dicache. It uses libjpeg to do the actual
|
||||
* decoding.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#ifdef ENABLE_JPEG
|
||||
|
||||
#include <stdio.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
/* avoid a redefinition of HAVE_STDLIB_H with old jpeglib.h */
|
||||
#ifdef HAVE_STDLIB_H
|
||||
# undef HAVE_STDLIB_H
|
||||
#endif
|
||||
#include <jpeglib.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
# undef HAVE_STDLIB_H
|
||||
#endif
|
||||
|
||||
#include "image.hh"
|
||||
#include "cache.h"
|
||||
#include "dicache.h"
|
||||
#include "capi.h" /* get cache entry status */
|
||||
#include "msg.h"
|
||||
|
||||
typedef enum {
|
||||
DILLO_JPEG_INIT,
|
||||
DILLO_JPEG_STARTING,
|
||||
DILLO_JPEG_READ_BEGIN_SCAN,
|
||||
DILLO_JPEG_READ_IN_SCAN,
|
||||
DILLO_JPEG_READ_END_SCAN,
|
||||
DILLO_JPEG_DONE,
|
||||
DILLO_JPEG_ERROR
|
||||
} DilloJpegState;
|
||||
|
||||
typedef struct DilloJpeg DilloJpeg;
|
||||
|
||||
/* An implementation of a suspending source manager */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_source_mgr pub; /**< public fields */
|
||||
DilloJpeg *jpeg; /**< a pointer back to the jpeg object */
|
||||
} my_source_mgr;
|
||||
|
||||
struct my_error_mgr {
|
||||
struct jpeg_error_mgr pub; /**< "public" fields */
|
||||
jmp_buf setjmp_buffer; /**< for return to caller */
|
||||
};
|
||||
typedef struct my_error_mgr *my_error_ptr;
|
||||
|
||||
struct DilloJpeg {
|
||||
DilloImage *Image;
|
||||
DilloUrl *url;
|
||||
int version;
|
||||
|
||||
my_source_mgr Src;
|
||||
|
||||
DilloJpegState state;
|
||||
size_t Start_Ofs, Skip, NewStart;
|
||||
char *Data;
|
||||
|
||||
uint_t y;
|
||||
|
||||
struct jpeg_decompress_struct cinfo;
|
||||
struct my_error_mgr jerr;
|
||||
};
|
||||
|
||||
/*
|
||||
* Forward declarations
|
||||
*/
|
||||
static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize);
|
||||
|
||||
|
||||
/** Routine called by libjpeg when it detects an error. */
|
||||
METHODDEF(void) Jpeg_errorexit (j_common_ptr cinfo)
|
||||
{
|
||||
/* display message and return to setjmp buffer */
|
||||
my_error_ptr myerr = (my_error_ptr) cinfo->err;
|
||||
if (prefs.show_msg) {
|
||||
DilloJpeg *jpeg =
|
||||
((my_source_mgr *) ((j_decompress_ptr) cinfo)->src)->jpeg;
|
||||
MSG_WARN("\"%s\": ", URL_STR(jpeg->url));
|
||||
(*cinfo->err->output_message) (cinfo);
|
||||
}
|
||||
longjmp(myerr->setjmp_buffer, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the jpeg-decoding data structure.
|
||||
*/
|
||||
static void Jpeg_free(DilloJpeg *jpeg)
|
||||
{
|
||||
_MSG("Jpeg_free: jpeg=%p\n", jpeg);
|
||||
jpeg_destroy_decompress(&(jpeg->cinfo));
|
||||
dFree(jpeg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish the decoding process
|
||||
*/
|
||||
static void Jpeg_close(DilloJpeg *jpeg, CacheClient_t *Client)
|
||||
{
|
||||
_MSG("Jpeg_close\n");
|
||||
a_Dicache_close(jpeg->url, jpeg->version, Client);
|
||||
Jpeg_free(jpeg);
|
||||
}
|
||||
|
||||
static void init_source(struct jpeg_decompress_struct *p)
|
||||
{
|
||||
(void) p; /* unused */
|
||||
}
|
||||
|
||||
static boolean fill_input_buffer(j_decompress_ptr cinfo)
|
||||
{
|
||||
DilloJpeg *jpeg = ((my_source_mgr *) cinfo->src)->jpeg;
|
||||
|
||||
_MSG("fill_input_buffer\n");
|
||||
#if 0
|
||||
if (!cinfo->src->bytes_in_buffer) {
|
||||
_MSG("fill_input_buffer: %ld bytes in buffer\n",
|
||||
(long)cinfo->src->bytes_in_buffer);
|
||||
|
||||
jpeg->Start_Ofs = (ulong_t) jpeg->cinfo.src->next_input_byte -
|
||||
(ulong_t) jpeg->Data;
|
||||
#endif
|
||||
if (jpeg->Skip) {
|
||||
jpeg->Start_Ofs = jpeg->NewStart + jpeg->Skip - 1;
|
||||
jpeg->Skip = 0;
|
||||
} else {
|
||||
jpeg->Start_Ofs = (ulong_t) jpeg->cinfo.src->next_input_byte -
|
||||
(ulong_t) jpeg->Data;
|
||||
}
|
||||
return FALSE;
|
||||
#if 0
|
||||
}
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
|
||||
{
|
||||
DilloJpeg *jpeg;
|
||||
|
||||
if (num_bytes < 1)
|
||||
return;
|
||||
jpeg = ((my_source_mgr *) cinfo->src)->jpeg;
|
||||
|
||||
_MSG("skip_input_data: Start_Ofs = %lu, num_bytes = %ld,"
|
||||
" %ld bytes in buffer\n",
|
||||
(ulong_t)jpeg->Start_Ofs, num_bytes,(long)cinfo->src->bytes_in_buffer);
|
||||
|
||||
cinfo->src->next_input_byte += num_bytes;
|
||||
if (num_bytes < (long)cinfo->src->bytes_in_buffer) {
|
||||
cinfo->src->bytes_in_buffer -= num_bytes;
|
||||
} else {
|
||||
jpeg->Skip += num_bytes - cinfo->src->bytes_in_buffer + 1;
|
||||
cinfo->src->bytes_in_buffer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The proper signature is:
|
||||
* static void term_source(j_decompress_ptr cinfo)
|
||||
* (declaring it with no parameter avoids a compiler warning)
|
||||
*/
|
||||
static void term_source(struct jpeg_decompress_struct *p)
|
||||
{
|
||||
(void) p; /* unused */
|
||||
}
|
||||
|
||||
void *a_Jpeg_new(DilloImage *Image, DilloUrl *url, int version)
|
||||
{
|
||||
my_source_mgr *src;
|
||||
DilloJpeg *jpeg = dMalloc(sizeof(*jpeg));
|
||||
_MSG("a_Jpeg_new: jpeg=%p\n", jpeg);
|
||||
|
||||
jpeg->Image = Image;
|
||||
jpeg->url = url;
|
||||
jpeg->version = version;
|
||||
|
||||
jpeg->state = DILLO_JPEG_INIT;
|
||||
jpeg->Start_Ofs = 0;
|
||||
jpeg->Skip = 0;
|
||||
|
||||
/* decompression step 1 (see libjpeg.doc) */
|
||||
jpeg->cinfo.err = jpeg_std_error(&(jpeg->jerr.pub));
|
||||
jpeg->jerr.pub.error_exit = Jpeg_errorexit;
|
||||
|
||||
jpeg_create_decompress(&(jpeg->cinfo));
|
||||
|
||||
/* decompression step 2 (see libjpeg.doc) */
|
||||
jpeg->cinfo.src = &jpeg->Src.pub;
|
||||
src = &jpeg->Src;
|
||||
src->pub.init_source = init_source;
|
||||
src->pub.fill_input_buffer = fill_input_buffer;
|
||||
src->pub.skip_input_data = skip_input_data;
|
||||
src->pub.resync_to_restart = jpeg_resync_to_restart;/* use default method */
|
||||
src->pub.term_source = term_source;
|
||||
src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
|
||||
src->pub.next_input_byte = NULL;/* until buffer loaded */
|
||||
|
||||
src->jpeg = jpeg;
|
||||
|
||||
/* decompression steps continue in write method */
|
||||
return jpeg;
|
||||
}
|
||||
|
||||
void a_Jpeg_callback(int Op, void *data)
|
||||
{
|
||||
if (Op == CA_Send) {
|
||||
CacheClient_t *Client = data;
|
||||
Jpeg_write(Client->CbData, Client->Buf, Client->BufSize);
|
||||
} else if (Op == CA_Close) {
|
||||
CacheClient_t *Client = data;
|
||||
Jpeg_close(Client->CbData, Client);
|
||||
} else if (Op == CA_Abort) {
|
||||
Jpeg_free(data);
|
||||
}
|
||||
}
|
||||
|
||||
const char *a_Jpeg_version(void)
|
||||
{
|
||||
#define QUOTE(x) #x
|
||||
#define STR(x) QUOTE(x)
|
||||
|
||||
#if defined(LIBJPEG_TURBO_VERSION)
|
||||
return STR(LIBJPEG_TURBO_VERSION);
|
||||
#else
|
||||
return STR(JPEG_LIB_VERSION);
|
||||
#endif
|
||||
|
||||
#undef STR
|
||||
#undef QUOTE
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive and process new chunks of JPEG image data
|
||||
*/
|
||||
static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize)
|
||||
{
|
||||
DilloImgType type;
|
||||
uchar_t *linebuf;
|
||||
JSAMPLE *array[1];
|
||||
int num_read;
|
||||
|
||||
_MSG("Jpeg_write: (%p) Bytes in buff: %ld Ofs: %lu\n", jpeg,
|
||||
(long) BufSize, (ulong_t)jpeg->Start_Ofs);
|
||||
|
||||
/* See if we are supposed to skip ahead. */
|
||||
if (BufSize <= jpeg->Start_Ofs)
|
||||
return;
|
||||
|
||||
/* Concatenate with the partial input, if any. */
|
||||
jpeg->cinfo.src->next_input_byte = (uchar_t *)Buf + jpeg->Start_Ofs;
|
||||
jpeg->cinfo.src->bytes_in_buffer = BufSize - jpeg->Start_Ofs;
|
||||
jpeg->NewStart = BufSize;
|
||||
jpeg->Data = Buf;
|
||||
|
||||
if (setjmp(jpeg->jerr.setjmp_buffer)) {
|
||||
/* If we get here, the JPEG code has signaled an error. */
|
||||
jpeg->state = DILLO_JPEG_ERROR;
|
||||
}
|
||||
|
||||
/* Process the bytes in the input buffer. */
|
||||
if (jpeg->state == DILLO_JPEG_INIT) {
|
||||
|
||||
/* decompression step 3 (see libjpeg.doc) */
|
||||
if (jpeg_read_header(&(jpeg->cinfo), TRUE) != JPEG_SUSPENDED) {
|
||||
type = DILLO_IMG_TYPE_GRAY;
|
||||
if (jpeg->cinfo.num_components == 1) {
|
||||
type = DILLO_IMG_TYPE_GRAY;
|
||||
} else if (jpeg->cinfo.num_components == 3) {
|
||||
type = DILLO_IMG_TYPE_RGB;
|
||||
} else {
|
||||
if (jpeg->cinfo.jpeg_color_space == JCS_YCCK)
|
||||
MSG("YCCK JPEG. Are the colors wrong?\n");
|
||||
if (!jpeg->cinfo.saw_Adobe_marker)
|
||||
MSG("No adobe marker! Is the image shown in reverse video?\n");
|
||||
type = DILLO_IMG_TYPE_CMYK_INV;
|
||||
}
|
||||
/*
|
||||
* If a multiple-scan image is not completely in cache,
|
||||
* use progressive display, updating as it arrives.
|
||||
*/
|
||||
if (jpeg_has_multiple_scans(&jpeg->cinfo) &&
|
||||
!(a_Capi_get_flags(jpeg->url) & CAPI_Completed))
|
||||
jpeg->cinfo.buffered_image = TRUE;
|
||||
|
||||
/* check max image size */
|
||||
if (jpeg->cinfo.image_width <= 0 || jpeg->cinfo.image_height <= 0 ||
|
||||
jpeg->cinfo.image_width >
|
||||
IMAGE_MAX_AREA / jpeg->cinfo.image_height) {
|
||||
MSG("Jpeg_write: suspicious image size request %u x %u\n",
|
||||
(uint_t)jpeg->cinfo.image_width,
|
||||
(uint_t)jpeg->cinfo.image_height);
|
||||
jpeg->state = DILLO_JPEG_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
/** \todo Gamma for JPEG? */
|
||||
a_Dicache_set_parms(jpeg->url, jpeg->version, jpeg->Image,
|
||||
(uint_t)jpeg->cinfo.image_width,
|
||||
(uint_t)jpeg->cinfo.image_height,
|
||||
type, 1 / 2.2);
|
||||
jpeg->Image = NULL; /* safeguard: may be freed by its owner later */
|
||||
|
||||
/* decompression step 4 (see libjpeg.doc) */
|
||||
jpeg->state = DILLO_JPEG_STARTING;
|
||||
}
|
||||
}
|
||||
if (jpeg->state == DILLO_JPEG_STARTING) {
|
||||
/* decompression step 5 (see libjpeg.doc) */
|
||||
if (jpeg_start_decompress(&(jpeg->cinfo))) {
|
||||
jpeg->y = 0;
|
||||
jpeg->state = jpeg->cinfo.buffered_image ?
|
||||
DILLO_JPEG_READ_BEGIN_SCAN : DILLO_JPEG_READ_IN_SCAN;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A progressive jpeg contains multiple scans that can be used to display
|
||||
* an increasingly sharp image as it is being received. The reading of each
|
||||
* scan must be surrounded by jpeg_start_output()/jpeg_finish_output().
|
||||
*/
|
||||
|
||||
if (jpeg->state == DILLO_JPEG_READ_END_SCAN) {
|
||||
if (jpeg_finish_output(&jpeg->cinfo)) {
|
||||
if (jpeg_input_complete(&jpeg->cinfo)) {
|
||||
jpeg->state = DILLO_JPEG_DONE;
|
||||
} else {
|
||||
jpeg->state = DILLO_JPEG_READ_BEGIN_SCAN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (jpeg->state == DILLO_JPEG_READ_BEGIN_SCAN) {
|
||||
if (jpeg_start_output(&jpeg->cinfo, jpeg->cinfo.input_scan_number)) {
|
||||
a_Dicache_new_scan(jpeg->url, jpeg->version);
|
||||
jpeg->state = DILLO_JPEG_READ_IN_SCAN;
|
||||
}
|
||||
}
|
||||
|
||||
if (jpeg->state == DILLO_JPEG_READ_IN_SCAN) {
|
||||
linebuf = dMalloc(jpeg->cinfo.image_width *
|
||||
jpeg->cinfo.num_components);
|
||||
array[0] = linebuf;
|
||||
|
||||
while (1) {
|
||||
num_read = jpeg_read_scanlines(&(jpeg->cinfo), array, 1);
|
||||
if (num_read == 0) {
|
||||
/* out of input */
|
||||
break;
|
||||
}
|
||||
a_Dicache_write(jpeg->url, jpeg->version, linebuf, jpeg->y);
|
||||
|
||||
jpeg->y++;
|
||||
|
||||
if (jpeg->y == jpeg->cinfo.image_height) {
|
||||
/* end of scan */
|
||||
if (!jpeg->cinfo.buffered_image) {
|
||||
/* single scan */
|
||||
jpeg->state = DILLO_JPEG_DONE;
|
||||
break;
|
||||
} else {
|
||||
jpeg->y = 0;
|
||||
if (jpeg_input_complete(&jpeg->cinfo)) {
|
||||
if (jpeg->cinfo.input_scan_number ==
|
||||
jpeg->cinfo.output_scan_number) {
|
||||
jpeg->state = DILLO_JPEG_DONE;
|
||||
break;
|
||||
} else {
|
||||
/* one final loop through the scanlines */
|
||||
jpeg_finish_output(&jpeg->cinfo);
|
||||
jpeg_start_output(&jpeg->cinfo,
|
||||
jpeg->cinfo.input_scan_number);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
jpeg->state = DILLO_JPEG_READ_END_SCAN;
|
||||
if (!jpeg_finish_output(&jpeg->cinfo)) {
|
||||
/* out of input */
|
||||
break;
|
||||
} else {
|
||||
if (jpeg_input_complete(&jpeg->cinfo)) {
|
||||
jpeg->state = DILLO_JPEG_DONE;
|
||||
break;
|
||||
} else {
|
||||
jpeg->state = DILLO_JPEG_READ_BEGIN_SCAN;
|
||||
}
|
||||
}
|
||||
if (!jpeg_start_output(&jpeg->cinfo,
|
||||
jpeg->cinfo.input_scan_number)) {
|
||||
/* out of input */
|
||||
break;
|
||||
}
|
||||
a_Dicache_new_scan(jpeg->url, jpeg->version);
|
||||
jpeg->state = DILLO_JPEG_READ_IN_SCAN;
|
||||
}
|
||||
}
|
||||
}
|
||||
dFree(linebuf);
|
||||
}
|
||||
}
|
||||
|
||||
#else /* ENABLE_JPEG */
|
||||
|
||||
void *a_Jpeg_new() { return 0; }
|
||||
void a_Jpeg_callback() { return; }
|
||||
const char *a_Jpeg_version(void) { return 0; }
|
||||
|
||||
#endif /* ENABLE_JPEG */
|
||||
400
src/keys.cc
Normal file
400
src/keys.cc
Normal file
@ -0,0 +1,400 @@
|
||||
/*
|
||||
* Key parser
|
||||
*
|
||||
* Copyright (C) 2009 Jorge Arellano Cid <jcid@dillo.org>
|
||||
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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 <FL/Fl.H>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> /* strtol */
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "dlib/dlib.h"
|
||||
#include "keys.hh"
|
||||
#include "utf8.hh"
|
||||
#include "msg.h"
|
||||
|
||||
/*
|
||||
* Local data types
|
||||
*/
|
||||
typedef struct {
|
||||
const char *name;
|
||||
KeysCommand_t cmd;
|
||||
int modifier, key;
|
||||
} KeyBinding_t;
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const int value;
|
||||
} Mapping_t;
|
||||
|
||||
|
||||
/*
|
||||
* Local data
|
||||
*/
|
||||
static const Mapping_t keyNames[] = {
|
||||
{ "Backspace", FL_BackSpace },
|
||||
{ "Delete", FL_Delete },
|
||||
{ "Down", FL_Down },
|
||||
{ "End", FL_End },
|
||||
{ "Esc", FL_Escape },
|
||||
{ "F1", FL_F + 1 },
|
||||
{ "F2", FL_F + 2 },
|
||||
{ "F3", FL_F + 3 },
|
||||
{ "F4", FL_F + 4 },
|
||||
{ "F5", FL_F + 5 },
|
||||
{ "F6", FL_F + 6 },
|
||||
{ "F7", FL_F + 7 },
|
||||
{ "F8", FL_F + 8 },
|
||||
{ "F9", FL_F + 9 },
|
||||
{ "F10", FL_F + 10 },
|
||||
{ "F11", FL_F + 11 },
|
||||
{ "F12", FL_F + 12 },
|
||||
{ "Home", FL_Home },
|
||||
{ "Insert", FL_Insert },
|
||||
{ "Left", FL_Left },
|
||||
{ "Menu", FL_Menu },
|
||||
{ "PageDown", FL_Page_Down },
|
||||
{ "PageUp", FL_Page_Up },
|
||||
{ "Print", FL_Print },
|
||||
{ "Return", FL_Enter },
|
||||
{ "Right", FL_Right },
|
||||
{ "Space", ' ' },
|
||||
{ "Tab", FL_Tab },
|
||||
{ "Up", FL_Up },
|
||||
/* multimedia keys */
|
||||
{ "Back", FL_Back },
|
||||
{ "Favorites", FL_Favorites },
|
||||
{ "Forward", FL_Forward },
|
||||
{ "HomePage", FL_Home_Page },
|
||||
{ "Mail", FL_Mail },
|
||||
{ "MediaNext", FL_Media_Next },
|
||||
{ "MediaPlay", FL_Media_Play },
|
||||
{ "MediaPrev", FL_Media_Prev },
|
||||
{ "MediaStop", FL_Media_Stop },
|
||||
{ "Refresh", FL_Refresh },
|
||||
{ "Search", FL_Search },
|
||||
{ "Sleep", FL_Sleep },
|
||||
{ "Stop", FL_Stop },
|
||||
{ "VolumeDown", FL_Volume_Down },
|
||||
{ "VolumeMute", FL_Volume_Mute },
|
||||
{ "VolumeUp", FL_Volume_Up },
|
||||
};
|
||||
|
||||
static const Mapping_t modifierNames[] = {
|
||||
{ "Shift", FL_SHIFT },
|
||||
{ "Ctrl", FL_CTRL },
|
||||
{ "Alt", FL_ALT },
|
||||
{ "Meta", FL_META },
|
||||
{ "Button1", FL_BUTTON1 },
|
||||
{ "Button2", FL_BUTTON2 },
|
||||
{ "Button3", FL_BUTTON3 }
|
||||
};
|
||||
|
||||
static const KeyBinding_t default_keys[] = {
|
||||
{ "nop" , KEYS_NOP , 0 , 0 },
|
||||
{ "open" , KEYS_OPEN , FL_CTRL , 'o' },
|
||||
{ "new-window" , KEYS_NEW_WINDOW , FL_CTRL , 'n' },
|
||||
{ "new-tab" , KEYS_NEW_TAB , FL_CTRL , 't' },
|
||||
{ "left-tab" , KEYS_LEFT_TAB , FL_CTRL |
|
||||
FL_SHIFT , FL_Tab },
|
||||
{ "left-tab" , KEYS_LEFT_TAB , FL_CTRL , FL_Page_Up },
|
||||
{ "right-tab" , KEYS_RIGHT_TAB , FL_CTRL , FL_Tab },
|
||||
{ "right-tab" , KEYS_RIGHT_TAB , FL_CTRL , FL_Page_Down },
|
||||
{ "close-tab" , KEYS_CLOSE_TAB , FL_CTRL , 'w' },
|
||||
{ "find" , KEYS_FIND , FL_CTRL , 'f' },
|
||||
{ "websearch" , KEYS_WEBSEARCH , FL_CTRL , 's' },
|
||||
{ "bookmarks" , KEYS_BOOKMARKS , FL_CTRL , 'b' },
|
||||
{ "reload" , KEYS_RELOAD , FL_CTRL , 'r' },
|
||||
{ "stop" , KEYS_STOP , 0 , 0 },
|
||||
{ "save" , KEYS_SAVE , 0 , 0 },
|
||||
{ "hide-panels" , KEYS_HIDE_PANELS , 0 , FL_Escape },
|
||||
{ "file-menu" , KEYS_FILE_MENU , FL_ALT , 'f' },
|
||||
{ "close-all" , KEYS_CLOSE_ALL , FL_CTRL , 'q' },
|
||||
{ "back" , KEYS_BACK , 0 , FL_BackSpace },
|
||||
{ "back" , KEYS_BACK , 0 , ',' },
|
||||
{ "forward" , KEYS_FORWARD , FL_SHIFT , FL_BackSpace },
|
||||
{ "forward" , KEYS_FORWARD , 0 , '.' },
|
||||
{ "goto" , KEYS_GOTO , FL_CTRL , 'l' },
|
||||
{ "home" , KEYS_HOME , FL_CTRL , 'h' },
|
||||
{ "view-source" , KEYS_VIEW_SOURCE , FL_CTRL , 'u' },
|
||||
{ "screen-up" , KEYS_SCREEN_UP , 0 , FL_Page_Up },
|
||||
{ "screen-up" , KEYS_SCREEN_UP , 0 , 'b' },
|
||||
{ "screen-down" , KEYS_SCREEN_DOWN , 0 , FL_Page_Down },
|
||||
{ "screen-down" , KEYS_SCREEN_DOWN , 0 , ' ' },
|
||||
{ "screen-left" , KEYS_SCREEN_LEFT , 0 , 0 },
|
||||
{ "screen-right" , KEYS_SCREEN_RIGHT , 0 , 0 },
|
||||
{ "line-up" , KEYS_LINE_UP , 0 , FL_Up },
|
||||
{ "line-down" , KEYS_LINE_DOWN , 0 , FL_Down },
|
||||
{ "left" , KEYS_LEFT , 0 , FL_Left },
|
||||
{ "right" , KEYS_RIGHT , 0 , FL_Right },
|
||||
{ "top" , KEYS_TOP , 0 , FL_Home },
|
||||
{ "bottom" , KEYS_BOTTOM , 0 , FL_End },
|
||||
{ "zoom-in" , KEYS_ZOOM_IN , FL_CTRL , '+' },
|
||||
{ "zoom-in" , KEYS_ZOOM_IN , FL_CTRL , '=' /* US + */ },
|
||||
{ "zoom-out" , KEYS_ZOOM_OUT , FL_CTRL , '-' },
|
||||
{ "zoom-reset" , KEYS_ZOOM_RESET , FL_CTRL , '0' },
|
||||
};
|
||||
|
||||
static Dlist *bindings;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the bindings list
|
||||
*/
|
||||
void Keys::init()
|
||||
{
|
||||
KeyBinding_t *node;
|
||||
|
||||
// Fill our key bindings list
|
||||
bindings = dList_new(32);
|
||||
for (uint_t i = 0; i < sizeof(default_keys) / sizeof(default_keys[0]); i++) {
|
||||
if (default_keys[i].key) {
|
||||
node = dNew(KeyBinding_t, 1);
|
||||
node->name = dStrdup(default_keys[i].name);
|
||||
node->cmd = default_keys[i].cmd;
|
||||
node->modifier = default_keys[i].modifier;
|
||||
node->key = default_keys[i].key;
|
||||
dList_insert_sorted(bindings, node, nodeByKeyCmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Free data
|
||||
*/
|
||||
void Keys::free()
|
||||
{
|
||||
KeyBinding_t *node;
|
||||
|
||||
while ((node = (KeyBinding_t*)dList_nth_data(bindings, 0))) {
|
||||
dFree((char*)node->name);
|
||||
dList_remove_fast(bindings, node);
|
||||
dFree(node);
|
||||
}
|
||||
dList_free(bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare function by {key,modifier} pairs.
|
||||
*/
|
||||
int Keys::nodeByKeyCmp(const void *node, const void *key)
|
||||
{
|
||||
KeyBinding_t *n = (KeyBinding_t*)node, *k = (KeyBinding_t*)key;
|
||||
_MSG("Keys::nodeByKeyCmp modifier=%d\n", k->modifier);
|
||||
return (n->key != k->key) ? (n->key - k->key) : (n->modifier - k->modifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Look if the just pressed key is bound to a command.
|
||||
* Return value: The command if found, KEYS_NOP otherwise.
|
||||
*/
|
||||
KeysCommand_t Keys::getKeyCmd()
|
||||
{
|
||||
KeysCommand_t ret = KEYS_NOP;
|
||||
KeyBinding_t keyNode;
|
||||
|
||||
keyNode.modifier = Fl::event_state() & (FL_SHIFT | FL_CTRL |FL_ALT|FL_META);
|
||||
if (iscntrl(Fl::event_text()[0])) {
|
||||
keyNode.key = Fl::event_key();
|
||||
} else {
|
||||
const char *beyond = Fl::event_text() + Fl::event_length();
|
||||
keyNode.key = a_Utf8_decode(Fl::event_text(), beyond, NULL);
|
||||
|
||||
/* BUG: The idea is to drop the modifiers if their use results in a
|
||||
* different character (e.g., if shift-8 gives '*', drop the shift,
|
||||
* but if ctrl-6 gives '6', keep the ctrl), but we have to compare a
|
||||
* keysym with a Unicode codepoint, which only works for characters
|
||||
* below U+0100 (those known to latin-1).
|
||||
*/
|
||||
if (keyNode.key != Fl::event_key())
|
||||
keyNode.modifier = 0;
|
||||
}
|
||||
_MSG("getKeyCmd: evkey=0x%x evtext=\'%s\' key=0x%x, mod=0x%x\n",
|
||||
Fl::event_key(), Fl::event_text(), keyNode.key, keyNode.modifier);
|
||||
void *data = dList_find_sorted(bindings, &keyNode, nodeByKeyCmp);
|
||||
if (data)
|
||||
ret = ((KeyBinding_t*)data)->cmd;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a key binding from the table.
|
||||
*/
|
||||
void Keys::delKeyCmd(int key, int mod)
|
||||
{
|
||||
KeyBinding_t keyNode, *node;
|
||||
keyNode.key = key;
|
||||
keyNode.modifier = mod;
|
||||
|
||||
node = (KeyBinding_t*) dList_find_sorted(bindings, &keyNode, nodeByKeyCmp);
|
||||
if (node) {
|
||||
dList_remove(bindings, node);
|
||||
dFree((char*)node->name);
|
||||
dFree(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a key name and looks it up in the mapping table. If
|
||||
* found, its key code is returned. Otherwise -1 is given
|
||||
* back.
|
||||
*/
|
||||
int Keys::getKeyCode(char *keyName)
|
||||
{
|
||||
uint_t i;
|
||||
for (i = 0; i < sizeof(keyNames) / sizeof(keyNames[0]); i++) {
|
||||
if (!dStrAsciiCasecmp(keyNames[i].name, keyName)) {
|
||||
return keyNames[i].value;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a command name and searches it in the mapping table.
|
||||
* Return value: command code if found, -1 otherwise
|
||||
*/
|
||||
KeysCommand_t Keys::getCmdCode(const char *commandName)
|
||||
{
|
||||
uint_t i;
|
||||
|
||||
for (i = 0; i < sizeof(default_keys) / sizeof(KeyBinding_t); i++) {
|
||||
if (!dStrAsciiCasecmp(default_keys[i].name, commandName))
|
||||
return default_keys[i].cmd;
|
||||
}
|
||||
return KEYS_INVALID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a modifier name and looks it up in the mapping table. If
|
||||
* found, its key code is returned. Otherwise -1 is given back.
|
||||
*/
|
||||
int Keys::getModifier(char *modifierName)
|
||||
{
|
||||
uint_t i;
|
||||
for (i = 0; i < sizeof(modifierNames) / sizeof(modifierNames[0]); i++) {
|
||||
if (!dStrAsciiCasecmp(modifierNames[i].name, modifierName)) {
|
||||
return modifierNames[i].value;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a keys command, return a shortcut for it, or 0 if there is none
|
||||
* (e.g., for KEYS_NEW_WINDOW, return CTRL+'n').
|
||||
*/
|
||||
int Keys::getShortcut(KeysCommand_t cmd)
|
||||
{
|
||||
int len = dList_length(bindings);
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
KeyBinding_t *node = (KeyBinding_t*)dList_nth_data(bindings, i);
|
||||
if (cmd == node->cmd)
|
||||
return node->modifier + node->key;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a key-combination/command-name pair, and
|
||||
* insert it into the bindings list.
|
||||
*/
|
||||
void Keys::parseKey(char *key, char *commandName)
|
||||
{
|
||||
char *p, *modstr, *keystr;
|
||||
KeysCommand_t symcode;
|
||||
int st, keymod = 0, keycode = 0;
|
||||
|
||||
_MSG("Keys::parseKey key='%s' commandName='%s'\n", key, commandName);
|
||||
|
||||
// Get command code
|
||||
if ((symcode = getCmdCode(commandName)) == KEYS_INVALID) {
|
||||
MSG("Keys::parseKey: Invalid command name: '%s'\n", commandName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip space
|
||||
for ( ; isspace(*key); ++key) ;
|
||||
// Get modifiers
|
||||
while(*key == '<' && (p = strchr(key, '>'))) {
|
||||
++key;
|
||||
modstr = dStrndup(key, p - key);
|
||||
if ((st = getModifier(modstr)) == -1) {
|
||||
MSG("Keys::parseKey unknown modifier: %s\n", modstr);
|
||||
} else {
|
||||
keymod |= st;
|
||||
}
|
||||
dFree(modstr);
|
||||
key = p + 1;
|
||||
}
|
||||
// Allow trailing space after keyname
|
||||
keystr = (*key && (p = strchr(key + 1, ' '))) ? dStrndup(key, p - key - 1) :
|
||||
dStrdup(key);
|
||||
// Get key code
|
||||
if (!key[1]) {
|
||||
keycode = *key;
|
||||
} else if (a_Utf8_char_count(keystr, strlen(keystr)) == 1) {
|
||||
const char *beyond = keystr + strlen(keystr);
|
||||
keycode = a_Utf8_decode(keystr, beyond, NULL);
|
||||
} else if (key[0] == '0' && key[1] == 'x') {
|
||||
/* keysym */
|
||||
keycode = strtol(key, NULL, 0x10);
|
||||
} else if ((st = getKeyCode(keystr)) == -1) {
|
||||
MSG("Keys::parseKey unknown keyname: %s\n", keystr);
|
||||
} else {
|
||||
keycode = st;
|
||||
}
|
||||
dFree(keystr);
|
||||
|
||||
// Set binding
|
||||
if (keycode) {
|
||||
delKeyCmd(keycode, keymod);
|
||||
if (symcode != KEYS_NOP) {
|
||||
KeyBinding_t *node = dNew(KeyBinding_t, 1);
|
||||
node->name = dStrdup(commandName);
|
||||
node->cmd = symcode;
|
||||
node->modifier = keymod;
|
||||
node->key = keycode;
|
||||
dList_insert_sorted(bindings, node, nodeByKeyCmp);
|
||||
_MSG("parseKey: Adding key=%d, mod=%d\n", node->key, node->modifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the keysrc.
|
||||
*/
|
||||
void Keys::parse(FILE *fp)
|
||||
{
|
||||
char *line, *keycomb, *command;
|
||||
int st, lineno = 1;
|
||||
|
||||
// scan the file line by line
|
||||
while ((line = dGetline(fp)) != NULL) {
|
||||
st = dParser_parse_rc_line(&line, &keycomb, &command);
|
||||
|
||||
if (st == 0) {
|
||||
_MSG("Keys::parse: keycomb=%s, command=%s\n", keycomb, command);
|
||||
parseKey(keycomb, command);
|
||||
} else if (st < 0) {
|
||||
MSG("Keys::parse: Syntax error in keysrc line %d: "
|
||||
"keycomb=\"%s\" command=\"%s\"\n", lineno, keycomb, command);
|
||||
}
|
||||
|
||||
dFree(line);
|
||||
++lineno;
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
74
src/keys.hh
Normal file
74
src/keys.hh
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Key parser
|
||||
*
|
||||
* Copyright (C) 2009 Jorge Arellano Cid <jcid@dillo.org>
|
||||
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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 __KEYS_HH__
|
||||
#define __KEYS_HH__
|
||||
|
||||
|
||||
typedef enum {
|
||||
KEYS_INVALID = -1,
|
||||
KEYS_NOP, /* No operation bound */
|
||||
KEYS_OPEN,
|
||||
KEYS_NEW_WINDOW,
|
||||
KEYS_NEW_TAB,
|
||||
KEYS_LEFT_TAB,
|
||||
KEYS_RIGHT_TAB,
|
||||
KEYS_CLOSE_TAB,
|
||||
KEYS_FIRST_TAB,
|
||||
KEYS_LAST_TAB,
|
||||
KEYS_FIND,
|
||||
KEYS_WEBSEARCH,
|
||||
KEYS_BOOKMARKS,
|
||||
KEYS_RELOAD,
|
||||
KEYS_STOP,
|
||||
KEYS_SAVE,
|
||||
KEYS_HIDE_PANELS,
|
||||
KEYS_FILE_MENU,
|
||||
KEYS_CLOSE_ALL,
|
||||
KEYS_BACK,
|
||||
KEYS_FORWARD,
|
||||
KEYS_GOTO,
|
||||
KEYS_HOME,
|
||||
KEYS_VIEW_SOURCE,
|
||||
KEYS_SCREEN_UP,
|
||||
KEYS_SCREEN_DOWN,
|
||||
KEYS_SCREEN_LEFT,
|
||||
KEYS_SCREEN_RIGHT,
|
||||
KEYS_LINE_UP,
|
||||
KEYS_LINE_DOWN,
|
||||
KEYS_LEFT,
|
||||
KEYS_RIGHT,
|
||||
KEYS_TOP,
|
||||
KEYS_BOTTOM,
|
||||
KEYS_ZOOM_IN,
|
||||
KEYS_ZOOM_OUT,
|
||||
KEYS_ZOOM_RESET
|
||||
} KeysCommand_t;
|
||||
|
||||
class Keys {
|
||||
private:
|
||||
static int nodeByKeyCmp(const void *node, const void *key);
|
||||
static void delKeyCmd(int key, int mod);
|
||||
static KeysCommand_t getCmdCode(const char *symbolName);
|
||||
static int getKeyCode(char *keyName);
|
||||
static int getModifier(char *modifierName);
|
||||
static void parseKey(char *key, char *symbol);
|
||||
public:
|
||||
static void init();
|
||||
static void free();
|
||||
static void parse(FILE *fp);
|
||||
static KeysCommand_t getKeyCmd(void);
|
||||
static int getShortcut(KeysCommand_t cmd);
|
||||
};
|
||||
|
||||
|
||||
#endif /* __KEYS_HH__ */
|
||||
120
src/keysrc
Normal file
120
src/keysrc
Normal file
@ -0,0 +1,120 @@
|
||||
# keysrc
|
||||
# Sample dillo key bindings file.
|
||||
#
|
||||
# The format is: "key = action" or "<modifier>key = action".
|
||||
# Lines that begin with a '#' are comments.
|
||||
# The commented-out bindings below show the defaults built into Dillo.
|
||||
#
|
||||
# Modifiers recognized: "Shift", "Ctrl", "Alt", "Meta".
|
||||
# (OS X: Use "Meta" for Command)
|
||||
#
|
||||
# Key names recognized: "Backspace", "Delete", "Down", "End", "Esc",
|
||||
# "F1" through "F12", "Home", "Insert", "Left", "Menu", "PageDown", "PageUp",
|
||||
# "Print", "Return", "Right", "Space", "Tab", "Up".
|
||||
#
|
||||
# Multimedia keys: "Back", "Favorites", "Forward", "HomePage", "Mail",
|
||||
# "MediaNext", "MediaPlay", "MediaPrev", "MediaStop", "Refresh", "Search",
|
||||
# "Sleep", "Stop", "VolumeDown", "VolumeMute", VolumeUp".
|
||||
#
|
||||
# If Dillo is running under X11, keys whose names are not recognized can
|
||||
# be specified using their keysym value in hexadecimal. Use xev to get
|
||||
# the keysym. Example rule: "0x1008ff27 = forward".
|
||||
#
|
||||
# The action "nop" (no operation) can be used to remove a binding.
|
||||
|
||||
# "open" lets you browse your local files for one to open.
|
||||
#<ctrl>o = open
|
||||
|
||||
# "new-window" opens a new browser window.
|
||||
#<ctrl>n = new-window
|
||||
|
||||
# "new-tab" opens a new tab in the current browser window.
|
||||
#<ctrl>t = new-tab
|
||||
|
||||
# "close-tab" closes the current tab.
|
||||
# Note that this closes the browser window if there is only one tab.
|
||||
#<ctrl>w = close-tab
|
||||
|
||||
# "close-all" closes all tabs/windows and exits.
|
||||
#<ctrl>q = close-all
|
||||
|
||||
# "left-tab" and "right-tab" switch to the left/right of the current tab.
|
||||
# <ctrl><shift>tab = left-tab
|
||||
# <ctrl>PageUp = left-tab
|
||||
# <ctrl>tab = right-tab
|
||||
# <ctrl>PageDown = right-tab
|
||||
|
||||
# "back" and "forward" move back/forward through the browser history.
|
||||
#backspace = back
|
||||
#<shift>backspace = forward
|
||||
#, = back
|
||||
#. = forward
|
||||
|
||||
# "reload" the current page.
|
||||
#<ctrl>r = reload
|
||||
|
||||
# "home" goes to the homepage that you set in your dillorc.
|
||||
#<ctrl>h = home
|
||||
|
||||
# "find" lets you search for a text string on the current page.
|
||||
#<ctrl>f = find
|
||||
|
||||
# "hide-panels" hides the findbar if present, control panels if not.
|
||||
#esc = hide-panels
|
||||
|
||||
# "websearch" lets you send a text string to the search engine that you
|
||||
# set in your dillorc.
|
||||
#<ctrl>s = websearch
|
||||
|
||||
# go to your "bookmarks".
|
||||
#<ctrl>b = bookmarks
|
||||
|
||||
# "file-menu" pops up the file menu.
|
||||
#<alt>f = file-menu
|
||||
|
||||
# "view-source" displays the page source.
|
||||
#<ctrl>u = view-source
|
||||
|
||||
# "goto" goes to the location bar at the top of the window.
|
||||
#<ctrl>l = goto
|
||||
|
||||
# "stop" loading the page.
|
||||
#(stop has no default binding)
|
||||
|
||||
# "save" the current page.
|
||||
#(save has no default binding)
|
||||
|
||||
# "zoom-in" increases the zoom by 10% (make elements bigger).
|
||||
#<ctrl>+ = zoom-in
|
||||
#<ctrl>= = zoom-in
|
||||
# "zoom-out" decreases the zoom by 10% (make elements smaller).
|
||||
#<ctrl>- = zoom-out
|
||||
# "zoom-reset" resets the zoom to 100%.
|
||||
#<ctrl>0 = zoom-reset
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# MOTION COMMANDS
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
#pageup = screen-up
|
||||
#b = screen-up
|
||||
|
||||
#pagedown = screen-down
|
||||
#space = screen-down
|
||||
|
||||
#(screen-left has no default binding)
|
||||
|
||||
#(screen-right has no default binding)
|
||||
|
||||
#up = line-up
|
||||
|
||||
#down = line-down
|
||||
|
||||
#left = left
|
||||
|
||||
#right = right
|
||||
|
||||
#home = top
|
||||
|
||||
#end = bottom
|
||||
|
||||
127
src/klist.c
Normal file
127
src/klist.c
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* File: klist.c
|
||||
*
|
||||
* Copyright 2001-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.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* A simple ADT for Key-Data pairs.
|
||||
*
|
||||
* NOTE: this ADT is not perfect. The possibility of holding a Key, after
|
||||
* its data has been removed, long enough for the key-counter to reset and
|
||||
* reuse the same key is very low, but EXISTS. So, the responsibility
|
||||
* remains with the caller.
|
||||
*/
|
||||
|
||||
#include "klist.h"
|
||||
|
||||
|
||||
/**
|
||||
* Compare function for searching data by its key
|
||||
*/
|
||||
static int Klist_node_by_key_cmp(const void *Node, const void *key)
|
||||
{
|
||||
return ((KlistNode_t *)Node)->Key - VOIDP2INT(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare function for searching data by node
|
||||
*/
|
||||
static int Klist_node_by_node_cmp(const void *Node1, const void *Node2)
|
||||
{
|
||||
return ((KlistNode_t *)Node1)->Key - ((KlistNode_t *)Node2)->Key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the data pointer for a given Key (or NULL if not found)
|
||||
*/
|
||||
void *a_Klist_get_data(Klist_t *Klist, int Key)
|
||||
{
|
||||
void *aux;
|
||||
|
||||
if (!Klist)
|
||||
return NULL;
|
||||
aux = dList_find_sorted(Klist->List, INT2VOIDP(Key), Klist_node_by_key_cmp);
|
||||
return (aux) ? ((KlistNode_t*)aux)->Data : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a data pointer and return a key for it.
|
||||
*/
|
||||
int a_Klist_insert(Klist_t **Klist, void *Data)
|
||||
{
|
||||
KlistNode_t *Node;
|
||||
|
||||
if (!*Klist) {
|
||||
(*Klist) = dNew(Klist_t, 1);
|
||||
(*Klist)->List = dList_new(32);
|
||||
(*Klist)->Clean = 1;
|
||||
(*Klist)->Counter = 0;
|
||||
}
|
||||
|
||||
/* This avoids repeated keys at the same time */
|
||||
do {
|
||||
if (++((*Klist)->Counter) == 0) {
|
||||
(*Klist)->Counter = 1;
|
||||
(*Klist)->Clean = 0;
|
||||
}
|
||||
} while (!((*Klist)->Clean) &&
|
||||
a_Klist_get_data((*Klist), (*Klist)->Counter));
|
||||
|
||||
Node = dNew(KlistNode_t, 1);
|
||||
Node->Key = (*Klist)->Counter;
|
||||
Node->Data = Data;
|
||||
dList_insert_sorted((*Klist)->List, Node, Klist_node_by_node_cmp);
|
||||
return (*Klist)->Counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove data by Key
|
||||
*/
|
||||
void a_Klist_remove(Klist_t *Klist, int Key)
|
||||
{
|
||||
void *data;
|
||||
|
||||
data = dList_find_sorted(Klist->List, INT2VOIDP(Key),Klist_node_by_key_cmp);
|
||||
if (data) {
|
||||
dList_remove(Klist->List, data);
|
||||
dFree(data);
|
||||
}
|
||||
if (dList_length(Klist->List) == 0)
|
||||
Klist->Clean = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of elements in the Klist
|
||||
*/
|
||||
int a_Klist_length(Klist_t *Klist)
|
||||
{
|
||||
return dList_length(Klist->List);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a Klist
|
||||
*/
|
||||
void a_Klist_free(Klist_t **KlistPtr)
|
||||
{
|
||||
void *node;
|
||||
Klist_t *Klist = *KlistPtr;
|
||||
|
||||
if (!Klist)
|
||||
return;
|
||||
|
||||
while (dList_length(Klist->List) > 0) {
|
||||
node = dList_nth_data(Klist->List, 0);
|
||||
dList_remove_fast(Klist->List, node);
|
||||
dFree(node);
|
||||
}
|
||||
dList_free(Klist->List);
|
||||
dFree(Klist);
|
||||
*KlistPtr = NULL;
|
||||
}
|
||||
|
||||
37
src/klist.h
Normal file
37
src/klist.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef __KLIST_H__
|
||||
#define __KLIST_H__
|
||||
|
||||
#include "../dlib/dlib.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef struct {
|
||||
int Key; /**< primary key */
|
||||
void *Data; /**< data reference */
|
||||
} KlistNode_t;
|
||||
|
||||
typedef struct {
|
||||
Dlist *List;
|
||||
int Clean; /**< check flag */
|
||||
int Counter; /**< counter (for making keys) */
|
||||
} Klist_t;
|
||||
|
||||
|
||||
/*
|
||||
* Function prototypes
|
||||
*/
|
||||
void* a_Klist_get_data(Klist_t *Klist, int Key);
|
||||
int a_Klist_insert(Klist_t **Klist, void *Data);
|
||||
void a_Klist_remove(Klist_t *Klist, int Key);
|
||||
int a_Klist_length(Klist_t *Klist);
|
||||
void a_Klist_free(Klist_t **Klist);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __KLIST_H__ */
|
||||
44
src/list.h
Normal file
44
src/list.h
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* @file
|
||||
* Fast list methods.
|
||||
* Feb 2000 --Jcid
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LIST_H__
|
||||
#define __LIST_H__
|
||||
|
||||
/**
|
||||
* Make sure there's space for 'num_items' items within the list.
|
||||
* (First, allocate an 'alloc_step' sized chunk, after that, double the
|
||||
* list size --to make it faster)
|
||||
*/
|
||||
#define a_List_resize(list,num_items,alloc_step) \
|
||||
if (!list) { \
|
||||
list = dMalloc(alloc_step * sizeof(*list)); \
|
||||
} \
|
||||
if (num_items >= alloc_step){ \
|
||||
while ( num_items >= alloc_step ) \
|
||||
alloc_step <<= 1; \
|
||||
list = dRealloc(list, alloc_step * sizeof(*list)); \
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make sure there's space for one more item within the list.
|
||||
*/
|
||||
#define a_List_add(list,num_items,alloc_step) \
|
||||
a_List_resize(list,num_items,alloc_step)
|
||||
|
||||
|
||||
/**
|
||||
* Quickly remove an item from the list
|
||||
* ==> We preserve relative position, but not the element index <==
|
||||
*/
|
||||
#define a_List_remove(list, item, num_items) \
|
||||
if (list && item < num_items) { \
|
||||
list[item] = list[--num_items]; \
|
||||
}
|
||||
|
||||
|
||||
#endif /* __LIST_H__ */
|
||||
392
src/md5.c
Normal file
392
src/md5.c
Normal file
@ -0,0 +1,392 @@
|
||||
/*
|
||||
* md5.c was taken from "RFC1321-based (RSA-free) MD5 library" by L. Peter
|
||||
* Deutsch at http://sourceforge.net/projects/libmd5-rfc/ in October 2011.
|
||||
*
|
||||
* The code was not modified when integrated into the Dillo project, but you
|
||||
* should check the source repository to be sure that there have not been
|
||||
* modifications since this notice.
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
L. Peter Deutsch
|
||||
ghost@aladdin.com
|
||||
|
||||
*/
|
||||
/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
|
||||
/**
|
||||
@file
|
||||
Independent implementation of MD5 (RFC 1321).
|
||||
|
||||
This code implements the MD5 Algorithm defined in RFC 1321, whose
|
||||
text is available at
|
||||
http://www.ietf.org/rfc/rfc1321.txt
|
||||
The code is derived from the text of the RFC, including the test suite
|
||||
(section A.5) but excluding the rest of Appendix A. It does not include
|
||||
any code or documentation that is identified in the RFC as being
|
||||
copyrighted.
|
||||
|
||||
The original and principal author of md5.c is L. Peter Deutsch
|
||||
<ghost@aladdin.com>. Other authors are noted in the change history
|
||||
that follows (in reverse chronological order):
|
||||
|
||||
- 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
|
||||
either statically or dynamically; added missing #include <string.h>
|
||||
in library.
|
||||
- 2002-03-11 lpd Corrected argument list for main(), and added int return
|
||||
type, in test program and T value program.
|
||||
- 2002-02-21 lpd Added missing #include <stdio.h> in test program.
|
||||
- 2000-07-03 lpd Patched to eliminate warnings about "constant is
|
||||
unsigned in ANSI C, signed in traditional"; made test program
|
||||
self-checking.
|
||||
- 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
|
||||
- 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
|
||||
- 1999-05-03 lpd Original version.
|
||||
*/
|
||||
|
||||
#include "md5.h"
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
|
||||
#ifdef ARCH_IS_BIG_ENDIAN
|
||||
# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
|
||||
#else
|
||||
# define BYTE_ORDER 0
|
||||
#endif
|
||||
|
||||
#define T_MASK ((md5_word_t)~0)
|
||||
#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
|
||||
#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
|
||||
#define T3 0x242070db
|
||||
#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
|
||||
#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
|
||||
#define T6 0x4787c62a
|
||||
#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
|
||||
#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
|
||||
#define T9 0x698098d8
|
||||
#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
|
||||
#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
|
||||
#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
|
||||
#define T13 0x6b901122
|
||||
#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
|
||||
#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
|
||||
#define T16 0x49b40821
|
||||
#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
|
||||
#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
|
||||
#define T19 0x265e5a51
|
||||
#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
|
||||
#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
|
||||
#define T22 0x02441453
|
||||
#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
|
||||
#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
|
||||
#define T25 0x21e1cde6
|
||||
#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
|
||||
#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
|
||||
#define T28 0x455a14ed
|
||||
#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
|
||||
#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
|
||||
#define T31 0x676f02d9
|
||||
#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
|
||||
#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
|
||||
#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
|
||||
#define T35 0x6d9d6122
|
||||
#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
|
||||
#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
|
||||
#define T38 0x4bdecfa9
|
||||
#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
|
||||
#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
|
||||
#define T41 0x289b7ec6
|
||||
#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
|
||||
#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
|
||||
#define T44 0x04881d05
|
||||
#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
|
||||
#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
|
||||
#define T47 0x1fa27cf8
|
||||
#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
|
||||
#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
|
||||
#define T50 0x432aff97
|
||||
#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
|
||||
#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
|
||||
#define T53 0x655b59c3
|
||||
#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
|
||||
#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
|
||||
#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
|
||||
#define T57 0x6fa87e4f
|
||||
#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
|
||||
#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
|
||||
#define T60 0x4e0811a1
|
||||
#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
|
||||
#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
|
||||
#define T63 0x2ad7d2bb
|
||||
#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
|
||||
|
||||
|
||||
static void
|
||||
md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
|
||||
{
|
||||
md5_word_t
|
||||
a = pms->abcd[0], b = pms->abcd[1],
|
||||
c = pms->abcd[2], d = pms->abcd[3];
|
||||
md5_word_t t;
|
||||
#if BYTE_ORDER > 0
|
||||
/* Define storage only for big-endian CPUs. */
|
||||
md5_word_t X[16];
|
||||
#else
|
||||
/* Define storage for little-endian or both types of CPUs. */
|
||||
md5_word_t xbuf[16];
|
||||
const md5_word_t *X;
|
||||
#endif
|
||||
|
||||
{
|
||||
#if BYTE_ORDER == 0
|
||||
/*
|
||||
* Determine dynamically whether this is a big-endian or
|
||||
* little-endian machine, since we can use a more efficient
|
||||
* algorithm on the latter.
|
||||
*/
|
||||
static const int w = 1;
|
||||
|
||||
if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
|
||||
#endif
|
||||
#if BYTE_ORDER <= 0 /* little-endian */
|
||||
{
|
||||
/*
|
||||
* On little-endian machines, we can process properly aligned
|
||||
* data without copying it.
|
||||
*/
|
||||
if (((uintptr_t) data & 3) == 0) {
|
||||
/* data are properly aligned */
|
||||
X = (const md5_word_t *)data;
|
||||
} else {
|
||||
/* not aligned */
|
||||
memcpy(xbuf, data, 64);
|
||||
X = xbuf;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if BYTE_ORDER == 0
|
||||
else /* dynamic big-endian */
|
||||
#endif
|
||||
#if BYTE_ORDER >= 0 /* big-endian */
|
||||
{
|
||||
/*
|
||||
* On big-endian machines, we must arrange the bytes in the
|
||||
* right order.
|
||||
*/
|
||||
const md5_byte_t *xp = data;
|
||||
int i;
|
||||
|
||||
# if BYTE_ORDER == 0
|
||||
X = xbuf; /* (dynamic only) */
|
||||
# else
|
||||
# define xbuf X /* (static only) */
|
||||
# endif
|
||||
for (i = 0; i < 16; ++i, xp += 4)
|
||||
xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
|
||||
|
||||
/* Round 1. */
|
||||
/* Let [abcd k s i] denote the operation
|
||||
a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + F(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 0, 7, T1);
|
||||
SET(d, a, b, c, 1, 12, T2);
|
||||
SET(c, d, a, b, 2, 17, T3);
|
||||
SET(b, c, d, a, 3, 22, T4);
|
||||
SET(a, b, c, d, 4, 7, T5);
|
||||
SET(d, a, b, c, 5, 12, T6);
|
||||
SET(c, d, a, b, 6, 17, T7);
|
||||
SET(b, c, d, a, 7, 22, T8);
|
||||
SET(a, b, c, d, 8, 7, T9);
|
||||
SET(d, a, b, c, 9, 12, T10);
|
||||
SET(c, d, a, b, 10, 17, T11);
|
||||
SET(b, c, d, a, 11, 22, T12);
|
||||
SET(a, b, c, d, 12, 7, T13);
|
||||
SET(d, a, b, c, 13, 12, T14);
|
||||
SET(c, d, a, b, 14, 17, T15);
|
||||
SET(b, c, d, a, 15, 22, T16);
|
||||
#undef SET
|
||||
|
||||
/* Round 2. */
|
||||
/* Let [abcd k s i] denote the operation
|
||||
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + G(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 1, 5, T17);
|
||||
SET(d, a, b, c, 6, 9, T18);
|
||||
SET(c, d, a, b, 11, 14, T19);
|
||||
SET(b, c, d, a, 0, 20, T20);
|
||||
SET(a, b, c, d, 5, 5, T21);
|
||||
SET(d, a, b, c, 10, 9, T22);
|
||||
SET(c, d, a, b, 15, 14, T23);
|
||||
SET(b, c, d, a, 4, 20, T24);
|
||||
SET(a, b, c, d, 9, 5, T25);
|
||||
SET(d, a, b, c, 14, 9, T26);
|
||||
SET(c, d, a, b, 3, 14, T27);
|
||||
SET(b, c, d, a, 8, 20, T28);
|
||||
SET(a, b, c, d, 13, 5, T29);
|
||||
SET(d, a, b, c, 2, 9, T30);
|
||||
SET(c, d, a, b, 7, 14, T31);
|
||||
SET(b, c, d, a, 12, 20, T32);
|
||||
#undef SET
|
||||
|
||||
/* Round 3. */
|
||||
/* Let [abcd k s t] denote the operation
|
||||
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define H(x, y, z) ((x) ^ (y) ^ (z))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + H(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 5, 4, T33);
|
||||
SET(d, a, b, c, 8, 11, T34);
|
||||
SET(c, d, a, b, 11, 16, T35);
|
||||
SET(b, c, d, a, 14, 23, T36);
|
||||
SET(a, b, c, d, 1, 4, T37);
|
||||
SET(d, a, b, c, 4, 11, T38);
|
||||
SET(c, d, a, b, 7, 16, T39);
|
||||
SET(b, c, d, a, 10, 23, T40);
|
||||
SET(a, b, c, d, 13, 4, T41);
|
||||
SET(d, a, b, c, 0, 11, T42);
|
||||
SET(c, d, a, b, 3, 16, T43);
|
||||
SET(b, c, d, a, 6, 23, T44);
|
||||
SET(a, b, c, d, 9, 4, T45);
|
||||
SET(d, a, b, c, 12, 11, T46);
|
||||
SET(c, d, a, b, 15, 16, T47);
|
||||
SET(b, c, d, a, 2, 23, T48);
|
||||
#undef SET
|
||||
|
||||
/* Round 4. */
|
||||
/* Let [abcd k s t] denote the operation
|
||||
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + I(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 0, 6, T49);
|
||||
SET(d, a, b, c, 7, 10, T50);
|
||||
SET(c, d, a, b, 14, 15, T51);
|
||||
SET(b, c, d, a, 5, 21, T52);
|
||||
SET(a, b, c, d, 12, 6, T53);
|
||||
SET(d, a, b, c, 3, 10, T54);
|
||||
SET(c, d, a, b, 10, 15, T55);
|
||||
SET(b, c, d, a, 1, 21, T56);
|
||||
SET(a, b, c, d, 8, 6, T57);
|
||||
SET(d, a, b, c, 15, 10, T58);
|
||||
SET(c, d, a, b, 6, 15, T59);
|
||||
SET(b, c, d, a, 13, 21, T60);
|
||||
SET(a, b, c, d, 4, 6, T61);
|
||||
SET(d, a, b, c, 11, 10, T62);
|
||||
SET(c, d, a, b, 2, 15, T63);
|
||||
SET(b, c, d, a, 9, 21, T64);
|
||||
#undef SET
|
||||
|
||||
/* Then perform the following additions. (That is increment each
|
||||
of the four registers by the value it had before this block
|
||||
was started.) */
|
||||
pms->abcd[0] += a;
|
||||
pms->abcd[1] += b;
|
||||
pms->abcd[2] += c;
|
||||
pms->abcd[3] += d;
|
||||
}
|
||||
|
||||
void
|
||||
md5_init(md5_state_t *pms)
|
||||
{
|
||||
pms->count[0] = pms->count[1] = 0;
|
||||
pms->abcd[0] = 0x67452301;
|
||||
pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
|
||||
pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
|
||||
pms->abcd[3] = 0x10325476;
|
||||
}
|
||||
|
||||
void
|
||||
md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
|
||||
{
|
||||
const md5_byte_t *p = data;
|
||||
int left = nbytes;
|
||||
int offset = (pms->count[0] >> 3) & 63;
|
||||
md5_word_t nbits = (md5_word_t)(nbytes << 3);
|
||||
|
||||
if (nbytes <= 0)
|
||||
return;
|
||||
|
||||
/* Update the message length. */
|
||||
pms->count[1] += nbytes >> 29;
|
||||
pms->count[0] += nbits;
|
||||
if (pms->count[0] < nbits)
|
||||
pms->count[1]++;
|
||||
|
||||
/* Process an initial partial block. */
|
||||
if (offset) {
|
||||
int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
|
||||
|
||||
memcpy(pms->buf + offset, p, copy);
|
||||
if (offset + copy < 64)
|
||||
return;
|
||||
p += copy;
|
||||
left -= copy;
|
||||
md5_process(pms, pms->buf);
|
||||
}
|
||||
|
||||
/* Process full blocks. */
|
||||
for (; left >= 64; p += 64, left -= 64)
|
||||
md5_process(pms, p);
|
||||
|
||||
/* Process a final partial block. */
|
||||
if (left)
|
||||
memcpy(pms->buf, p, left);
|
||||
}
|
||||
|
||||
void
|
||||
md5_finish(md5_state_t *pms, md5_byte_t digest[16])
|
||||
{
|
||||
static const md5_byte_t pad[64] = {
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
md5_byte_t data[8];
|
||||
int i;
|
||||
|
||||
/* Save the length before padding. */
|
||||
for (i = 0; i < 8; ++i)
|
||||
data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
|
||||
/* Pad to 56 bytes mod 64. */
|
||||
md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
|
||||
/* Append the length. */
|
||||
md5_append(pms, data, 8);
|
||||
for (i = 0; i < 16; ++i)
|
||||
digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
|
||||
}
|
||||
100
src/md5.h
Normal file
100
src/md5.h
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* md5.h was taken from "RFC1321-based (RSA-free) MD5 library" by L. Peter
|
||||
* Deutsch at http://sourceforge.net/projects/libmd5-rfc/ in October 2011.
|
||||
*
|
||||
* The code was not modified when integrated into the Dillo project, but you
|
||||
* should check the source repository to be sure that there have not been
|
||||
* modifications since this notice.
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
L. Peter Deutsch
|
||||
ghost@aladdin.com
|
||||
|
||||
*/
|
||||
/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
|
||||
/*
|
||||
Independent implementation of MD5 (RFC 1321).
|
||||
|
||||
This code implements the MD5 Algorithm defined in RFC 1321, whose
|
||||
text is available at
|
||||
http://www.ietf.org/rfc/rfc1321.txt
|
||||
The code is derived from the text of the RFC, including the test suite
|
||||
(section A.5) but excluding the rest of Appendix A. It does not include
|
||||
any code or documentation that is identified in the RFC as being
|
||||
copyrighted.
|
||||
|
||||
The original and principal author of md5.h is L. Peter Deutsch
|
||||
<ghost@aladdin.com>. Other authors are noted in the change history
|
||||
that follows (in reverse chronological order):
|
||||
|
||||
2002-04-13 lpd Removed support for non-ANSI compilers; removed
|
||||
references to Ghostscript; clarified derivation from RFC 1321;
|
||||
now handles byte order either statically or dynamically.
|
||||
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
|
||||
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
|
||||
added conditionalization for C++ compilation from Martin
|
||||
Purschke <purschke@bnl.gov>.
|
||||
1999-05-03 lpd Original version.
|
||||
*/
|
||||
|
||||
#ifndef md5_INCLUDED
|
||||
# define md5_INCLUDED
|
||||
|
||||
/*
|
||||
* This package supports both compile-time and run-time determination of CPU
|
||||
* byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
|
||||
* compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
|
||||
* defined as non-zero, the code will be compiled to run only on big-endian
|
||||
* CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
|
||||
* run on either big- or little-endian CPUs, but will run slightly less
|
||||
* efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
|
||||
*/
|
||||
|
||||
typedef unsigned char md5_byte_t; /* 8-bit byte */
|
||||
typedef unsigned int md5_word_t; /* 32-bit word */
|
||||
|
||||
/** Define the state of the MD5 Algorithm. */
|
||||
typedef struct md5_state_s {
|
||||
md5_word_t count[2]; /* message length in bits, lsw first */
|
||||
md5_word_t abcd[4]; /* digest buffer */
|
||||
md5_byte_t buf[64]; /* accumulate block */
|
||||
} md5_state_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/** Initialize the algorithm. */
|
||||
void md5_init(md5_state_t *pms);
|
||||
|
||||
/** Append a string to the message. */
|
||||
void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
|
||||
|
||||
/** Finish the message and return the digest. */
|
||||
void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* md5_INCLUDED */
|
||||
878
src/menu.cc
Normal file
878
src/menu.cc
Normal file
@ -0,0 +1,878 @@
|
||||
/*
|
||||
* File: menu.cc
|
||||
*
|
||||
* Copyright (C) 2005-2007 Jorge Arellano Cid <jcid@dillo.org>
|
||||
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Functions for menus
|
||||
*/
|
||||
|
||||
#include <FL/Fl.H>
|
||||
#include <FL/Fl_Menu_Item.H>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include "lout/misc.hh" /* SimpleVector */
|
||||
#include "msg.h"
|
||||
#include "menu.hh"
|
||||
#include "actions.h"
|
||||
#include "uicmd.hh"
|
||||
#include "history.h"
|
||||
#include "html.hh"
|
||||
#include "ui.hh" // for (UI *)
|
||||
#include "keys.hh"
|
||||
#include "timeout.hh"
|
||||
|
||||
/*
|
||||
* Local data types
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
const char *title;
|
||||
const Fl_Menu_Item *picked;
|
||||
const Fl_Menu_Item *menu;
|
||||
} Menu_popup_data_t;
|
||||
|
||||
/*
|
||||
* Local data
|
||||
*/
|
||||
|
||||
// (This data can be encapsulated inside a class for each popup, but
|
||||
// as popups are modal, there's no need).
|
||||
static DilloUrl *popup_url = NULL;
|
||||
// Weak reference to the popup's bw
|
||||
static BrowserWindow *popup_bw = NULL;
|
||||
static void *popup_form = NULL;
|
||||
// Where to place the popup
|
||||
static int popup_x, popup_y;
|
||||
// History popup direction (-1 = back, 1 = forward).
|
||||
static int history_direction = -1;
|
||||
// History popup, list of URL-indexes.
|
||||
static int *history_list = NULL;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static void Menu_nop_cb(Fl_Widget*, void*)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Static function for File menu callbacks.
|
||||
*/
|
||||
static void filemenu_cb(Fl_Widget*, void *data)
|
||||
{
|
||||
if (strcmp((char*)data, "nw") == 0) {
|
||||
a_UIcmd_open_url_nw(popup_bw, NULL);
|
||||
} else if (strcmp((char*)data, "nt") == 0) {
|
||||
a_UIcmd_open_url_nt(popup_bw, NULL, 1);
|
||||
} else if (strcmp((char*)data, "of") == 0) {
|
||||
a_UIcmd_open_file(popup_bw);
|
||||
} else if (strcmp((char*)data, "ou") == 0) {
|
||||
a_UIcmd_focus_location(popup_bw);
|
||||
} else if (strcmp((char*)data, "cw") == 0) {
|
||||
a_Timeout_add(0.0, a_UIcmd_close_bw, popup_bw);
|
||||
} else if (strcmp((char*)data, "ed") == 0) {
|
||||
a_Timeout_add(0.0, a_UIcmd_close_all_bw, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void Menu_copy_urlstr_cb(Fl_Widget*, void *user_data)
|
||||
{
|
||||
if (user_data) {
|
||||
DilloUrl *url = (DilloUrl *)user_data ;
|
||||
a_UIcmd_copy_urlstr(popup_bw, URL_STR(url));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open URL
|
||||
*/
|
||||
static void Menu_open_url_cb(Fl_Widget*, void *user_data)
|
||||
{
|
||||
DilloUrl *url = (DilloUrl *)user_data;
|
||||
_MSG("Open URL cb: click! :-)\n");
|
||||
a_UIcmd_open_url(popup_bw, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open URL in new window
|
||||
*/
|
||||
static void Menu_open_url_nw_cb(Fl_Widget*, void *user_data)
|
||||
{
|
||||
DilloUrl *url = (DilloUrl *)user_data;
|
||||
_MSG("Open URL in new window cb: click! :-)\n");
|
||||
a_UIcmd_open_url_nw(popup_bw, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open URL in new Tab
|
||||
*/
|
||||
static void Menu_open_url_nt_cb(Fl_Widget*, void *user_data)
|
||||
{
|
||||
DilloUrl *url = (DilloUrl *)user_data;
|
||||
int focus = prefs.focus_new_tab ? 1 : 0;
|
||||
if (Fl::event_state(FL_SHIFT)) focus = !focus;
|
||||
a_UIcmd_open_url_nt(popup_bw, url, focus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add bookmark
|
||||
*/
|
||||
static void Menu_add_bookmark_cb(Fl_Widget*, void *user_data)
|
||||
{
|
||||
DilloUrl *url = (DilloUrl *)user_data;
|
||||
a_UIcmd_add_bookmark(popup_bw, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find text
|
||||
*/
|
||||
static void Menu_find_text_cb(Fl_Widget*, void*)
|
||||
{
|
||||
((UI *)popup_bw->ui)->findbar_toggle(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save link
|
||||
*/
|
||||
static void Menu_save_link_cb(Fl_Widget*, void *user_data)
|
||||
{
|
||||
DilloUrl *url = (DilloUrl *)user_data;
|
||||
a_UIcmd_save_link(popup_bw, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save current page
|
||||
*/
|
||||
static void Menu_save_page_cb(Fl_Widget*, void*)
|
||||
{
|
||||
a_UIcmd_save(popup_bw);
|
||||
}
|
||||
|
||||
/**
|
||||
* View current page source
|
||||
*/
|
||||
static void Menu_view_page_source_cb(Fl_Widget*, void *user_data)
|
||||
{
|
||||
DilloUrl *url = (DilloUrl *)user_data;
|
||||
a_UIcmd_view_page_source(popup_bw, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* View current page's bugs
|
||||
*/
|
||||
static void Menu_view_page_bugs_cb(Fl_Widget*, void*)
|
||||
{
|
||||
a_UIcmd_view_page_bugs(popup_bw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load images on current page that match URL pattern
|
||||
*/
|
||||
static void Menu_load_images_cb(Fl_Widget*, void *user_data)
|
||||
{
|
||||
DilloUrl *page_url = (DilloUrl *) user_data;
|
||||
void *doc = a_Bw_get_url_doc(popup_bw, page_url);
|
||||
|
||||
if (doc)
|
||||
a_Html_load_images(doc, popup_url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit form
|
||||
*/
|
||||
static void Menu_form_submit_cb(Fl_Widget*, void*)
|
||||
{
|
||||
void *doc = a_Bw_get_url_doc(popup_bw, popup_url);
|
||||
|
||||
if (doc)
|
||||
a_Html_form_submit(doc, popup_form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset form
|
||||
*/
|
||||
static void Menu_form_reset_cb(Fl_Widget*, void*)
|
||||
{
|
||||
void *doc = a_Bw_get_url_doc(popup_bw, popup_url);
|
||||
|
||||
if (doc)
|
||||
a_Html_form_reset(doc, popup_form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle display of 'hidden' form controls.
|
||||
*/
|
||||
static void Menu_form_hiddens_cb(Fl_Widget*, void *user_data)
|
||||
{
|
||||
bool visible = *((bool *) user_data);
|
||||
void *doc = a_Bw_get_url_doc(popup_bw, popup_url);
|
||||
|
||||
if (doc)
|
||||
a_Html_form_display_hiddens(doc, popup_form, !visible);
|
||||
}
|
||||
|
||||
static void Menu_stylesheet_cb(Fl_Widget*, void *vUrl)
|
||||
{
|
||||
int mb = Fl::event_button();
|
||||
const DilloUrl *url = (const DilloUrl *) vUrl;
|
||||
|
||||
if (mb == 1) {
|
||||
a_UIcmd_open_url(popup_bw, url);
|
||||
} else if (mb == 2) {
|
||||
if (prefs.middle_click_opens_new_tab) {
|
||||
int focus = prefs.focus_new_tab ? 1 : 0;
|
||||
if (Fl::event_state(FL_SHIFT)) focus = !focus;
|
||||
a_UIcmd_open_url_nt(popup_bw, url, focus);
|
||||
} else {
|
||||
a_UIcmd_open_url_nw(popup_bw, url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Menu_bugmeter_validate(const char *validator_url)
|
||||
{
|
||||
if (popup_url &&
|
||||
dStrAsciiCasecmp(URL_SCHEME(popup_url), "dpi")) {
|
||||
const char *popup_str = URL_STR(popup_url),
|
||||
*ptr = strrchr(popup_str, '#');
|
||||
char *no_fragment = ptr ? dStrndup(popup_str, ptr - popup_str)
|
||||
: dStrdup(popup_str);
|
||||
char *encoded = a_Url_encode_hex_str(no_fragment);
|
||||
Dstr *dstr = dStr_sized_new(128);
|
||||
|
||||
dStr_sprintf(dstr, validator_url, encoded);
|
||||
a_UIcmd_open_urlstr(popup_bw, dstr->str);
|
||||
dStr_free(dstr, 1);
|
||||
dFree(encoded);
|
||||
dFree(no_fragment);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate URL with the W3C Nu validator (for HTML 5)
|
||||
*/
|
||||
static void Menu_bugmeter_validate_w3c_nu_cb(Fl_Widget*, void*)
|
||||
{
|
||||
Menu_bugmeter_validate(
|
||||
"https://validator.w3.org/nu/"
|
||||
"?useragent=Validator.nu%%2FLV+https%%3A%%2F%%2Fvalidator.w3.org%%2Fservices"
|
||||
"&acceptlanguage="
|
||||
"&doc=%s");
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate URL with the W3C legacy validator (HTML 4.01 and older)
|
||||
*/
|
||||
static void Menu_bugmeter_validate_w3c_cb(Fl_Widget*, void*)
|
||||
{
|
||||
Menu_bugmeter_validate(
|
||||
"https://validator.w3.org/check?uri=%s"
|
||||
"&charset=%%28detect+automatically%%29"
|
||||
"&doctype=Inline&group=0"
|
||||
"&user-agent=W3C_Validator%%2F1.3+");
|
||||
}
|
||||
|
||||
/**
|
||||
* Show info page for the bug meter
|
||||
*/
|
||||
static void Menu_bugmeter_about_cb(Fl_Widget*, void*)
|
||||
{
|
||||
a_UIcmd_open_urlstr(popup_bw, "https://dillo-browser.github.io/old/help/bug_meter.html");
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigation History callback.
|
||||
* Go to selected URL.
|
||||
*/
|
||||
static void Menu_history_cb(Fl_Widget*, void *data)
|
||||
{
|
||||
int mb = Fl::event_button();
|
||||
int offset = history_direction * VOIDP2INT(data);
|
||||
const DilloUrl *url = a_History_get_url(history_list[VOIDP2INT(data)-1]);
|
||||
|
||||
if (mb == 1) {
|
||||
a_UIcmd_nav_jump(popup_bw, offset, 0);
|
||||
} else if (mb == 2) {
|
||||
// Middle button, open in a new window/tab
|
||||
if (prefs.middle_click_opens_new_tab) {
|
||||
int focus = prefs.focus_new_tab ? 1 : 0;
|
||||
if (Fl::event_state(FL_SHIFT)) focus = !focus;
|
||||
a_UIcmd_open_url_nt(popup_bw, url, focus);
|
||||
} else {
|
||||
a_UIcmd_open_url_nw(popup_bw, url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Menus are popped-up from this timeout callback so the events
|
||||
* associated with the button are gone when it pops. This way we
|
||||
* avoid a segfault when a new page replaces the page that issued
|
||||
* the popup menu.
|
||||
*/
|
||||
static void Menu_simple_popup_cb(void *data)
|
||||
{
|
||||
const Fl_Menu_Item *m;
|
||||
|
||||
((UI*)popup_bw->ui)->window()->cursor(FL_CURSOR_DEFAULT);
|
||||
|
||||
m = ((Fl_Menu_Item *)data)->popup(popup_x, popup_y);
|
||||
|
||||
if (m && m->callback())
|
||||
m->do_callback((Fl_Widget *)data);
|
||||
a_Timeout_remove();
|
||||
}
|
||||
|
||||
static void Menu_popup_cb(void *data)
|
||||
{
|
||||
const Fl_Menu_Item *picked;
|
||||
Menu_popup_data_t *d = (Menu_popup_data_t *)data;
|
||||
|
||||
((UI*)popup_bw->ui)->window()->cursor(FL_CURSOR_DEFAULT);
|
||||
|
||||
picked = d->menu->popup(popup_x, popup_y, d->title, d->picked);
|
||||
if (picked) {
|
||||
d->picked = picked;
|
||||
if (picked->callback())
|
||||
picked->do_callback((Fl_Widget *)(d->menu));
|
||||
}
|
||||
a_Timeout_remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Page popup menu (construction & popup)
|
||||
*/
|
||||
void a_Menu_page_popup(BrowserWindow *bw, const DilloUrl *url,
|
||||
bool_t has_bugs, void *v_cssUrls)
|
||||
{
|
||||
lout::misc::SimpleVector <DilloUrl*> *cssUrls =
|
||||
(lout::misc::SimpleVector <DilloUrl*> *) v_cssUrls;
|
||||
int j = 0;
|
||||
|
||||
static Fl_Menu_Item *stylesheets = NULL;
|
||||
static Fl_Menu_Item pm[] = {
|
||||
{"View page source", 0, Menu_view_page_source_cb,0,0,0,0,0,0},
|
||||
{"View page bugs", 0, Menu_view_page_bugs_cb,0,0,0,0,0,0},
|
||||
{"View stylesheets", 0, Menu_nop_cb,0,FL_SUBMENU_POINTER|FL_MENU_DIVIDER,
|
||||
0,0,0,0},
|
||||
{"Bookmark this page", 0,Menu_add_bookmark_cb,0,FL_MENU_DIVIDER,0,0,0,0},
|
||||
{"Find text", 0, Menu_find_text_cb,0,0,0,0,0,0},
|
||||
{"Save page as...", 0, Menu_save_page_cb,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0}
|
||||
};
|
||||
static Menu_popup_data_t page_data = {"Page menu", NULL, pm};
|
||||
|
||||
popup_x = Fl::event_x();
|
||||
popup_y = Fl::event_y();
|
||||
popup_bw = bw;
|
||||
a_Url_free(popup_url);
|
||||
popup_url = a_Url_dup(url);
|
||||
|
||||
has_bugs == TRUE ? pm[1].activate() : pm[1].deactivate();
|
||||
|
||||
if (dStrAsciiCasecmp(URL_SCHEME(url), "dpi") == 0 &&
|
||||
strncmp(URL_PATH(url), "/vsource/", 9) == 0)
|
||||
pm[0].deactivate();
|
||||
else {
|
||||
pm[0].activate();
|
||||
pm[0].user_data(popup_url);
|
||||
}
|
||||
|
||||
if (stylesheets) {
|
||||
while (stylesheets[j].text) {
|
||||
dFree((char *) stylesheets[j].label());
|
||||
a_Url_free((DilloUrl *) stylesheets[j].user_data());
|
||||
j++;
|
||||
}
|
||||
delete [] stylesheets;
|
||||
stylesheets = NULL;
|
||||
}
|
||||
|
||||
if (cssUrls && cssUrls->size () > 0) {
|
||||
stylesheets = new Fl_Menu_Item[cssUrls->size() + 1];
|
||||
memset(stylesheets, '\0', (cssUrls->size() + 1) * sizeof(Fl_Menu_Item));
|
||||
|
||||
for (j = 0; j < cssUrls->size(); j++) {
|
||||
DilloUrl *url = cssUrls->get(j);
|
||||
const char *url_str = URL_STR(url);
|
||||
const uint_t head_length = 30, tail_length = 40,
|
||||
url_len = strlen(url_str);
|
||||
char *label;
|
||||
|
||||
if (url_len > head_length + tail_length + 3) {
|
||||
/* trim long URLs when making the label */
|
||||
char *url_head = dStrndup(url_str, head_length);
|
||||
const char *url_tail = url_str + (url_len - tail_length);
|
||||
label = dStrconcat(url_head, "...", url_tail, NULL);
|
||||
dFree(url_head);
|
||||
} else {
|
||||
label = dStrdup(url_str);
|
||||
}
|
||||
|
||||
stylesheets[j].label(FL_NORMAL_LABEL, label);
|
||||
stylesheets[j].callback(Menu_stylesheet_cb, a_Url_dup(url));
|
||||
}
|
||||
|
||||
pm[2].user_data(stylesheets);
|
||||
pm[2].activate();
|
||||
} else {
|
||||
pm[2].deactivate();
|
||||
}
|
||||
pm[3].user_data(popup_url);
|
||||
|
||||
a_Timeout_add(0.0, Menu_popup_cb, (void*)&page_data);
|
||||
}
|
||||
|
||||
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,FL_MENU_DIVIDER,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0}
|
||||
};
|
||||
|
||||
/* As we can only provide a pointer to the link menu 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 {
|
||||
const DilloUrl *url;
|
||||
const DilloUrl *origin;
|
||||
Action *action;
|
||||
};
|
||||
|
||||
/**
|
||||
* Open URL following a custom action
|
||||
*/
|
||||
static void Menu_open_url_action_cb(Fl_Widget*, void *user_data)
|
||||
{
|
||||
/* Don't use popup_url because it is used for the image URL when coming from
|
||||
* the image menu. We should get rid of the global variables and pass them
|
||||
* via the user_data. */
|
||||
|
||||
struct link_menu_item *mitem = (struct link_menu_item *) user_data;
|
||||
const DilloUrl *url = mitem->url;
|
||||
const DilloUrl *origin = mitem->origin;
|
||||
Action *action = mitem->action;
|
||||
|
||||
/* Set the environment variables */
|
||||
setenv("url", URL_STR(url), 1);
|
||||
setenv("origin", URL_STR(origin), 1);
|
||||
|
||||
if (fork() == 0) {
|
||||
/* Child */
|
||||
errno = 0;
|
||||
int ret = system(action->cmd);
|
||||
if (ret == -1) {
|
||||
MSG("Cannot run '%s': %s\n", action->cmd, strerror(errno));
|
||||
exit(1);
|
||||
} else if (ret != 0) {
|
||||
MSG("Command exited with '%d': %s\n", ret, action->cmd);
|
||||
exit(1);
|
||||
} else {
|
||||
/* All good, terminate the child */
|
||||
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;
|
||||
|
||||
Dlist *actions = a_Actions_link_get();
|
||||
int nactions = dList_length(actions);
|
||||
|
||||
/* Count static menu entries */
|
||||
int nstatic = 0;
|
||||
while (link_menu_[nstatic].text)
|
||||
nstatic++;
|
||||
|
||||
int ntotal = nstatic + nactions;
|
||||
link_menu = (Fl_Menu_Item *) calloc(ntotal + 1, sizeof(Fl_Menu_Item));
|
||||
link_menu_item = (struct link_menu_item *) calloc(nactions, 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 < nactions; i++) {
|
||||
Action *action = (Action *) dList_nth_data(actions, i);
|
||||
struct link_menu_item *mitem = &link_menu_item[i];
|
||||
mitem->url = NULL; /* Not known yet */
|
||||
mitem->action = action;
|
||||
|
||||
Fl_Menu_Item *item = &link_menu[nstatic + i];
|
||||
item->text = action->label;
|
||||
item->callback_ = Menu_open_url_action_cb;
|
||||
item->user_data_ = mitem;
|
||||
}
|
||||
|
||||
return link_menu;
|
||||
}
|
||||
|
||||
static void Menu_set_link_menu_user_data(const DilloUrl *url, const DilloUrl *page_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_;
|
||||
/* Set the url and origin */
|
||||
mitem->url = url;
|
||||
mitem->origin = page_url;
|
||||
} else {
|
||||
link_menu[i].user_data_ = (void *) url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Link popup menu (construction & popup)
|
||||
*/
|
||||
void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url, const DilloUrl *page_url)
|
||||
{
|
||||
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, page_url);
|
||||
|
||||
a_Timeout_add(0.0, Menu_popup_cb, (void*)&link_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Image popup menu (construction & popup)
|
||||
*/
|
||||
void a_Menu_image_popup(BrowserWindow *bw, const DilloUrl *url,
|
||||
bool_t loaded_img, DilloUrl *page_url,
|
||||
DilloUrl *link_url)
|
||||
{
|
||||
static DilloUrl *popup_page_url = NULL;
|
||||
static DilloUrl *popup_link_url = NULL;
|
||||
static Fl_Menu_Item pm[] = {
|
||||
{"Isolate image", 0, Menu_open_url_cb,0,0,0,0,0,0},
|
||||
{"Open image in new tab", 0, Menu_open_url_nt_cb,0,0,0,0,0,0},
|
||||
{"Open image in new window", 0, Menu_open_url_nw_cb, 0, FL_MENU_DIVIDER,
|
||||
0,0,0,0},
|
||||
{"Load image", 0, Menu_load_images_cb,0,0,0,0,0,0},
|
||||
{"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, 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};
|
||||
|
||||
popup_x = Fl::event_x();
|
||||
popup_y = Fl::event_y();
|
||||
popup_bw = bw;
|
||||
a_Url_free(popup_url);
|
||||
popup_url = a_Url_dup(url);
|
||||
a_Url_free(popup_page_url);
|
||||
popup_page_url = a_Url_dup(page_url);
|
||||
a_Url_free(popup_link_url);
|
||||
popup_link_url = a_Url_dup(link_url);
|
||||
|
||||
|
||||
pm[0].user_data(popup_url);
|
||||
pm[1].user_data(popup_url);
|
||||
pm[2].user_data(popup_url);
|
||||
|
||||
if (loaded_img) {
|
||||
pm[3].deactivate();
|
||||
} else {
|
||||
pm[3].activate();
|
||||
pm[3].user_data(popup_page_url);
|
||||
}
|
||||
|
||||
pm[4].user_data(popup_url);
|
||||
pm[5].user_data(popup_url);
|
||||
pm[6].user_data(popup_url);
|
||||
|
||||
if (link_url) {
|
||||
pm[7].activate();
|
||||
Menu_set_link_menu_user_data(popup_link_url, popup_page_url);
|
||||
} else {
|
||||
pm[7].deactivate();
|
||||
}
|
||||
|
||||
a_Timeout_add(0.0, Menu_popup_cb, (void*)&image_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Form popup menu (construction & popup)
|
||||
*/
|
||||
void a_Menu_form_popup(BrowserWindow *bw, const DilloUrl *page_url,
|
||||
void *formptr, bool_t hidvis)
|
||||
{
|
||||
static bool hiddens_visible;
|
||||
static Fl_Menu_Item pm[] = {
|
||||
{"Submit form", 0, Menu_form_submit_cb,0,0,0,0,0,0},
|
||||
{"Reset form", 0, Menu_form_reset_cb,0,0,0,0,0,0},
|
||||
{0, 0, Menu_form_hiddens_cb, &hiddens_visible, 0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0}
|
||||
};
|
||||
static Menu_popup_data_t form_data = {"Form menu", NULL, pm};
|
||||
|
||||
popup_x = Fl::event_x();
|
||||
popup_y = Fl::event_y();
|
||||
popup_bw = bw;
|
||||
a_Url_free(popup_url);
|
||||
popup_url = a_Url_dup(page_url);
|
||||
popup_form = formptr;
|
||||
|
||||
hiddens_visible = hidvis;
|
||||
pm[2].label(hiddens_visible ? "Hide hiddens": "Show hiddens");
|
||||
|
||||
a_Timeout_add(0.0, Menu_popup_cb, (void*)&form_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* File popup menu (construction & popup)
|
||||
*/
|
||||
void a_Menu_file_popup(BrowserWindow *bw, void *v_wid)
|
||||
{
|
||||
Fl_Widget *wid = (Fl_Widget*)v_wid;
|
||||
|
||||
static Fl_Menu_Item pm[] = {
|
||||
{"New tab", Keys::getShortcut(KEYS_NEW_TAB), filemenu_cb,
|
||||
(void*)"nt",0,0,0,0,0},
|
||||
{"New window", Keys::getShortcut(KEYS_NEW_WINDOW), filemenu_cb,
|
||||
(void*)"nw", FL_MENU_DIVIDER,0,0,0,0},
|
||||
{"Open file...", Keys::getShortcut(KEYS_OPEN), filemenu_cb,
|
||||
(void*)"of",0,0,0,0,0},
|
||||
{"Open URL...", Keys::getShortcut(KEYS_GOTO), filemenu_cb,
|
||||
(void*)"ou",0,0,0,0,0},
|
||||
{"Close", Keys::getShortcut(KEYS_CLOSE_TAB), filemenu_cb,
|
||||
(void*)"cw", FL_MENU_DIVIDER,0,0,0,0},
|
||||
{"Exit Dillo", Keys::getShortcut(KEYS_CLOSE_ALL), filemenu_cb,
|
||||
(void*)"ed",0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0}
|
||||
};
|
||||
|
||||
popup_bw = bw;
|
||||
popup_x = wid->x();
|
||||
popup_y = wid->y() + wid->h();
|
||||
a_Url_free(popup_url);
|
||||
popup_url = NULL;
|
||||
|
||||
//pm->label(wid->visible() ? NULL : "File");
|
||||
a_Timeout_add(0.0, Menu_simple_popup_cb, (void*)pm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bugmeter popup menu (construction & popup)
|
||||
*/
|
||||
void a_Menu_bugmeter_popup(BrowserWindow *bw, const DilloUrl *url)
|
||||
{
|
||||
static Fl_Menu_Item pm[] = {
|
||||
{"Validate URL with W3C Nu validator (HTML5 only)", 0,
|
||||
Menu_bugmeter_validate_w3c_nu_cb,0,0,0,0,0,0},
|
||||
{"Validate URL with W3C validator (HTML 4.01 and older)", 0,
|
||||
Menu_bugmeter_validate_w3c_cb,0,FL_MENU_DIVIDER,0,0,0,0},
|
||||
{"About bug meter", 0,
|
||||
Menu_bugmeter_about_cb,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0}
|
||||
};
|
||||
|
||||
popup_x = Fl::event_x();
|
||||
popup_y = Fl::event_y();
|
||||
popup_bw = bw;
|
||||
a_Url_free(popup_url);
|
||||
popup_url = a_Url_dup(url);
|
||||
|
||||
a_Timeout_add(0.0, Menu_simple_popup_cb, (void*)pm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigation History popup menu (construction & popup).
|
||||
*
|
||||
* direction: {backward = -1, forward = 1}
|
||||
*/
|
||||
void a_Menu_history_popup(BrowserWindow *bw, int x, int y, int direction)
|
||||
{
|
||||
static Fl_Menu_Item *pm = 0;
|
||||
int i, n;
|
||||
|
||||
popup_bw = bw;
|
||||
popup_x = x;
|
||||
popup_y = y;
|
||||
history_direction = direction;
|
||||
|
||||
// TODO: hook popdown event with delete or similar.
|
||||
if (pm)
|
||||
delete [] pm;
|
||||
if (history_list)
|
||||
dFree(history_list);
|
||||
|
||||
// Get a list of URLs for this popup
|
||||
history_list = a_UIcmd_get_history(bw, direction);
|
||||
|
||||
for (n = 0; history_list[n] != -1; n++)
|
||||
;
|
||||
|
||||
pm = new Fl_Menu_Item[n + 1];
|
||||
memset(pm, '\0', (n + 1) * sizeof(Fl_Menu_Item));
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
pm[i].label(FL_NORMAL_LABEL, a_History_get_title(history_list[i], 1));
|
||||
pm[i].callback(Menu_history_cb, INT2VOIDP(i+1));
|
||||
}
|
||||
a_Timeout_add(0.0, Menu_simple_popup_cb, (void*)pm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle use of remote stylesheets
|
||||
*/
|
||||
static void Menu_remote_css_cb(Fl_Widget *wid, void*)
|
||||
{
|
||||
Fl_Menu_Item *item = (Fl_Menu_Item*) wid;
|
||||
|
||||
item->flags ^= FL_MENU_VALUE;
|
||||
prefs.load_stylesheets = item->flags & FL_MENU_VALUE ? 1 : 0;
|
||||
a_UIcmd_repush(popup_bw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle use of embedded CSS style
|
||||
*/
|
||||
static void Menu_embedded_css_cb(Fl_Widget *wid, void*)
|
||||
{
|
||||
Fl_Menu_Item *item = (Fl_Menu_Item*) wid;
|
||||
|
||||
item->flags ^= FL_MENU_VALUE;
|
||||
prefs.parse_embedded_css = item->flags & FL_MENU_VALUE ? 1 : 0;
|
||||
a_UIcmd_repush(popup_bw);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Toggle use of force https mode
|
||||
*/
|
||||
static void Menu_force_https_cb(Fl_Widget *wid, void*)
|
||||
{
|
||||
Fl_Menu_Item *item = (Fl_Menu_Item*) wid;
|
||||
|
||||
item->flags ^= FL_MENU_VALUE;
|
||||
prefs.http_force_https = item->flags & FL_MENU_VALUE ? 1 : 0;
|
||||
a_UIcmd_repush(popup_bw);
|
||||
}
|
||||
|
||||
static void Menu_panel_change_cb(Fl_Widget*, void *user_data)
|
||||
{
|
||||
UI *ui = (UI*)popup_bw->ui;
|
||||
|
||||
if (VOIDP2INT(user_data) == 10) /* small icons */
|
||||
ui->change_panel(ui->get_panelsize(), !ui->get_smallicons());
|
||||
else
|
||||
ui->change_panel(VOIDP2INT(user_data), ui->get_smallicons());
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle loading of images -- and load them if enabling.
|
||||
*/
|
||||
static void Menu_imgload_toggle_cb(Fl_Widget *wid, void*)
|
||||
{
|
||||
Fl_Menu_Item *item = (Fl_Menu_Item*) wid;
|
||||
|
||||
item->flags ^= FL_MENU_VALUE;
|
||||
|
||||
if ((prefs.load_images = item->flags & FL_MENU_VALUE ? 1 : 0)) {
|
||||
void *doc = a_Bw_get_current_doc(popup_bw);
|
||||
|
||||
if (doc) {
|
||||
DilloUrl *pattern = NULL;
|
||||
a_Html_load_images(doc, pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle loading of background images.
|
||||
*/
|
||||
static void Menu_bgimg_load_toggle_cb(Fl_Widget *wid, void*)
|
||||
{
|
||||
Fl_Menu_Item *item = (Fl_Menu_Item*) wid;
|
||||
|
||||
item->flags ^= FL_MENU_VALUE;
|
||||
prefs.load_background_images = item->flags & FL_MENU_VALUE ? 1 : 0;
|
||||
a_UIcmd_repush(popup_bw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tools popup menu (construction & popup).
|
||||
*/
|
||||
void a_Menu_tools_popup(BrowserWindow *bw, int x, int y)
|
||||
{
|
||||
const Fl_Menu_Item *item;
|
||||
UI *ui = (UI*)bw->ui;
|
||||
|
||||
static Fl_Menu_Item pm[] = {
|
||||
{"Use remote CSS", 0, Menu_remote_css_cb, 0, FL_MENU_TOGGLE,0,0,0,0},
|
||||
{"Use embedded CSS", 0, Menu_embedded_css_cb, 0,
|
||||
FL_MENU_TOGGLE|FL_MENU_DIVIDER,0,0,0,0},
|
||||
{"Load images", 0, Menu_imgload_toggle_cb, 0,
|
||||
FL_MENU_TOGGLE,0,0,0,0},
|
||||
{"Load background images", 0, Menu_bgimg_load_toggle_cb, 0,
|
||||
FL_MENU_TOGGLE|FL_MENU_DIVIDER,0,0,0,0},
|
||||
{"Force HTTPS", 0, Menu_force_https_cb, 0,
|
||||
FL_MENU_TOGGLE|FL_MENU_DIVIDER,0,0,0,0},
|
||||
{"Panel size", 0, Menu_nop_cb, (void*)"Submenu1", FL_SUBMENU,0,0,0,0},
|
||||
{"tiny", 0,Menu_panel_change_cb,(void*)0,FL_MENU_RADIO,0,0,0,0},
|
||||
{"small", 0,Menu_panel_change_cb,(void*)1,FL_MENU_RADIO,0,0,0,0},
|
||||
{"medium",0,Menu_panel_change_cb,(void*)2,
|
||||
FL_MENU_RADIO|FL_MENU_DIVIDER,0,0,0,0},
|
||||
{"small icons", 0,Menu_panel_change_cb,(void*)10,
|
||||
FL_MENU_TOGGLE,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0}
|
||||
};
|
||||
|
||||
popup_bw = bw;
|
||||
int cur_panelsize = ui->get_panelsize();
|
||||
int cur_smallicons = ui->get_smallicons();
|
||||
|
||||
if (prefs.load_stylesheets)
|
||||
pm[0].set();
|
||||
if (prefs.parse_embedded_css)
|
||||
pm[1].set();
|
||||
if (prefs.load_images)
|
||||
pm[2].set();
|
||||
if (prefs.load_background_images)
|
||||
pm[3].set();
|
||||
if (prefs.http_force_https)
|
||||
pm[4].set();
|
||||
pm[6+cur_panelsize].setonly();
|
||||
cur_smallicons ? pm[9].set() : pm[9].clear();
|
||||
|
||||
item = pm->popup(x, y);
|
||||
if (item) {
|
||||
((Fl_Widget *)item)->do_callback();
|
||||
}
|
||||
}
|
||||
|
||||
30
src/menu.hh
Normal file
30
src/menu.hh
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef __MENU_HH__
|
||||
#define __MENU_HH__
|
||||
|
||||
#include "bw.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void a_Menu_page_popup(BrowserWindow *bw, const DilloUrl *url,
|
||||
bool_t has_bugs, void *v_cssUrls);
|
||||
void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url,
|
||||
const DilloUrl *page_url);
|
||||
void a_Menu_image_popup(BrowserWindow *bw, const DilloUrl *url,
|
||||
bool_t loaded_img, DilloUrl *page_url,
|
||||
DilloUrl *link_url);
|
||||
void a_Menu_form_popup(BrowserWindow *bw, const DilloUrl *page_url,
|
||||
void *vform, bool_t showing_hiddens);
|
||||
void a_Menu_file_popup(BrowserWindow *bw, void *v_wid);
|
||||
void a_Menu_bugmeter_popup(BrowserWindow *bw, const DilloUrl *url);
|
||||
void a_Menu_history_popup(BrowserWindow *bw, int x, int y, int direction);
|
||||
void a_Menu_tools_popup(BrowserWindow *bw, int x, int y);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* MENU_HH */
|
||||
|
||||
479
src/misc.c
Normal file
479
src/misc.c
Normal file
@ -0,0 +1,479 @@
|
||||
/*
|
||||
* File: misc.c
|
||||
*
|
||||
* Copyright (C) 2000-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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "utf8.hh"
|
||||
#include "msg.h"
|
||||
#include "misc.h"
|
||||
|
||||
/**
|
||||
* Escape characters as %XX sequences.
|
||||
* Return value: New string.
|
||||
*/
|
||||
char *a_Misc_escape_chars(const char *str, const char *esc_set)
|
||||
{
|
||||
static const char *const hex = "0123456789ABCDEF";
|
||||
char *p = NULL;
|
||||
Dstr *dstr;
|
||||
int i;
|
||||
|
||||
dstr = dStr_sized_new(64);
|
||||
for (i = 0; str[i]; ++i) {
|
||||
if (str[i] <= 0x1F || str[i] == 0x7F || strchr(esc_set, str[i])) {
|
||||
dStr_append_c(dstr, '%');
|
||||
dStr_append_c(dstr, hex[(str[i] >> 4) & 15]);
|
||||
dStr_append_c(dstr, hex[str[i] & 15]);
|
||||
} else {
|
||||
dStr_append_c(dstr, str[i]);
|
||||
}
|
||||
}
|
||||
p = dstr->str;
|
||||
dStr_free(dstr, FALSE);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
#define TAB_SIZE 8
|
||||
/**
|
||||
* Takes a string and converts any tabs to spaces.
|
||||
*/
|
||||
int
|
||||
a_Misc_expand_tabs(char **start, char *end, char *buf, int buflen)
|
||||
{
|
||||
int j, pos = 0, written = 0, old_pos, char_len;
|
||||
uint_t code;
|
||||
static const int combining_char_space = 32;
|
||||
|
||||
while (*start < end && written < buflen - TAB_SIZE - combining_char_space) {
|
||||
code = a_Utf8_decode(*start, end, &char_len);
|
||||
|
||||
if (code == '\t') {
|
||||
/* Fill with whitespaces until the next tab. */
|
||||
old_pos = pos;
|
||||
pos += TAB_SIZE - (pos % TAB_SIZE);
|
||||
for (j = old_pos; j < pos; j++)
|
||||
buf[written++] = ' ';
|
||||
} else {
|
||||
assert(char_len <= 4);
|
||||
for (j = 0; j < char_len; j++)
|
||||
buf[written++] = (*start)[j];
|
||||
pos++;
|
||||
}
|
||||
|
||||
*start += char_len;
|
||||
}
|
||||
|
||||
/* If following chars are combining chars (e.g. accents) add them to the
|
||||
* buffer. We have reserved combining_char_space bytes for this.
|
||||
* If there should be more combining chars, we split nevertheless.
|
||||
*/
|
||||
while (*start < end && written < buflen - 4) {
|
||||
code = a_Utf8_decode(*start, end, &char_len);
|
||||
|
||||
if (! a_Utf8_combining_char(code))
|
||||
break;
|
||||
|
||||
assert(char_len <= 4);
|
||||
for (j = 0; j < char_len; j++)
|
||||
buf[written++] = (*start)[j];
|
||||
|
||||
*start += char_len;
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
/* TODO: could use dStr ADT! */
|
||||
typedef struct {
|
||||
const char *str;
|
||||
int len;
|
||||
} ContentType_t;
|
||||
|
||||
static const ContentType_t MimeTypes[] = {
|
||||
{ "application/octet-stream", 24 },
|
||||
{ "application/xhtml+xml", 21 },
|
||||
{ "text/html", 9 },
|
||||
{ "text/plain", 10 },
|
||||
{ "image/gif", 9 },
|
||||
{ "image/png", 9 },
|
||||
{ "image/jpeg", 10 },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
DT_OCTET_STREAM = 0,
|
||||
DT_PLACEHOLDER,
|
||||
DT_TEXT_HTML,
|
||||
DT_TEXT_PLAIN,
|
||||
DT_IMAGE_GIF,
|
||||
DT_IMAGE_PNG,
|
||||
DT_IMAGE_JPG,
|
||||
} DetectedContentType;
|
||||
|
||||
/**
|
||||
* Detects 'Content-Type' from a data stream sample.
|
||||
*
|
||||
* It uses the magic(5) logic from file(1). Currently, it
|
||||
* only checks the few mime types that Dillo supports.
|
||||
*
|
||||
* 'Data' is a pointer to the first bytes of the raw data.
|
||||
*
|
||||
* Return value: (0 on success, 1 on doubt, 2 on lack of data).
|
||||
*/
|
||||
int a_Misc_get_content_type_from_data(void *Data, size_t Size, const char **PT)
|
||||
{
|
||||
size_t i, non_ascci, non_ascci_text, bin_chars;
|
||||
char *p = Data;
|
||||
int st = 1; /* default to "doubt' */
|
||||
DetectedContentType Type = DT_OCTET_STREAM; /* default to binary */
|
||||
|
||||
/* HTML try */
|
||||
for (i = 0; i < Size && dIsspace(p[i]); ++i);
|
||||
if ((Size - i >= 5 && !dStrnAsciiCasecmp(p+i, "<html", 5)) ||
|
||||
(Size - i >= 5 && !dStrnAsciiCasecmp(p+i, "<head", 5)) ||
|
||||
(Size - i >= 6 && !dStrnAsciiCasecmp(p+i, "<title", 6)) ||
|
||||
(Size - i >= 14 && !dStrnAsciiCasecmp(p+i, "<!doctype html", 14)) ||
|
||||
/* this line is workaround for FTP through the Squid proxy and Doxygen */
|
||||
(Size - i >= 9 && !dStrnAsciiCasecmp(p+i, "<!-- HTML", 9))) {
|
||||
|
||||
Type = DT_TEXT_HTML;
|
||||
st = 0;
|
||||
/* Images */
|
||||
} else if (Size >= 4 && !strncmp(p, "GIF8", 4)) {
|
||||
Type = DT_IMAGE_GIF;
|
||||
st = 0;
|
||||
} else if (Size >= 4 && !strncmp(p, "\x89PNG", 4)) {
|
||||
Type = DT_IMAGE_PNG;
|
||||
st = 0;
|
||||
} else if (Size >= 2 && !strncmp(p, "\xff\xd8", 2)) {
|
||||
/* JPEG has the first 2 bytes set to 0xffd8 in BigEndian - looking
|
||||
* at the character representation should be machine independent. */
|
||||
Type = DT_IMAGE_JPG;
|
||||
st = 0;
|
||||
|
||||
/* Text */
|
||||
} else {
|
||||
/* Heuristic for "text/plain"
|
||||
* {ASCII, LATIN1, UTF8, KOI8-R, CP-1251}
|
||||
* All in the above set regard [00-31] as control characters.
|
||||
* LATIN1: [7F-9F] unused
|
||||
* CP-1251 {7F,98} unused (two characters).
|
||||
*
|
||||
* We'll use [0-31] as indicators of non-text content.
|
||||
* Better heuristics are welcomed! :-) */
|
||||
|
||||
non_ascci = non_ascci_text = bin_chars = 0;
|
||||
Size = MIN (Size, 256);
|
||||
for (i = 0; i < Size; i++) {
|
||||
int ch = (uchar_t) p[i];
|
||||
if (ch < 32 && !dIsspace(ch))
|
||||
++bin_chars;
|
||||
if (ch > 126)
|
||||
++non_ascci;
|
||||
if (ch > 190)
|
||||
++non_ascci_text;
|
||||
}
|
||||
if (bin_chars == 0 && (non_ascci - non_ascci_text) <= Size/10) {
|
||||
/* Let's say text: if "rare" chars are <= 10% */
|
||||
Type = DT_TEXT_PLAIN;
|
||||
} else if (Size > 0) {
|
||||
/* a special check for UTF-8 */
|
||||
Size = a_Utf8_end_of_char(p, Size - 1) + 1;
|
||||
if (a_Utf8_test(p, Size) > 0)
|
||||
Type = DT_TEXT_PLAIN;
|
||||
}
|
||||
if (Size >= 256)
|
||||
st = 0;
|
||||
}
|
||||
|
||||
*PT = MimeTypes[Type].str;
|
||||
return st;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse Content-Type string, e.g., "text/html; charset=utf-8".
|
||||
* Content-Type is defined in RFC 2045 section 5.1.
|
||||
*/
|
||||
void a_Misc_parse_content_type(const char *type, char **major, char **minor,
|
||||
char **charset)
|
||||
{
|
||||
static const char tspecials_space[] = "()<>@,;:\\\"/[]?= ";
|
||||
const char *str, *s;
|
||||
|
||||
if (major)
|
||||
*major = NULL;
|
||||
if (minor)
|
||||
*minor = NULL;
|
||||
if (charset)
|
||||
*charset = NULL;
|
||||
if (!(str = type))
|
||||
return;
|
||||
|
||||
for (s = str; *s && d_isascii((uchar_t)*s) && !iscntrl((uchar_t)*s) &&
|
||||
!strchr(tspecials_space, *s); s++) ;
|
||||
if (major)
|
||||
*major = dStrndup(str, s - str);
|
||||
|
||||
if (*s == '/') {
|
||||
for (str = ++s; *s && d_isascii((uchar_t)*s) && !iscntrl((uchar_t)*s) &&
|
||||
!strchr(tspecials_space, *s); s++) ;
|
||||
if (minor)
|
||||
*minor = dStrndup(str, s - str);
|
||||
}
|
||||
if (charset && *s &&
|
||||
(dStrnAsciiCasecmp(type, "text/", 5) == 0 ||
|
||||
dStrnAsciiCasecmp(type, "application/xhtml+xml", 21) == 0)) {
|
||||
/* "charset" parameter defined for text media type in RFC 2046,
|
||||
* application/xhtml+xml in RFC 3236.
|
||||
*
|
||||
* Note that RFC 3023 lists some main xml media types and provides
|
||||
* the convention of using the "+xml" minor type suffix for other
|
||||
* xml types, so it would be reasonable to check for that suffix if
|
||||
* we have need to care about various xml types someday.
|
||||
*/
|
||||
const char terminators[] = " ;\t";
|
||||
const char key[] = "charset";
|
||||
|
||||
if ((s = dStriAsciiStr(str, key)) &&
|
||||
(s == str || strchr(terminators, s[-1]))) {
|
||||
s += sizeof(key) - 1;
|
||||
for ( ; *s == ' ' || *s == '\t'; ++s);
|
||||
if (*s == '=') {
|
||||
size_t len;
|
||||
for (++s; *s == ' ' || *s == '\t'; ++s);
|
||||
if ((len = strcspn(s, terminators))) {
|
||||
if (*s == '"' && s[len-1] == '"' && len > 1) {
|
||||
/* quoted string */
|
||||
s++;
|
||||
len -= 2;
|
||||
}
|
||||
*charset = dStrndup(s, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two Content-Type strings.
|
||||
* Return 0 if they are equivalent, and 1 otherwise.
|
||||
*/
|
||||
int a_Misc_content_type_cmp(const char *ct1, const char *ct2)
|
||||
{
|
||||
char *major1, *major2, *minor1, *minor2, *charset1, *charset2;
|
||||
int ret;
|
||||
|
||||
if ((!ct1 || !*ct1) && (!ct2 || !*ct2))
|
||||
return 0;
|
||||
if ((!ct1 || !*ct1) || (!ct2 || !*ct2))
|
||||
return 1;
|
||||
|
||||
a_Misc_parse_content_type(ct1, &major1, &minor1, &charset1);
|
||||
a_Misc_parse_content_type(ct2, &major2, &minor2, &charset2);
|
||||
|
||||
if (major1 && major2 && !dStrAsciiCasecmp(major1, major2) &&
|
||||
minor1 && minor2 && !dStrAsciiCasecmp(minor1, minor2) &&
|
||||
((!charset1 && !charset2) ||
|
||||
(charset1 && charset2 && !dStrAsciiCasecmp(charset1, charset2)) ||
|
||||
(!charset1 && charset2 && !dStrAsciiCasecmp(charset2, "UTF-8")) ||
|
||||
(charset1 && !charset2 && !dStrAsciiCasecmp(charset1, "UTF-8")))) {
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = 1;
|
||||
}
|
||||
dFree(major1); dFree(major2);
|
||||
dFree(minor1); dFree(minor2);
|
||||
dFree(charset1); dFree(charset2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the server-supplied 'Content-Type' against our detected type.
|
||||
* (some servers seem to default to "text/plain").
|
||||
*
|
||||
* @return
|
||||
* - 0, if they match
|
||||
* - -1, if a mismatch is detected
|
||||
*
|
||||
* There are many MIME types Dillo doesn't know, they're handled
|
||||
* as "application/octet-stream" (as the SPEC says).
|
||||
*
|
||||
* A mismatch happens when receiving a binary stream as
|
||||
* "text/plain" or "text/html", or an image that's not an image of its kind.
|
||||
*
|
||||
* Note: this is a basic security procedure.
|
||||
*
|
||||
*/
|
||||
int a_Misc_content_type_check(const char *EntryType, const char *DetectedType)
|
||||
{
|
||||
int i;
|
||||
int st = -1;
|
||||
|
||||
_MSG("Type check: [Srv: %s Det: %s]\n", EntryType, DetectedType);
|
||||
|
||||
if (!EntryType)
|
||||
return 0; /* there's no mismatch without server type */
|
||||
|
||||
for (i = 1; MimeTypes[i].str; ++i)
|
||||
if (dStrnAsciiCasecmp(EntryType, MimeTypes[i].str, MimeTypes[i].len) ==0)
|
||||
break;
|
||||
|
||||
if (!MimeTypes[i].str) {
|
||||
/* type not found, no mismatch */
|
||||
st = 0;
|
||||
} else if (dStrnAsciiCasecmp(EntryType, "image/", 6) == 0 &&
|
||||
!dStrnAsciiCasecmp(DetectedType, MimeTypes[i].str,
|
||||
MimeTypes[i].len)){
|
||||
/* An image, and there's an exact match */
|
||||
st = 0;
|
||||
} else if (dStrnAsciiCasecmp(EntryType, "text/", 5) ||
|
||||
dStrnAsciiCasecmp(DetectedType, "application/", 12)) {
|
||||
/* Not an application sent as text */
|
||||
st = 0;
|
||||
} else if (dStrnAsciiCasecmp(EntryType, "application/xhtml+xml", 21) &&
|
||||
dStrnAsciiCasecmp(DetectedType, "text/html", 9)) {
|
||||
/* XML version of HTML */
|
||||
st = 0;
|
||||
}
|
||||
_MSG("Type check: %s\n", st == 0 ? "MATCH" : "MISMATCH");
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a geometry string.
|
||||
*/
|
||||
int a_Misc_parse_geometry(char *str, int *x, int *y, int *w, int *h)
|
||||
{
|
||||
char *p, *t1, *t2;
|
||||
int n1, n2;
|
||||
int ret = 0;
|
||||
|
||||
if ((p = strchr(str, 'x')) || (p = strchr(str, 'X'))) {
|
||||
n1 = strtol(str, &t1, 10);
|
||||
n2 = strtol(++p, &t2, 10);
|
||||
if (t1 != str && t2 != p) {
|
||||
*w = n1;
|
||||
*h = n2;
|
||||
ret = 1;
|
||||
/* parse x,y now */
|
||||
p = t2;
|
||||
n1 = strtol(p, &t1, 10);
|
||||
n2 = strtol(t1, &t2, 10);
|
||||
if (t1 != p && t2 != t1) {
|
||||
*x = n1;
|
||||
*y = n2;
|
||||
}
|
||||
}
|
||||
}
|
||||
_MSG("geom: w,h,x,y = (%d,%d,%d,%d)\n", *w, *h, *x, *y);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse dillorc's search_url string (`[<label> ]<url>`)
|
||||
* Return value: -1 on error, 0 on success (and label and urlstr pointers)
|
||||
*/
|
||||
int a_Misc_parse_search_url(char *source, char **label, char **urlstr)
|
||||
{
|
||||
static char buf[32];
|
||||
char *p, *q;
|
||||
int ret = -1;
|
||||
|
||||
if ((p = strrchr(source, ' '))) {
|
||||
/* label and url pair */
|
||||
strncpy(buf,source,MIN(p-source,31));
|
||||
buf[MIN(p-source,31)] = 0;
|
||||
source = p+1;
|
||||
if ((p = strchr(source, '/')) && p[1] && (q = strchr(p+2,'/'))) {
|
||||
*urlstr = source;
|
||||
ret = 0;
|
||||
}
|
||||
} else {
|
||||
/* url only, make a custom label */
|
||||
if ((p = strchr(source, '/')) && p[1] && (q = strchr(p+2,'/'))) {
|
||||
strncpy(buf,p+2,MIN(q-p-2,31));
|
||||
buf[MIN(q-p-2,31)] = 0;
|
||||
*urlstr = source;
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
*label = buf;
|
||||
if (ret == -1)
|
||||
MSG("Invalid search_url: \"%s\"\n", source);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes string using base64 encoding.
|
||||
* Return value: new string or NULL if input string is empty.
|
||||
*/
|
||||
char *a_Misc_encode_base64(const char *in)
|
||||
{
|
||||
static const char *const base64_hex = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
char *out = NULL;
|
||||
int len, i = 0;
|
||||
|
||||
if (in == NULL) return NULL;
|
||||
len = strlen(in);
|
||||
|
||||
out = (char *)dMalloc((len + 2) / 3 * 4 + 1);
|
||||
|
||||
for (; len >= 3; len -= 3) {
|
||||
out[i++] = base64_hex[in[0] >> 2];
|
||||
out[i++] = base64_hex[((in[0]<<4) & 0x30) | (in[1]>>4)];
|
||||
out[i++] = base64_hex[((in[1]<<2) & 0x3c) | (in[2]>>6)];
|
||||
out[i++] = base64_hex[in[2] & 0x3f];
|
||||
in += 3;
|
||||
}
|
||||
|
||||
if (len > 0) {
|
||||
unsigned char fragment;
|
||||
out[i++] = base64_hex[in[0] >> 2];
|
||||
fragment = (in[0] << 4) & 0x30;
|
||||
if (len > 1) fragment |= in[1] >> 4;
|
||||
out[i++] = base64_hex[fragment];
|
||||
out[i++] = (len < 2) ? '=' : base64_hex[(in[1] << 2) & 0x3c];
|
||||
out[i++] = '=';
|
||||
}
|
||||
out[i] = '\0';
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a local file into a dStr.
|
||||
* Return value: dStr on success, NULL on error.
|
||||
* TODO: a filesize threshold may be implemented.
|
||||
*/
|
||||
Dstr *a_Misc_file2dstr(const char *filename)
|
||||
{
|
||||
FILE *F_in;
|
||||
int n;
|
||||
char buf[4096];
|
||||
Dstr *dstr = NULL;
|
||||
|
||||
if ((F_in = fopen(filename, "r"))) {
|
||||
dstr = dStr_sized_new(4096);
|
||||
while ((n = fread (buf, 1, 4096, F_in)) > 0) {
|
||||
dStr_append_l(dstr, buf, n);
|
||||
}
|
||||
fclose(F_in);
|
||||
}
|
||||
return dstr;
|
||||
}
|
||||
30
src/misc.h
Normal file
30
src/misc.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef __DILLO_MISC_H__
|
||||
#define __DILLO_MISC_H__
|
||||
|
||||
#include <stddef.h> /* for size_t */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define d_isascii(c) (((c) & ~0x7f) == 0)
|
||||
|
||||
char *a_Misc_escape_chars(const char *str, const char *esc_set);
|
||||
int a_Misc_expand_tabs(char **start, char *end, char *buf, int buflen);
|
||||
int a_Misc_get_content_type_from_data(void *Data, size_t Size,const char **PT);
|
||||
int a_Misc_content_type_check(const char *EntryType, const char *DetectedType);
|
||||
void a_Misc_parse_content_type(const char *str, char **major, char **minor,
|
||||
char **charset);
|
||||
int a_Misc_content_type_cmp(const char* ct1, const char *ct2);
|
||||
int a_Misc_parse_geometry(char *geom, int *x, int *y, int *w, int *h);
|
||||
int a_Misc_parse_search_url(char *source, char **label, char **urlstr);
|
||||
char *a_Misc_encode_base64(const char *in);
|
||||
Dstr *a_Misc_file2dstr(const char *filename);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __DILLO_MISC_H__ */
|
||||
|
||||
27
src/msg.h
Normal file
27
src/msg.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef __MSG_H__
|
||||
#define __MSG_H__
|
||||
|
||||
#include <stdio.h>
|
||||
#include "prefs.h"
|
||||
|
||||
/*
|
||||
* You can disable any MSG* macro by adding the '_' prefix.
|
||||
*/
|
||||
#define _MSG(...)
|
||||
#define _MSG_WARN(...)
|
||||
#define _MSG_HTTP(...)
|
||||
|
||||
#define MSG_INNARDS(prefix, ...) \
|
||||
D_STMT_START { \
|
||||
if (prefs.show_msg){ \
|
||||
printf(prefix __VA_ARGS__); \
|
||||
fflush (stdout); \
|
||||
} \
|
||||
} D_STMT_END
|
||||
|
||||
#define MSG(...) MSG_INNARDS("", __VA_ARGS__)
|
||||
#define MSG_WARN(...) MSG_INNARDS("** WARNING **: ", __VA_ARGS__)
|
||||
#define MSG_ERR(...) MSG_INNARDS("** ERROR **: ", __VA_ARGS__)
|
||||
#define MSG_HTTP(...) MSG_INNARDS("HTTP warning: ", __VA_ARGS__)
|
||||
|
||||
#endif /* __MSG_H__ */
|
||||
3207
src/nanosvg.h
Normal file
3207
src/nanosvg.h
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user