aboutsummaryrefslogtreecommitdiff
path: root/dpi/bookmarks.c
diff options
context:
space:
mode:
authorjcid <devnull@localhost>2007-10-07 00:36:34 +0200
committerjcid <devnull@localhost>2007-10-07 00:36:34 +0200
commit93715c46a99c96d6c866968312691ec9ab0f6a03 (patch)
tree573f19ec6aa740844f53a7c0eb7114f04096bf64 /dpi/bookmarks.c
Initial revision
Diffstat (limited to 'dpi/bookmarks.c')
-rw-r--r--dpi/bookmarks.c1716
1 files changed, 1716 insertions, 0 deletions
diff --git a/dpi/bookmarks.c b/dpi/bookmarks.c
new file mode 100644
index 00000000..b0f4ca8e
--- /dev/null
+++ b/dpi/bookmarks.c
@@ -0,0 +1,1716 @@
+/*
+ * Bookmarks server (chat version).
+ *
+ * NOTE: this code illustrates how to make a dpi-program.
+ *
+ * Copyright 2002-2006 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.
+ *
+ */
+
+/* Todo: this server is not assembling the received packets.
+ * This means it currently expects dillo to send full dpi tags
+ * within the socket; if that fails, everything stops.
+ * This is not hard to fix, mainly is a matter of expecting the
+ * final '>' of a tag.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <signal.h>
+#include "../dpip/dpip.h"
+#include "dpiutil.h"
+
+
+/*
+ * Debugging macros
+ */
+#define _MSG(...)
+#define MSG(...) printf("[bookmarks dpi]: " __VA_ARGS__)
+
+/* 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))
+
+#define DOCTYPE \
+ "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
+
+/*
+ * Notes on character escaping:
+ * - Basically things are saved unescaped and escaped when in memory.
+ * - &<>"' are escaped in titles and sections and saved unescaped.
+ * - ' is escaped as %27 in URLs and saved escaped.
+ */
+typedef struct {
+ int key;
+ int section;
+ char *url;
+ char *title;
+} BmRec;
+
+typedef struct {
+ int section;
+ char *title;
+
+ int o_sec; /* private, for normalization */
+} BmSec;
+
+
+/*
+ * Local data
+ */
+static char *Header = "Content-type: text/html\n\n";
+static char *BmFile = NULL;
+static time_t BmFileTimeStamp = 0;
+static Dlist *B_bms = NULL;
+static int bm_key = 0;
+
+static Dlist *B_secs = NULL;
+static int sec_key = 0;
+
+static int MODIFY_PAGE_NUM = 1;
+
+
+/*
+ * Forward declarations
+ */
+
+
+/* -- HTML templates ------------------------------------------------------- */
+
+char *mainpage_header =
+DOCTYPE
+"<html>\n"
+"<head>\n"
+"<title>Bookmarks</title>\n"
+"</head>\n"
+"<body bgcolor='#778899' link='black' vlink='brown'>\n"
+"<table border='1' cellpadding='0' width='100%'>\n"
+" <tr><td>\n"
+" <table width='100%' bgcolor='#b4b4b4'>\n"
+" <tr>\n"
+" <td>&nbsp;Bookmarks::</td>\n"
+" <td width='100%' align='right'>\n"
+" [<a href='dpi:/bm/modify'>modify</a>]\n"
+" </td></tr>\n"
+" </table></td></tr>\n"
+"</table>\n"
+"<br>\n";
+
+char *modifypage_header =
+DOCTYPE
+"<html>\n"
+"<head>\n"
+"<title>Bookmarks</title>\n"
+"</head>\n"
+"<body bgcolor='#778899' link='black' vlink='brown'>\n"
+"<table border='1' cellpadding='0' width='100%'>\n"
+" <tr><td>\n"
+" <table width='100%' bgcolor='#b4b4b4'>\n"
+" <tr>\n"
+" <td>&nbsp;Bookmarks :: modify</td></tr>\n"
+" </table></td></tr> \n"
+"</table> \n"
+"\n"
+"<form>\n"
+"<table width='100%' border='1' cellpadding='0'>\n"
+" <tr><td>\n"
+" <table width='100%' bgcolor='teal'>\n"
+" <tr>\n"
+" <td><b>Select&nbsp;an&nbsp;operation&nbsp;</b></td>\n"
+" <td><select name='operation'>\n"
+" <option value='none' selected>--\n"
+" <option value='delete'>Delete\n"
+" <option value='move'>Move\n"
+" <option value='modify'>Modify\n"
+" <option value='add_sec'>Add Section\n"
+" <option value='add_url'>Add URL\n"
+" </select></td>\n"
+" <td><b>,&nbsp;mark&nbsp;its&nbsp;operands,&nbsp;and&nbsp;</b></td>\n"
+" <td><input type='submit' name='submit' value='submit.'></td>\n"
+" <td width='100%'></td>\n"
+" </tr>\n"
+" </table></td></tr>\n"
+"</table>\n";
+
+char *mainpage_sections_header =
+"<table border='1' cellpadding='0' cellspacing='20' width='100%'>\n"
+" <tr valign='top'>\n"
+" <td>\n"
+" <table bgcolor='#b4b4b4' border='2' cellpadding='4' cellspacing='1'>\n"
+" <tr><td>\n"
+" <table width='100%' bgcolor='#b4b4b4'>\n"
+" <tr><td><small>Sections:</small></td></tr></table></td></tr>\n";
+
+char *modifypage_sections_header =
+"<table border='1' cellpadding='0' cellspacing='20' width='100%'>\n"
+" <tr valign='top'>\n"
+" <td>\n"
+" <table bgcolor='#b4b4b4' border='1'>\n"
+" <tr><td>\n"
+" <table width='100%' bgcolor='#b4b4b4'>\n"
+" <tr><td><small>Sections:</small></td></tr></table></td></tr>\n";
+
+char *mainpage_sections_item =
+" <tr><td align='center'>\n"
+" <a href='#s%d'>%s</a></td></tr>\n";
+
+char *modifypage_sections_item =
+" <tr><td>\n"
+" <table width='100%%' bgcolor='#b4b4b4'cellspacing='0' cellpadding='0'>\n"
+" <tr align='center'>"
+" <td width='1%%'><input type='checkbox' name='s%d'></td>\n"
+" <td><a href='#s%d'>%s</a></td></tr></table></td></tr>\n";
+
+char *mainpage_sections_footer =
+" </table>\n";
+
+char *modifypage_sections_footer =
+" </table>\n";
+
+char *mainpage_middle1 =
+" </td>\n"
+" <td width='100%'>\n";
+
+char *modifypage_middle1 =
+" </td>\n"
+" <td width='100%'>\n";
+
+char *mainpage_section_card_header =
+" <a name='s%d'></a>\n"
+" <table bgcolor='#bfbfbf' width='100%%' cellspacing='2'>\n"
+" <tr>\n"
+" <td bgcolor='#bf0c0c'><font color='white'><b>\n"
+" &nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;&nbsp;</b></font></td>\n"
+" <td bgcolor='white' width='100%%'>&nbsp;</td></tr>\n";
+
+char *modifypage_section_card_header =
+" <a name='s%d'></a>\n"
+" <table bgcolor='#bfbfbf' width='100%%' cellspacing='2'>\n"
+" <tr>\n"
+" <td bgcolor='#bf0c0c'><font color='white'><b>\n"
+" &nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;&nbsp;</b></font></td>\n"
+" <td bgcolor='white' width='100%%'>&nbsp;</td></tr>\n";
+
+char *mainpage_section_card_item =
+" <tr><td colspan='2'>\n"
+" <a href='%s'>%s</a> </td></tr>\n";
+
+char *modifypage_section_card_item =
+" <tr>\n"
+" <td colspan='2'><input type='checkbox' name='url%d'>\n"
+" <a href='%s'>%s</a></td></tr>\n";
+
+char *mainpage_section_card_footer =
+" </table>\n"
+" <hr>\n";
+
+char *modifypage_section_card_footer =
+" </table>\n"
+" <hr>\n";
+
+char *mainpage_footer =
+" </td>\n"
+" </tr>\n"
+"</table>\n"
+"</body>\n"
+"</html>\n";
+
+char *modifypage_footer =
+" </td>\n"
+" </tr>\n"
+"</table>\n"
+"</form>\n"
+"</body>\n"
+"</html>\n";
+
+/* ------------------------------------------------------------------------- */
+char *modifypage_add_section_page =
+DOCTYPE
+"<html>\n"
+"<head>\n"
+"<title>Bookmarks</title>\n"
+"</head>\n"
+"<body bgcolor='#778899' link='black' vlink='brown'>\n"
+"<table border='1' cellpadding='0' width='100%'>\n"
+" <tr><td colspan='2'>\n"
+" <table bgcolor='#b4b4b4' width='100%'>\n"
+" <tr><td bgcolor='#b4b4b4'>&nbsp;Modify bookmarks:: add section\n"
+" </td></tr></table></td></tr>\n"
+"</table>\n"
+"<br>\n"
+"<form>\n"
+" <input type='hidden' name='operation' value='add_section'>\n"
+"<table border='1' width='100%'>\n"
+" <tr>\n"
+" <td bgcolor='olive'><b>New&nbsp;section:</b></td>\n"
+" <td bgcolor='white' width='100%'></td></tr>\n"
+"</table>\n"
+"<table width='100%' cellpadding='10'>\n"
+"<tr><td>\n"
+" <table width='100%' bgcolor='teal'>\n"
+" <tr>\n"
+" <td>Title:</td>\n"
+" <td><input type='text' name='title' size='64'></td></tr>\n"
+" </table>\n"
+" </td></tr>\n"
+"</table>\n"
+"<table width='100%' cellpadding='4' border='0'>\n"
+"<tr><td bgcolor='#a0a0a0'>\n"
+" <input type='submit' name='submit' value='submit.'></td></tr>\n"
+"</table>\n"
+"</form>\n"
+"</body>\n"
+"</html>\n"
+"\n";
+
+/* ------------------------------------------------------------------------- */
+char *modifypage_update_header =
+DOCTYPE
+"<html>\n"
+"<head>\n"
+"<title>Bookmarks</title>\n"
+"</head>\n"
+"<body bgcolor='#778899' link='black' vlink='brown'>\n"
+"<table border='1' cellpadding='0' width='100%'>\n"
+" <tr><td colspan='2'>\n"
+" <table bgcolor='#b4b4b4' width='100%'>\n"
+" <tr><td bgcolor='#b4b4b4'>&nbsp;Modify bookmarks:: update\n"
+" </td></tr></table></td></tr>\n"
+"</table>\n"
+"<br>\n"
+"<form>\n"
+"<input type='hidden' name='operation' value='modify2'>\n";
+
+char *modifypage_update_title =
+"<table border='1' width='100%%'>\n"
+" <tr>\n"
+" <td bgcolor='olive'><b>%s</b></td>\n"
+" <td bgcolor='white' width='100%%'></td></tr>\n"
+"</table>\n";
+
+char *modifypage_update_item_header =
+"<table width='100%' cellpadding='10'>\n";
+
+char *modifypage_update_item =
+"<tr><td>\n"
+" <table width='100%%' bgcolor='teal'>\n"
+" <tr>\n"
+" <td>Title:</td>\n"
+" <td><input type='text' name='title%d' size='64'\n"
+" value='%s'></td></tr>\n"
+" <tr>\n"
+" <td>URL:</td>\n"
+" <td>%s</td></tr>\n"
+" </table>\n"
+" </td></tr>\n";
+
+char *modifypage_update_item2 =
+"<tr><td>\n"
+" <table width='100%%' bgcolor='teal'>\n"
+" <tr>\n"
+" <td>Title:</td>\n"
+" <td><input type='text' name='s%d' size='64'\n"
+" value='%s'></td></tr>\n"
+" </table>\n"
+" </td></tr>\n";
+
+char *modifypage_update_item_footer =
+"</table>\n";
+
+char *modifypage_update_footer =
+"<table width='100%' cellpadding='4' border='0'>\n"
+"<tr><td bgcolor='#a0a0a0'>\n"
+" <input type='submit' name='submit' value='submit.'></td></tr>\n"
+"</table>\n"
+"</form>\n"
+"</body>\n"
+"</html>\n";
+
+/* ------------------------------------------------------------------------- */
+char *modifypage_add_url =
+DOCTYPE
+"<html>\n"
+"<head>\n"
+"<title>Bookmarks</title>\n"
+"</head>\n"
+"<body bgcolor='#778899' link='black' vlink='brown'>\n"
+"<table border='1' cellpadding='0' width='100%'>\n"
+" <tr><td colspan='2'>\n"
+" <table bgcolor='#b4b4b4' width='100%'>\n"
+" <tr><td bgcolor='#b4b4b4'>&nbsp;Modify bookmarks:: add url\n"
+" </td></tr></table></td></tr>\n"
+"</table>\n"
+"<br>\n"
+"<form>\n"
+"<input type='hidden' name='operation' value='add_url2'>\n"
+"<table border='1' width='100%'>\n"
+" <tr>\n"
+" <td bgcolor='olive'><b>Add&nbsp;url:</b></td>\n"
+" <td bgcolor='white' width='100%'></td></tr>\n"
+"</table>\n"
+"<table width='100%' cellpadding='10'>\n"
+"<tr><td>\n"
+" <table width='100%' bgcolor='teal'>\n"
+" <tr>\n"
+" <td>Title:</td>\n"
+" <td><input type='text' name='title' size='64'></td></tr>\n"
+" <tr>\n"
+" <td>URL:</td>\n"
+" <td><input type='text' name='url' size='64'></td></tr>\n"
+" </table>\n"
+" </td></tr>\n"
+"</table>\n"
+"<table width='100%' cellpadding='4' border='0'>\n"
+"<tr><td bgcolor='#a0a0a0'>\n"
+" <input type='submit' name='submit' value='submit.'></td></tr>\n"
+"</table>\n"
+"</form>\n"
+"</body>\n"
+"</html>\n";
+
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * Return a new string with spaces changed with &nbsp;
+ */
+static char *make_one_line_str(char *str)
+{
+ char *new_str;
+ int i, j, n;
+
+ for (i = 0, n = 0; str[i]; ++i)
+ if (str[i] == ' ')
+ ++n;
+
+ new_str = dNew(char, strlen(str) + 6*n + 1);
+ new_str[0] = 0;
+
+ for (i = 0, j = 0; str[i]; ++i) {
+ if (str[i] == ' ') {
+ strcpy(new_str + j, "&nbsp;");
+ j += 6;
+ } else {
+ new_str[j] = str[i];
+ new_str[++j] = 0;
+ }
+ }
+
+ return new_str;
+}
+
+/*
+ * Given an urlencoded string, return it to the original version.
+ */
+static void Unencode_str(char *e_str)
+{
+ char *p, *e;
+
+ for (p = e = e_str; *e; e++, p++) {
+ if (*e == '+') {
+ *p = ' ';
+ } else if (*e == '%') {
+ if (dStrncasecmp(e, "%0D%0A", 6) == 0) {
+ *p = '\n';
+ e += 5;
+ } else {
+ *p = (isdigit(e[1]) ? (e[1] - '0') : (e[1] - 'A' + 10)) * 16 +
+ (isdigit(e[2]) ? (e[2] - '0') : (e[2] - 'A' + 10));
+ e += 2;
+ }
+ } else {
+ *p = *e;
+ }
+ }
+ *p = 0;
+}
+
+/*
+ * Send a short message to dillo's status bar.
+ */
+static int Bmsrv_dpi_send_status_msg(SockHandler *sh, char *str)
+{
+ int st;
+ char *d_cmd;
+
+ d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "send_status_message", str);
+ st = sock_handler_write_str(sh, 1, d_cmd);
+ dFree(d_cmd);
+ return st;
+}
+
+/* -- ADT for bookmarks ---------------------------------------------------- */
+/*
+ * Compare function for searching a bookmark by its key
+ */
+static int Bms_node_by_key_cmp(const void *node, const void *key)
+{
+ return ((BmRec *)node)->key - VOIDP2INT(key);
+}
+
+/*
+ * Compare function for searching a bookmark by section
+ */
+static int Bms_node_by_section_cmp(const void *node, const void *key)
+{
+ return ((BmRec *)node)->section - VOIDP2INT(key);
+}
+
+/*
+ * Compare function for searching a section by its number
+ */
+static int Bms_sec_by_number_cmp(const void *node, const void *key)
+{
+ return ((BmSec *)node)->section - VOIDP2INT(key);
+}
+
+/*
+ * Return the Bm record by key
+ */
+static BmRec *Bms_get(int key)
+{
+ return dList_find_custom(B_bms, INT2VOIDP(key), Bms_node_by_key_cmp);
+}
+
+/*
+ * Return the Section record by key
+ */
+static BmSec *Bms_get_sec(int key)
+{
+ return dList_find_custom(B_secs, INT2VOIDP(key), Bms_sec_by_number_cmp);
+}
+
+/*
+ * Add a bookmark
+ */
+static void Bms_add(int section, char *url, char *title)
+{
+ BmRec *bm_node;
+
+ bm_node = dNew(BmRec, 1);
+ bm_node->key = ++bm_key;
+ bm_node->section = section;
+ bm_node->url = Escape_uri_str(url, "'");
+ bm_node->title = Escape_html_str(title);
+ dList_append(B_bms, bm_node);
+}
+
+/*
+ * Add a section
+ */
+static void Bms_sec_add(char *title)
+{
+ BmSec *sec_node;
+
+ sec_node = dNew(BmSec, 1);
+ sec_node->section = sec_key++;
+ sec_node->title = Escape_html_str(title);
+ dList_append(B_secs, sec_node);
+}
+
+/*
+ * Delete a bookmark by its key
+ */
+static void Bms_del(int key)
+{
+ BmRec *bm_node;
+
+ bm_node = dList_find_custom(B_bms, INT2VOIDP(key), Bms_node_by_key_cmp);
+ if (bm_node) {
+ dList_remove(B_bms, bm_node);
+ dFree(bm_node->title);
+ dFree(bm_node->url);
+ dFree(bm_node);
+ }
+ if (dList_length(B_bms) == 0)
+ bm_key = 0;
+}
+
+/*
+ * Delete a section and its bookmarks by section number
+ */
+static void Bms_sec_del(int section)
+{
+ BmSec *sec_node;
+ BmRec *bm_node;
+
+ sec_node = dList_find_custom(B_secs, INT2VOIDP(section),
+ Bms_sec_by_number_cmp);
+ if (sec_node) {
+ dList_remove(B_secs, sec_node);
+ dFree(sec_node->title);
+ dFree(sec_node);
+
+ /* iterate B_bms and remove those that match the section */
+ while ((bm_node = dList_find_custom(B_bms, INT2VOIDP(section),
+ Bms_node_by_section_cmp))) {
+ Bms_del(bm_node->key);
+ }
+ }
+ if (dList_length(B_secs) == 0)
+ sec_key = 0;
+}
+
+/*
+ * Move a bookmark to another section
+ */
+static void Bms_move(int key, int target_section)
+{
+ BmRec *bm_node;
+
+ bm_node = dList_find_custom(B_bms, INT2VOIDP(key), Bms_node_by_key_cmp);
+ if (bm_node) {
+ bm_node->section = target_section;
+ }
+}
+
+/*
+ * Update a bookmark title by key
+ */
+static void Bms_update_title(int key, char *n_title)
+{
+ BmRec *bm_node;
+
+ bm_node = dList_find_custom(B_bms, INT2VOIDP(key), Bms_node_by_key_cmp);
+ if (bm_node) {
+ dFree(bm_node->title);
+ bm_node->title = Escape_html_str(n_title);
+ }
+}
+
+/*
+ * Update a section title by key
+ */
+static void Bms_update_sec_title(int key, char *n_title)
+{
+ BmSec *sec_node;
+
+ sec_node = dList_find_custom(B_secs, INT2VOIDP(key), Bms_sec_by_number_cmp);
+ if (sec_node) {
+ dFree(sec_node->title);
+ sec_node->title = Escape_html_str(n_title);
+ }
+}
+
+/*
+ * Free all the bookmarks data (bookmarks and sections)
+ */
+static void Bms_free(void)
+{
+ BmRec *bm_node;
+ BmSec *sec_node;
+
+ /* free B_bms */
+ while ((bm_node = dList_nth_data(B_bms, 0))) {
+ Bms_del(bm_node->key);
+ }
+ /* free B_secs */
+ while ((sec_node = dList_nth_data(B_secs, 0))) {
+ Bms_sec_del(sec_node->section);
+ }
+}
+
+/*
+ * Enforce increasing correlative section numbers with no jumps.
+ */
+static void Bms_normalize(void)
+{
+ BmRec *bm_node;
+ BmSec *sec_node;
+ int i, j;
+
+ /* we need at least one section */
+ if (dList_length(B_secs) == 0)
+ Bms_sec_add("Unclassified");
+
+ /* make correlative section numbers */
+ for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
+ sec_node->o_sec = sec_node->section;
+ sec_node->section = i;
+ }
+
+ /* iterate B_secs and make the changes in B_bms */
+ for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
+ if (sec_node->section != sec_node->o_sec) {
+ /* update section numbers */
+ for (j = 0; (bm_node = dList_nth_data(B_bms, j)); ++j) {
+ if (bm_node->section == sec_node->o_sec)
+ bm_node->section = sec_node->section;
+ }
+ }
+ }
+}
+
+/* -- Load bookmarks file -------------------------------------------------- */
+
+/*
+ * If there's no "bm.txt", create one from "bookmarks.html".
+ */
+static void Bms_check_import(void)
+{
+ char *OldBmFile;
+ char *cmd1 =
+ "echo \":s0: Unclassified\" > %s";
+ char *cmd2 =
+ "grep -i \"href\" %s | "
+ "sed -e 's/<li><A HREF=\"/s0 /' -e 's/\">/ /' -e 's/<.*$//' >> %s";
+ Dstr *dstr = dStr_new("");
+
+
+ if (access(BmFile, F_OK) != 0) {
+ OldBmFile = dStrconcat(dGethomedir(), "/.dillo/bookmarks.html", NULL);
+ if (access(OldBmFile, F_OK) == 0) {
+ dStr_sprintf(dstr, cmd1, BmFile);
+ system(dstr->str);
+ dStr_sprintf(dstr, cmd2, OldBmFile, BmFile);
+ system(dstr->str);
+ dStr_free(dstr, TRUE);
+ dFree(OldBmFile);
+ }
+ }
+}
+
+/*
+ * Load bookmarks data from a file
+ */
+static int Bms_load(void)
+{
+ FILE *BmTxt;
+ char *buf, *p, *url, *title, *u_title;
+ int section;
+ struct stat TimeStamp;
+
+ /* clear current bookmarks */
+ Bms_free();
+
+ /* open bm file */
+ if (!(BmTxt = fopen(BmFile, "r"))) {
+ perror("[fopen]");
+ return 1;
+ }
+
+ /* load bm file into memory */
+ while ((buf = dGetline(BmTxt)) != NULL) {
+ if (buf[0] == 's') {
+ /* get section, url and title */
+ section = strtol(buf + 1, NULL, 10);
+ p = strchr(buf, ' '); *p = 0;
+ url = ++p;
+ p = strchr(p, ' '); *p = 0;
+ title = ++p;
+ p = strchr(p, '\n'); *p = 0;
+ u_title = Unescape_html_str(title);
+ Bms_add(section, url, u_title);
+ dFree(u_title);
+
+ } else if (buf[0] == ':' && buf[1] == 's') {
+ /* section = strtol(buf + 2, NULL, 10); */
+ p = strchr(buf + 2, ' ');
+ title = ++p;
+ p = strchr(p, '\n'); *p = 0;
+ Bms_sec_add(title);
+
+ } else {
+ MSG("Syntax error in bookmarks file:\n %s", buf);
+ }
+ dFree(buf);
+ }
+ fclose(BmTxt);
+
+ /* keep track of the timestamp */
+ stat(BmFile, &TimeStamp);
+ BmFileTimeStamp = TimeStamp.st_mtime;
+
+ return 0;
+}
+
+/*
+ * Load bookmarks data if:
+ * - file timestamp is newer than ours or
+ * - we haven't loaded anything yet :)
+ */
+static int Bms_cond_load(void)
+{
+ int st = 0;
+ struct stat TimeStamp;
+
+ if (stat(BmFile, &TimeStamp) != 0) {
+ /* try to import... */
+ Bms_check_import();
+ if (stat(BmFile, &TimeStamp) != 0)
+ TimeStamp.st_mtime = 0;
+ }
+
+ if (!BmFileTimeStamp || !dList_length(B_bms) || !dList_length(B_secs) ||
+ BmFileTimeStamp < TimeStamp.st_mtime) {
+ Bms_load();
+ st = 1;
+ }
+ return st;
+}
+
+/* -- Save bookmarks file -------------------------------------------------- */
+
+/*
+ * Update the bookmarks file from memory contents
+ * Return code: { 0:OK, 1:Abort }
+ */
+static int Bms_save(void)
+{
+ FILE *BmTxt;
+ BmRec *bm_node;
+ BmSec *sec_node;
+ struct stat BmStat;
+ char *u_title;
+ int i, j;
+ Dstr *dstr = dStr_new("");
+
+ /* make a safety backup */
+ if (stat(BmFile, &BmStat) == 0 && BmStat.st_size > 256) {
+ char *BmFileBak = dStrconcat(BmFile, ".bak", NULL);
+ rename(BmFile, BmFileBak);
+ dFree(BmFileBak);
+ }
+
+ /* open bm file */
+ if (!(BmTxt = fopen(BmFile, "w"))) {
+ perror("[fopen]");
+ return 1;
+ }
+
+ /* normalize */
+ Bms_normalize();
+
+ /* save sections */
+ for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
+ u_title = Unescape_html_str(sec_node->title);
+ dStr_sprintf(dstr, ":s%d: %s\n", sec_node->section, u_title);
+ fwrite(dstr->str, (size_t)dstr->len, 1, BmTxt);
+ dFree(u_title);
+ }
+
+ /* save bookmarks (section url title) */
+ for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
+ for (j = 0; (bm_node = dList_nth_data(B_bms, j)); ++j) {
+ if (bm_node->section == sec_node->section) {
+ u_title = Unescape_html_str(bm_node->title);
+ dStr_sprintf(dstr, "s%d %s %s\n",
+ bm_node->section, bm_node->url, u_title);
+ fwrite(dstr->str, (size_t)dstr->len, 1, BmTxt);
+ dFree(u_title);
+ }
+ }
+ }
+
+ dStr_free(dstr, TRUE);
+ fclose(BmTxt);
+
+ /* keep track of the timestamp */
+ stat(BmFile, &BmStat);
+ BmFileTimeStamp = BmStat.st_mtime;
+
+ return 0;
+}
+
+/* -- Add bookmark --------------------------------------------------------- */
+
+/*
+ * Add a new bookmark to DB :)
+ */
+static int Bmsrv_add_bm(SockHandler *sh, char *url, char *title)
+{
+ char *u_title;
+ char *msg="Added bookmark!";
+ int section = 0;
+
+ /* Add in memory */
+ u_title = Unescape_html_str(title);
+ Bms_add(section, url, u_title);
+ dFree(u_title);
+
+ /* Write to file */
+ Bms_save();
+
+ if (Bmsrv_dpi_send_status_msg(sh, msg))
+ return 1;
+
+ return 0;
+}
+
+/* -- Modify --------------------------------------------------------------- */
+
+/*
+ * Count how many sections and urls were marked in a request
+ */
+static void Bmsrv_count_urls_and_sections(char *url, int *n_sec, int *n_url)
+{
+ char *p, *q;
+ int i;
+
+ /* Check marked urls and sections */
+ *n_sec = *n_url = 0;
+ if ((p = strchr(url, '?'))) {
+ for (q = p; (q = strstr(q, "&url")); ++q) {
+ for (i = 0; isdigit(q[4+i]); ++i);
+ *n_url += (q[4+i] == '=') ? 1 : 0;
+ }
+ for (q = p; (q = strstr(q, "&s")); ++q) {
+ for (i = 0; isdigit(q[2+i]); ++i);
+ *n_sec += (q[2+i] == '=') ? 1 : 0;
+ }
+ }
+}
+
+/*
+ * Send a dpi reload request
+ * Return code: { 0:OK, 1:Abort, 2:Close }
+ */
+static int Bmsrv_send_reload_request(SockHandler *sh, char *url)
+{
+ int st;
+ char *d_cmd;
+
+ d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "reload_request", url);
+ st = sock_handler_write_str(sh, 1, d_cmd) ? 1 : 0;
+ dFree(d_cmd);
+ return st;
+}
+
+/*
+ * Send the HTML for the modify page
+ * Return code: { 0:OK, 1:Abort, 2:Close }
+ */
+static int Bmsrv_send_modify_page(SockHandler *sh)
+{
+ static Dstr *dstr = NULL;
+ char *l_title;
+ BmSec *sec_node;
+ BmRec *bm_node;
+ int i, j;
+
+ if (!dstr)
+ dstr = dStr_new("");
+
+ /* send modify page header */
+ if (sock_handler_write_str(sh, 0, modifypage_header))
+ return 1;
+
+ /* write sections header */
+ if (sock_handler_write_str(sh, 0, modifypage_sections_header))
+ return 1;
+ /* write sections */
+ for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
+ dStr_sprintf(dstr, modifypage_sections_item,
+ sec_node->section, sec_node->section, sec_node->title);
+ if (sock_handler_write_str(sh, 0, dstr->str))
+ return 1;
+ }
+ /* write sections footer */
+ if (sock_handler_write_str(sh, 0, modifypage_sections_footer))
+ return 1;
+
+ /* send page middle */
+ if (sock_handler_write_str(sh, 0, modifypage_middle1))
+ return 1;
+
+ /* send bookmark cards */
+ for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
+ /* send card header */
+ l_title = make_one_line_str(sec_node->title);
+ dStr_sprintf(dstr, modifypage_section_card_header,
+ sec_node->section, l_title);
+ dFree(l_title);
+ if (sock_handler_write_str(sh, 0, dstr->str))
+ return 1;
+
+ /* send section's bookmarks */
+ for (j = 0; (bm_node = dList_nth_data(B_bms, j)); ++j) {
+ if (bm_node->section == sec_node->section) {
+ dStr_sprintf(dstr, modifypage_section_card_item,
+ bm_node->key, bm_node->url, bm_node->title);
+ if (sock_handler_write_str(sh, 0, dstr->str))
+ return 1;
+ }
+ }
+
+ /* send card footer */
+ if (sock_handler_write_str(sh, 0, modifypage_section_card_footer))
+ return 1;
+ }
+
+ /* finish page */
+ if (sock_handler_write_str(sh, 1, modifypage_footer))
+ return 1;
+
+ return 2;
+}
+
+/*
+ * Send the HTML for the modify page for "add section"
+ * Return code: { 0:OK, 1:Abort, 2:Close }
+ */
+static int Bmsrv_send_modify_page_add_section(SockHandler *sh)
+{
+ /* send modify page2 */
+ if (sock_handler_write_str(sh, 1, modifypage_add_section_page))
+ return 1;
+
+ return 2;
+}
+
+/*
+ * Send the HTML for the modify page for "add url"
+ * Return code: { 0:OK, 1:Abort, 2:Close }
+ */
+static int Bmsrv_send_modify_page_add_url(SockHandler *sh)
+{
+ if (sock_handler_write_str(sh, 1, modifypage_add_url))
+ return 1;
+ return 2;
+}
+
+/*
+ * Parse a modify urls request and either:
+ * - make a local copy of the url
+ * or
+ * - send the modify page for the marked urls and sections
+ * Return code: { 0:OK, 1:Abort, 2:Close }
+ */
+static int Bmsrv_send_modify_update(SockHandler *sh, char *url)
+{
+ static char *url1 = NULL;
+ static Dstr *dstr = NULL;
+ char *p, *q;
+ int i, key, n_sec, n_url;
+ BmRec *bm_node;
+ BmSec *sec_node;
+
+ /* bookmarks were loaded before */
+
+ if (!dstr)
+ dstr = dStr_new("");
+
+ if (sh == NULL) {
+ /* just copy url */
+ dFree(url1);
+ url1 = dStrdup(url);
+ return 0;
+ }
+
+ /* send HTML here */
+ if (sock_handler_write_str(sh, 0, modifypage_update_header))
+ return 1;
+
+ /* Count number of marked urls and sections */
+ Bmsrv_count_urls_and_sections(url1, &n_sec, &n_url);
+
+ if (n_sec) {
+ dStr_sprintf(dstr, modifypage_update_title, "Update&nbsp;sections:");
+ sock_handler_write_str(sh, 0, dstr->str);
+ sock_handler_write_str(sh, 0, modifypage_update_item_header);
+ /* send items here */
+ p = strchr(url1, '?');
+ for (q = p; (q = strstr(q, "&s")); ++q) {
+ for (i = 0; isdigit(q[2+i]); ++i);
+ if (q[2+i] == '=') {
+ key = strtol(q + 2, NULL, 10);
+ if ((sec_node = Bms_get_sec(key))) {
+ dStr_sprintf(dstr, modifypage_update_item2,
+ sec_node->section, sec_node->title);
+ sock_handler_write_str(sh, 0, dstr->str);
+ }
+ }
+ }
+ sock_handler_write_str(sh, 0, modifypage_update_item_footer);
+ }
+
+ if (n_url) {
+ dStr_sprintf(dstr, modifypage_update_title, "Update&nbsp;titles:");
+ sock_handler_write_str(sh, 0, dstr->str);
+ sock_handler_write_str(sh, 0, modifypage_update_item_header);
+ /* send items here */
+ p = strchr(url1, '?');
+ for (q = p; (q = strstr(q, "&url")); ++q) {
+ for (i = 0; isdigit(q[4+i]); ++i);
+ if (q[4+i] == '=') {
+ key = strtol(q + 4, NULL, 10);
+ bm_node = Bms_get(key);
+ dStr_sprintf(dstr, modifypage_update_item,
+ bm_node->key, bm_node->title, bm_node->url);
+ sock_handler_write_str(sh, 0, dstr->str);
+ }
+ }
+ sock_handler_write_str(sh, 0, modifypage_update_item_footer);
+ }
+
+ sock_handler_write_str(sh, 1, modifypage_update_footer);
+
+ return 2;
+}
+
+/*
+ * Make the modify-page and send it back
+ * Return code: { 0:OK, 1:Abort, 2:Close }
+ */
+static int Bmsrv_send_modify_answer(SockHandler *sh, char *url)
+{
+ char *d_cmd;
+ int st;
+
+ d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
+ st = sock_handler_write_str(sh, 1, d_cmd);
+ dFree(d_cmd);
+ if (st != 0)
+ return 1;
+
+ /* Send HTTP header */
+ if (sock_handler_write_str(sh, 0, Header) != 0) {
+ return 1;
+ }
+
+ if (MODIFY_PAGE_NUM == 2) {
+ MODIFY_PAGE_NUM = 1;
+ return Bmsrv_send_modify_page_add_section(sh);
+ } else if (MODIFY_PAGE_NUM == 3) {
+ MODIFY_PAGE_NUM = 1;
+ return Bmsrv_send_modify_update(sh, NULL);
+ } else if (MODIFY_PAGE_NUM == 4) {
+ MODIFY_PAGE_NUM = 1;
+ return Bmsrv_send_modify_page_add_url(sh);
+ } else {
+ return Bmsrv_send_modify_page(sh);
+ }
+}
+
+
+/* Operations */
+
+/*
+ * Parse a delete bms request, delete them, and update bm file.
+ * Return code: { 0:OK, 1:Abort }
+ */
+static int Bmsrv_modify_delete(SockHandler *sh, char *url)
+{
+ char *p;
+ int nb, ns, key;
+
+ /* bookmarks were loaded before */
+
+ /* Remove marked sections */
+ p = strchr(url, '?');
+ for (ns = 0; (p = strstr(p, "&s")); ++p) {
+ if (isdigit(p[2])) {
+ key = strtol(p + 2, NULL, 10);
+ Bms_sec_del(key);
+ ++ns;
+ }
+ }
+
+ /* Remove marked urls */
+ p = strchr(url, '?');
+ for (nb = 0; (p = strstr(p, "&url")); ++p) {
+ if (isdigit(p[4])) {
+ key = strtol(p + 4, NULL, 10);
+ Bms_del(key);
+ ++nb;
+ }
+ }
+
+/* -- This doesn't work because dillo erases the message upon the
+ * receipt of the first data stream.
+ *
+ sprintf(msg, "Deleted %d bookmark%s!>", n, (n > 1) ? "s" : "");
+ if (Bmsrv_dpi_send_status_msg(sh, msg))
+ return 1;
+*/
+
+ /* Write new bookmarks file */
+ if (nb || ns)
+ Bms_save();
+
+ return 0;
+}
+
+/*
+ * Parse a move urls request, move and update bm file.
+ * Return code: { 0:OK, 1:Abort }
+ */
+static int Bmsrv_modify_move(SockHandler *sh, char *url)
+{
+ char *p;
+ int n, section = 0, key;
+
+ /* bookmarks were loaded before */
+
+ /* get target section */
+ for (p = url; (p = strstr(p, "&s")); ++p) {
+ if (isdigit(p[2])) {
+ section = strtol(p + 2, NULL, 10);
+ break;
+ }
+ }
+ if (!p)
+ return 1;
+
+ /* move marked urls */
+ p = strchr(url, '?');
+ for (n = 0; (p = strstr(p, "&url")); ++p) {
+ if (isdigit(p[4])) {
+ key = strtol(p + 4, NULL, 10);
+ Bms_move(key, section);
+ ++n;
+ }
+ }
+
+ /* Write new bookmarks file */
+ if (n) {
+ Bms_save();
+ }
+
+ return 0;
+}
+
+/*
+ * Parse a modify request: update urls and sections, then save.
+ * Return code: { 0:OK, 1:Abort }
+ */
+static int Bmsrv_modify_update(SockHandler *sh, char *url)
+{
+ char *p, *q, *title;
+ int i, key;
+
+ /* bookmarks were loaded before */
+
+ p = strchr(url, '?');
+ for ( ; (p = strstr(p, "s")); ++p) {
+ if (p[-1] == '&' || p[-1] == '?' ) {
+ for (i = 0; isdigit(p[1 + i]); ++i);
+ if (i && p[1 + i] == '=') {
+ /* we have a title/key to change */
+ key = strtol(p + 1, NULL, 10);
+ if ((q = strchr(p + 1, '&')))
+ title = dStrndup(p + 2 + i, (uint_t)(q - (p + 2 + i)));
+ else
+ title = dStrdup(p + 2 + i);
+
+ Unencode_str(title);
+ Bms_update_sec_title(key, title);
+ dFree(title);
+ }
+ }
+ }
+
+ p = strchr(url, '?');
+ for ( ; (p = strstr(p, "title")); ++p) {
+ if (p[-1] == '&' || p[-1] == '?' ) {
+ for (i = 0; isdigit(p[5 + i]); ++i);
+ if (i && p[5 + i] == '=') {
+ /* we have a title/key to change */
+ key = strtol(p + 5, NULL, 10);
+ if ((q = strchr(p + 5, '&')))
+ title = dStrndup(p + 6 + i, (uint_t)(q - (p + 6 + i)));
+ else
+ title = dStrdup(p + 6 + i);
+
+ Unencode_str(title);
+ Bms_update_title(key, title);
+ dFree(title);
+ }
+ }
+ }
+
+ /* Write new bookmarks file */
+ Bms_save();
+
+ return 0;
+}
+
+/*
+ * Parse an "add section" request, and update the bm file.
+ * Return code: { 0:OK, 1:Abort }
+ */
+static int Bmsrv_modify_add_section(SockHandler *sh, char *url)
+{
+ char *p, *title = NULL;
+
+ /* bookmarks were loaded before */
+
+ /* get new section's title */
+ if ((p = strstr(url, "&title="))) {
+ title = dStrdup (p + 7);
+ if ((p = strchr(title, '&')))
+ *p = 0;
+ Unencode_str(title);
+ } else
+ return 1;
+
+ Bms_sec_add(title);
+ dFree(title);
+
+ /* Write new bookmarks file */
+ Bms_save();
+
+ return 0;
+}
+
+/*
+ * Parse an "add url" request, and update the bm file.
+ * Return code: { 0:OK, 1:Abort }
+ */
+static int Bmsrv_modify_add_url(SockHandler *sh, char *s_url)
+{
+ char *p, *q, *title, *u_title, *url;
+ int i;
+ static int section = 0;
+
+ /* bookmarks were loaded before */
+
+ if (sh == NULL) {
+ /* look for section */
+ for (q = s_url; (q = strstr(q, "&s")); ++q) {
+ for (i = 0; isdigit(q[2+i]); ++i);
+ if (q[2+i] == '=')
+ section = strtol(q + 2, NULL, 10);
+ }
+ return 1;
+ }
+
+ if (!(p = strstr(s_url, "&title=")) ||
+ !(q = strstr(s_url, "&url=")))
+ return 1;
+
+ title = dStrdup (p + 7);
+ if ((p = strchr(title, '&')))
+ *p = 0;
+ url = dStrdup (q + 5);
+ if ((p = strchr(url, '&')))
+ *p = 0;
+ if (strlen(title) && strlen(url)) {
+ Unencode_str(title);
+ Unencode_str(url);
+ u_title = Unescape_html_str(title);
+ Bms_add(section, url, u_title);
+ dFree(u_title);
+ }
+ dFree(title);
+ dFree(url);
+ section = 0;
+
+ /* todo: we should send an "Bookmark added" message, but the
+ msg-after-HTML functionallity is still pending, not hard though. */
+
+ /* Write new bookmarks file */
+ Bms_save();
+
+ return 0;
+}
+
+/*
+ * Check the parameters of a modify request, and return an error message
+ * when it's wrong.
+ * Return code: { 0:OK, 2:Close }
+ */
+static int Bmsrv_check_modify_request(SockHandler *sh, char *url)
+{
+ char *p, *msg;
+ int n_sec, n_url;
+
+ /* Count number of marked urls and sections */
+ Bmsrv_count_urls_and_sections(url, &n_sec, &n_url);
+
+ p = strchr(url, '?');
+ if (strstr(p, "operation=delete&")) {
+ if (n_url || n_sec)
+ return 0;
+ msg = "Delete: you must mark what to delete!";
+
+ } else if (strstr(url, "operation=move&")) {
+ if (n_url && n_sec)
+ return 0;
+ else if (n_url)
+ msg = "Move: you must mark a target section!";
+ else if (n_sec)
+ msg = "Move: can not move a section (yet).";
+ else
+ msg = "Move: you must mark some urls, and a target section!";
+
+ } else if (strstr(url, "operation=modify&")) {
+ if (n_url || n_sec)
+ return 0;
+ msg = "Modify: you must mark what to update!";
+
+ } else if (strstr(url, "operation=modify2&")) {
+ /* nothing to check here */
+ return 0;
+
+ } else if (strstr(url, "operation=add_sec&")) {
+ /* nothing to check here */
+ return 0;
+
+ } else if (strstr(url, "operation=add_section&")) {
+ /* nothing to check here */
+ return 0;
+
+ } else if (strstr(url, "operation=add_url&")) {
+ if (n_sec <= 1)
+ return 0;
+ msg = "Add url: only one target section is allowed!";
+
+ } else if (strstr(url, "operation=add_url2&")) {
+ /* nothing to check here */
+ return 0;
+
+ } else if (strstr(url, "operation=none&")) {
+ msg = "No operation, just do nothing!";
+
+ } else {
+ msg = "Sorry, not implemented yet.";
+ }
+
+ Bmsrv_dpi_send_status_msg(sh, msg);
+ return 2;
+}
+
+/*
+ * Parse a and process a modify request.
+ * Return code: { 0:OK, 1:Abort, 2:Close }
+ */
+static int Bmsrv_process_modify_request(SockHandler *sh, char *url)
+{
+ /* check the provided parameters */
+ if (Bmsrv_check_modify_request(sh, url) != 0)
+ return 2;
+
+ if (strstr(url, "operation=delete&")) {
+ if (Bmsrv_modify_delete(sh, url) == 1)
+ return 1;
+ if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
+ return 1;
+
+ } else if (strstr(url, "operation=move&")) {
+ if (Bmsrv_modify_move(sh, url) == 1)
+ return 1;
+ if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
+ return 1;
+
+ } else if (strstr(url, "operation=modify&")) {
+ /* make a local copy of 'url' */
+ Bmsrv_send_modify_update(NULL, url);
+ MODIFY_PAGE_NUM = 3;
+ if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
+ return 1;
+
+ } else if (strstr(url, "operation=modify2&")) {
+ if (Bmsrv_modify_update(sh, url) == 1)
+ return 1;
+ if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
+ return 1;
+
+ } else if (strstr(url, "operation=add_sec&")) {
+ /* this global variable tells which page to send (--hackish...) */
+ MODIFY_PAGE_NUM = 2;
+ if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
+ return 1;
+
+ } else if (strstr(url, "operation=add_section&")) {
+ if (Bmsrv_modify_add_section(sh, url) == 1)
+ return 1;
+ if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
+ return 1;
+
+ } else if (strstr(url, "operation=add_url&")) {
+ /* this global variable tells which page to send (--hackish...) */
+ MODIFY_PAGE_NUM = 4;
+ /* parse section if present */
+ Bmsrv_modify_add_url(NULL, url);
+ if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
+ return 1;
+
+ } else if (strstr(url, "operation=add_url2&")) {
+ if (Bmsrv_modify_add_url(sh, url) == 1)
+ return 1;
+ if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
+ return 1;
+ }
+
+ return 2;
+}
+
+/* -- Bookmarks ------------------------------------------------------------ */
+
+/*
+ * Send the current bookmarks page (in HTML)
+ */
+static int send_bm_page(SockHandler *sh)
+{
+ static Dstr *dstr = NULL;
+ char *l_title;
+ BmSec *sec_node;
+ BmRec *bm_node;
+ int i, j;
+
+ if (!dstr)
+ dstr = dStr_new("");
+
+ if (sock_handler_write_str(sh, 0, mainpage_header))
+ return 1;
+
+ /* write sections header */
+ if (sock_handler_write_str(sh, 0, mainpage_sections_header))
+ return 1;
+ /* write sections */
+ for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
+ dStr_sprintf(dstr, mainpage_sections_item,
+ sec_node->section, sec_node->title);
+ if (sock_handler_write_str(sh, 0, dstr->str))
+ return 1;
+ }
+ /* write sections footer */
+ if (sock_handler_write_str(sh, 0, mainpage_sections_footer))
+ return 1;
+
+ /* send page middle */
+ if (sock_handler_write_str(sh, 0, mainpage_middle1))
+ return 1;
+
+ /* send bookmark cards */
+ for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
+ /* send card header */
+ l_title = make_one_line_str(sec_node->title);
+ dStr_sprintf(dstr, mainpage_section_card_header,
+ sec_node->section, l_title);
+ dFree(l_title);
+ if (sock_handler_write_str(sh, 0, dstr->str))
+ return 1;
+
+ /* send section's bookmarks */
+ for (j = 0; (bm_node = dList_nth_data(B_bms, j)); ++j) {
+ if (bm_node->section == sec_node->section) {
+ dStr_sprintf(dstr, mainpage_section_card_item,
+ bm_node->url, bm_node->title);
+ if (sock_handler_write_str(sh, 0, dstr->str))
+ return 1;
+ }
+ }
+
+ /* send card footer */
+ if (sock_handler_write_str(sh, 0, mainpage_section_card_footer))
+ return 1;
+ }
+
+ /* finish page */
+ if (sock_handler_write_str(sh, 1, mainpage_footer))
+ return 1;
+
+ return 0;
+}
+
+
+/* -- Dpi parser ----------------------------------------------------------- */
+
+/*
+ * Parse a data stream (dpi protocol)
+ * Note: Buf is a zero terminated string
+ * Return code: { 0:OK, 1:Abort, 2:Close }
+ */
+static int Bmsrv_parse_buf(SockHandler *sh, char *Buf)
+{
+ static char *msg1=NULL, *msg2=NULL, *msg3=NULL;
+ char *p, *cmd, *d_cmd, *url, *title, *msg;
+ size_t BufSize;
+ int st;
+
+ if (!msg1) {
+ /* Initialize data for the "chat" command. */
+ msg1 = a_Dpip_build_cmd("cmd=%s msg=%s", "chat", "Hi browser");
+ msg2 = a_Dpip_build_cmd("cmd=%s msg=%s", "chat", "Is it worth?");
+ msg3 = a_Dpip_build_cmd("cmd=%s msg=%s", "chat", "Ok, send it");
+ }
+
+ if (!(p = strchr(Buf, '>'))) {
+ /* Haven't got a full tag */
+ MSG("Haven't got a full tag!\n");
+ return 1;
+ }
+
+ BufSize = strlen(Buf);
+ cmd = a_Dpip_get_attr(Buf, BufSize, "cmd");
+
+ if (cmd && strcmp(cmd, "chat") == 0) {
+ dFree(cmd);
+ msg = a_Dpip_get_attr(Buf, BufSize, "msg");
+ if (*msg == 'H') {
+ /* "Hi server" */
+ if (sock_handler_write_str(sh, 1, msg1))
+ return 1;
+ } else if (*msg == 'I') {
+ /* "I want to set abookmark" */
+ if (sock_handler_write_str(sh, 1, msg2))
+ return 1;
+ } else if (*msg == 'S') {
+ /* "Sure" */
+ if (sock_handler_write_str(sh, 1, msg3))
+ return 1;
+ }
+ dFree(msg);
+ return 0;
+ }
+
+ /* sync with the bookmarks file */
+ Bms_cond_load();
+
+ if (cmd && strcmp(cmd, "DpiBye") == 0) {
+ MSG("(pid %d): Got DpiBye.\n", (int)getpid());
+ exit(0);
+
+ } else if (cmd && strcmp(cmd, "add_bookmark") == 0) {
+ dFree(cmd);
+ url = a_Dpip_get_attr(Buf, BufSize, "url");
+ title = a_Dpip_get_attr(Buf, BufSize, "title");
+ if (strlen(title) == 0) {
+ dFree(title);
+ title = dStrdup("(Untitled)");
+ }
+ if (url && title)
+ Bmsrv_add_bm(sh, url, title);
+ dFree(url);
+ dFree(title);
+ return 2;
+
+ } else if (cmd && strcmp(cmd, "open_url") == 0) {
+ dFree(cmd);
+ url = a_Dpip_get_attr(Buf, BufSize, "url");
+
+ if (strcmp(url, "dpi:/bm/modify") == 0) {
+ st = Bmsrv_send_modify_answer(sh, url);
+ return st;
+
+ } else if (strncmp(url, "dpi:/bm/modify?", 15) == 0) {
+ /* process request */
+ st = Bmsrv_process_modify_request(sh, url);
+ return st;
+ }
+
+
+ d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
+ st = sock_handler_write_str(sh, 1, d_cmd);
+ dFree(d_cmd);
+ if (st != 0)
+ return 1;
+
+ /* Send HTTP header */
+ if (sock_handler_write_str(sh, 1, Header) != 0) {
+ return 1;
+ }
+
+ st = send_bm_page(sh);
+ if (st != 0) {
+ char *err =
+ DOCTYPE
+ "<HTML><body> Error on the bookmarks server...</body></html>";
+ if (sock_handler_write_str(sh, 1, err) != 0) {
+ return 1;
+ }
+ }
+ return 2;
+ }
+
+ return 0;
+}
+
+/* -- Termination handlers ----------------------------------------------- */
+/*
+ * (was to delete the local namespace socket),
+ * but this is handled by 'dpid' now.
+ */
+static void cleanup(void)
+{
+ /* no cleanup required */
+}
+
+/*
+ * Perform any necessary cleanups upon abnormal termination
+ */
+static void termination_handler(int signum)
+{
+ exit(signum);
+}
+
+
+/*
+ * -- MAIN -------------------------------------------------------------------
+ */
+int main (void) {
+ struct sockaddr_un spun;
+ int temp_sock_descriptor;
+ socklen_t address_size;
+ char *buf;
+ int code;
+ SockHandler *sh;
+
+ /* Arrange the cleanup function for terminations via exit() */
+ atexit(cleanup);
+
+ /* Arrange the cleanup function for abnormal terminations */
+ if (signal (SIGINT, termination_handler) == SIG_IGN)
+ signal (SIGINT, SIG_IGN);
+ if (signal (SIGHUP, termination_handler) == SIG_IGN)
+ signal (SIGHUP, SIG_IGN);
+ if (signal (SIGTERM, termination_handler) == SIG_IGN)
+ signal (SIGTERM, SIG_IGN);
+
+ /* Initialize local data */
+ B_bms = dList_new(512);
+ B_secs = dList_new(32);
+ BmFile = dStrconcat(dGethomedir(), "/.dillo/bm.txt", NULL);
+ /* some OSes may need this... */
+ address_size = sizeof(struct sockaddr_un);
+
+ MSG("(v.13): accepting connections...\n");
+
+ while (1) {
+ temp_sock_descriptor =
+ accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size);
+ if (temp_sock_descriptor == -1) {
+ perror("[accept]");
+ exit(1);
+ }
+
+ /* create the SockHandler structure */
+ sh = sock_handler_new(temp_sock_descriptor,temp_sock_descriptor,8*1024);
+
+ while (1) {
+ code = 1;
+ if ((buf = sock_handler_read(sh)) != NULL) {
+ /* Let's see what we fished... */
+ code = Bmsrv_parse_buf(sh, buf);
+ }
+ if (code == 1)
+ exit(1);
+ else if (code == 2)
+ break;
+ }
+
+ sock_handler_close(sh);
+ sock_handler_free(sh);
+
+ }/*while*/
+}