1 /*
2  * Bookmarks server (chat version).
3  *
4  * NOTE: this code illustrates how to make a dpi-program.
5  *
6  * Copyright 2002-2007 Jorge Arellano Cid <jcid@dillo.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  */
14 
15 /* TODO: this server is not assembling the received packets.
16  * This means it currently expects dillo to send full dpi tags
17  * within the socket; if that fails, everything stops.
18  * This is not hard to fix, mainly is a matter of expecting the
19  * final '>' of a tag.
20  */
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <stddef.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <sys/socket.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <sys/un.h>
33 #include <time.h>
34 #include <netdb.h>
35 #include <fcntl.h>
36 #include <signal.h>
37 #include "../dpip/dpip.h"
38 #include "dpiutil.h"
39 
40 
41 /*
42  * Debugging macros
43  */
44 #define _MSG(...)
45 #define MSG(...)  printf("[bookmarks dpi]: " __VA_ARGS__)
46 
47 #define DOCTYPE \
48    "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
49 
50 /*
51  * Notes on character escaping:
52  *   - Basically things are saved unescaped and escaped when in memory.
53  *   - &<>"' are escaped in titles and sections and saved unescaped.
54  *   - ' is escaped as %27 in URLs and saved escaped.
55  */
56 typedef struct {
57    int key;
58    int section;
59    char *url;
60    char *title;
61 } BmRec;
62 
63 typedef struct {
64    int section;
65    char *title;
66 
67    int o_sec;   /* private, for normalization */
68 } BmSec;
69 
70 
71 /*
72  * Local data
73  */
74 static char *Header = "Content-type: text/html\n\n";
75 static char *BmFile = NULL;
76 static time_t BmFileTimeStamp = 0;
77 static Dlist *B_bms = NULL;
78 static int bm_key = 0;
79 
80 static Dlist *B_secs = NULL;
81 static int sec_key = 0;
82 
83 static int MODIFY_PAGE_NUM = 1;
84 
85 
86 /*
87  * Forward declarations
88  */
89 
90 
91 /* -- HTML templates ------------------------------------------------------- */
92 
93 static const char *mainpage_header =
94 DOCTYPE
95 "<html>\n"
96 "<head>\n"
97 "<title>Bookmarks</title>\n"
98 "</head>\n"
99 "<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
100 "<table border='1' cellpadding='0' width='100%'>\n"
101 " <tr><td>\n"
102 "  <table width='100%' bgcolor='#b4b4b4'>\n"
103 "   <tr>\n"
104 "    <td> Bookmarks :: </td>\n"
105 "    <td align='right'>\n"
106 "     [<a href='dpi:/bm/modify'>modify</a>]\n"
107 "    </td></tr>\n"
108 "  </table></td></tr>\n"
109 "</table>\n"
110 "<br>\n";
111 
112 static const char *modifypage_header =
113 DOCTYPE
114 "<html>\n"
115 "<head>\n"
116 "<title>Bookmarks</title>\n"
117 "</head>\n"
118 "<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
119 "<table border='1' cellpadding='0' width='100%'>\n"
120 " <tr><td>\n"
121 "  <table width='100%' bgcolor='#b4b4b4'>\n"
122 "   <tr>\n"
123 "    <td> Bookmarks :: modify</td>\n"
124 "    <td align='right'>\n"
125 "     [<a href='dpi:/bm/'>cancel</a>]\n"
126 "    </td>\n"
127 "   </tr>\n"
128 "  </table></td></tr>                            \n"
129 "</table>                                        \n"
130 "\n"
131 "<form action='modify'>\n"
132 "<table width='100%' border='1' cellpadding='0'>\n"
133 " <tr style='background-color: teal'>\n"
134 "  <td>\n"
135 "   <b>Select an operation</b>\n"
136 "   <select name='operation'>\n"
137 "    <option value='none' selected>--\n"
138 "    <option value='delete'>Delete\n"
139 "    <option value='move'>Move\n"
140 "    <option value='modify'>Modify\n"
141 "    <option value='add_sec'>Add Section\n"
142 "    <option value='add_url'>Add URL\n"
143 "   </select>\n"
144 "   <b>, mark its operands, and</b>\n"
145 "   <input type='submit' name='submit' value='submit.'>\n"
146 "  </td>\n"
147 " </tr>\n"
148 "</table>\n";
149 
150 static const char *mainpage_sections_header =
151 "<table border='1' cellpadding='0' cellspacing='20' width='100%'>\n"
152 " <tr valign='top'>\n"
153 "  <td>\n"
154 "   <table bgcolor='#b4b4b4' border='2' cellpadding='4' cellspacing='1'>\n"
155 "    <tr><td>\n"
156 "     <table width='100%' bgcolor='#b4b4b4'>\n"
157 "      <tr><td><small>Sections:</small></td></tr></table></td></tr>\n";
158 
159 static const char *modifypage_sections_header =
160 "<table border='1' cellpadding='0' cellspacing='20' width='100%'>\n"
161 " <tr valign='top'>\n"
162 "  <td>\n"
163 "   <table bgcolor='#b4b4b4' border='1'>\n"
164 "    <tr><td>\n"
165 "     <table width='100%' bgcolor='#b4b4b4'>\n"
166 "      <tr><td><small>Sections:</small></td></tr></table></td></tr>\n";
167 
168 static const char *mainpage_sections_item =
169 "    <tr><td align='center'>\n"
170 "      <a href='#s%d'>%s</a></td></tr>\n";
171 
172 static const char *modifypage_sections_item =
173 "    <tr><td>\n"
174 "     <table width='100%%'>\n"
175 "      <tr align='center'>"
176 "       <td><input type='checkbox' name='s%d'></td>\n"
177 "       <td width='100%%'><a href='#s%d'>%s</a></td></tr></table></td></tr>\n";
178 
179 static const char *mainpage_sections_footer =
180 "   </table>\n";
181 
182 static const char *modifypage_sections_footer =
183 "   </table>\n";
184 
185 static const char *mainpage_middle1 =
186 "  </td>\n"
187 "  <td width='100%'>\n";
188 
189 static const char *modifypage_middle1 =
190 "  </td>\n"
191 "  <td width='100%'>\n";
192 
193 static const char *mainpage_section_card_header =
194 "   <a name='s%d'></a>\n"
195 "   <table bgcolor='#bfbfbf' width='100%%' cellspacing='2'>\n"
196 "    <tr>\n"
197 "     <td bgcolor='#bf0c0c'><font color='white'><b>\n"
198 "      &nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;&nbsp;</b></font></td>\n"
199 "     <td bgcolor='white' width='100%%'>&nbsp;</td></tr>\n";
200 
201 static const char *modifypage_section_card_header =
202 "   <a name='s%d'></a>\n"
203 "   <table bgcolor='#bfbfbf' width='100%%' cellspacing='2'>\n"
204 "    <tr>\n"
205 "     <td bgcolor='#bf0c0c'><font color='white'><b>\n"
206 "      &nbsp;&nbsp;&nbsp;%s&nbsp;&nbsp;&nbsp;</b></font></td>\n"
207 "     <td bgcolor='white' width='100%%'>&nbsp;</td></tr>\n";
208 
209 static const char *mainpage_section_card_item =
210 "    <tr><td colspan='2'>\n"
211 "      <a href='%s'>%s</a> </td></tr>\n";
212 
213 static const char *modifypage_section_card_item =
214 "    <tr>\n"
215 "     <td colspan='2'><input type='checkbox' name='url%d'>\n"
216 "      <a href='%s'>%s</a></td></tr>\n";
217 
218 static const char *mainpage_section_card_footer =
219 "   </table>\n"
220 "   <hr>\n";
221 
222 static const char *modifypage_section_card_footer =
223 "   </table>\n"
224 "   <hr>\n";
225 
226 static const char *mainpage_footer =
227 "  </td>\n"
228 " </tr>\n"
229 "</table>\n"
230 "</body>\n"
231 "</html>\n";
232 
233 static const char *modifypage_footer =
234 "  </td>\n"
235 " </tr>\n"
236 "</table>\n"
237 "</form>\n"
238 "</body>\n"
239 "</html>\n";
240 
241 /* ------------------------------------------------------------------------- */
242 static const char *modifypage_add_section_page =
243 DOCTYPE
244 "<html>\n"
245 "<head>\n"
246 "<title>Bookmarks</title>\n"
247 "</head>\n"
248 "<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
249 "<table border='1' cellpadding='0' width='100%'>\n"
250 " <tr><td colspan='2'>\n"
251 "  <table bgcolor='#b4b4b4' width='100%'>\n"
252 "   <tr>\n"
253 "    <td bgcolor='#b4b4b4'>\n"
254 "     Modify bookmarks :: add section\n"
255 "    </td>\n"
256 "    <td align='right'>\n"
257 "     [<a href='dpi:/bm/'>cancel</a>]\n"
258 "    </td>\n"
259 "   </tr>\n"
260 "  </table></td></tr>\n"
261 "</table>\n"
262 "<br>\n"
263 "<form action='modify'>\n"
264 " <input type='hidden' name='operation' value='add_section'>\n"
265 "<table border='1' width='100%'>\n"
266 " <tr>\n"
267 "  <td bgcolor='olive'><b>New&nbsp;section:</b></td>\n"
268 "  <td bgcolor='white' width='100%'></td></tr>\n"
269 "</table>\n"
270 "<table width='100%' cellpadding='10'>\n"
271 "<tr><td>\n"
272 " <table width='100%' bgcolor='teal'>\n"
273 "  <tr>\n"
274 "   <td>Title:</td>\n"
275 "   <td><input type='text' name='title' size='64'></td></tr>\n"
276 " </table>\n"
277 " </td></tr>\n"
278 "</table>\n"
279 "<table width='100%' cellpadding='4' border='0'>\n"
280 "<tr><td bgcolor='#a0a0a0'>\n"
281 " <input type='submit' name='submit' value='submit.'></td></tr>\n"
282 "</table>\n"
283 "</form>\n"
284 "</body>\n"
285 "</html>\n"
286 "\n";
287 
288 /* ------------------------------------------------------------------------- */
289 static const char *modifypage_update_header =
290 DOCTYPE
291 "<html>\n"
292 "<head>\n"
293 "<title>Bookmarks</title>\n"
294 "</head>\n"
295 "<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
296 "<table border='1' cellpadding='0' width='100%'>\n"
297 " <tr><td colspan='2'>\n"
298 "  <table bgcolor='#b4b4b4' width='100%'>\n"
299 "   <tr><td bgcolor='#b4b4b4'> Modify bookmarks :: update\n"
300 "    </td>\n"
301 "    <td align='right'>\n"
302 "     [<a href='dpi:/bm/'>cancel</a>]\n"
303 "    </td>\n"
304 "   </tr>\n"
305 "  </table></td></tr>\n"
306 "</table>\n"
307 "<br>\n"
308 "<form action='modify'>\n"
309 "<input type='hidden' name='operation' value='modify2'>\n";
310 
311 static const char *modifypage_update_title =
312 "<table border='1' width='100%%'>\n"
313 " <tr>\n"
314 "  <td bgcolor='olive'><b>%s</b></td>\n"
315 "  <td bgcolor='white' width='100%%'></td></tr>\n"
316 "</table>\n";
317 
318 static const char *modifypage_update_item_header =
319 "<table width='100%' cellpadding='10'>\n";
320 
321 static const char *modifypage_update_item =
322 "<tr><td>\n"
323 " <table width='100%%' bgcolor='teal'>\n"
324 "  <tr>\n"
325 "   <td>Title:</td>\n"
326 "   <td><input type='text' name='title%d' size='64'\n"
327 "        value='%s'></td></tr>\n"
328 "  <tr>\n"
329 "   <td>URL:</td>\n"
330 "   <td>%s</td></tr>\n"
331 " </table>\n"
332 " </td></tr>\n";
333 
334 static const char *modifypage_update_item2 =
335 "<tr><td>\n"
336 " <table width='100%%' bgcolor='teal'>\n"
337 "  <tr>\n"
338 "   <td>Title:</td>\n"
339 "   <td><input type='text' name='s%d' size='64'\n"
340 "        value='%s'></td></tr>\n"
341 " </table>\n"
342 " </td></tr>\n";
343 
344 static const char *modifypage_update_item_footer =
345 "</table>\n";
346 
347 static const char *modifypage_update_footer =
348 "<table width='100%' cellpadding='4' border='0'>\n"
349 "<tr><td bgcolor='#a0a0a0'>\n"
350 " <input type='submit' name='submit' value='submit.'></td></tr>\n"
351 "</table>\n"
352 "</form>\n"
353 "</body>\n"
354 "</html>\n";
355 
356 /* ------------------------------------------------------------------------- */
357 static const char *modifypage_add_url =
358 DOCTYPE
359 "<html>\n"
360 "<head>\n"
361 "<title>Bookmarks</title>\n"
362 "</head>\n"
363 "<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
364 "<table border='1' cellpadding='0' width='100%'>\n"
365 " <tr><td colspan='2'>\n"
366 "  <table bgcolor='#b4b4b4' width='100%'>\n"
367 "   <tr><td bgcolor='#b4b4b4'> Modify bookmarks :: add url\n"
368 "    </td>\n"
369 "    <td align='right'>\n"
370 "     [<a href='dpi:/bm/'>cancel</a>]\n"
371 "    </td>\n"
372 "   </tr>\n"
373 "  </table></td></tr>\n"
374 "</table>\n"
375 "<br>\n"
376 "<form action='modify'>\n"
377 "<input type='hidden' name='operation' value='add_url2'>\n"
378 "<table border='1' width='100%'>\n"
379 " <tr>\n"
380 "  <td bgcolor='olive'><b>Add&nbsp;url:</b></td>\n"
381 "  <td bgcolor='white' width='100%'></td></tr>\n"
382 "</table>\n"
383 "<table width='100%' cellpadding='10'>\n"
384 "<tr><td>\n"
385 " <table width='100%' bgcolor='teal'>\n"
386 "  <tr>\n"
387 "   <td>Title:</td>\n"
388 "   <td><input type='text' name='title' size='64'></td></tr>\n"
389 "  <tr>\n"
390 "   <td>URL:</td>\n"
391 "   <td><input type='text' name='url' size='64'></td></tr>\n"
392 " </table>\n"
393 " </td></tr>\n"
394 "</table>\n"
395 "<table width='100%' cellpadding='4' border='0'>\n"
396 "<tr><td bgcolor='#a0a0a0'>\n"
397 " <input type='submit' name='submit' value='submit.'></td></tr>\n"
398 "</table>\n"
399 "</form>\n"
400 "</body>\n"
401 "</html>\n";
402 
403 
404 /* ------------------------------------------------------------------------- */
405 
406 /*
407  * Return a new string with spaces changed with &nbsp;
408  */
make_one_line_str(char * str)409 static char *make_one_line_str(char *str)
410 {
411    char *new_str;
412    int i, j, n;
413 
414    for (i = 0, n = 0; str[i]; ++i)
415       if (str[i] == ' ')
416          ++n;
417 
418    new_str = dNew(char, strlen(str) + 6*n + 1);
419    new_str[0] = 0;
420 
421    for (i = 0, j = 0; str[i]; ++i) {
422       if (str[i] == ' ') {
423          strcpy(new_str + j, "&nbsp;");
424          j += 6;
425       } else {
426          new_str[j] = str[i];
427          new_str[++j] = 0;
428       }
429    }
430 
431    return new_str;
432 }
433 
434 /*
435  * Given an urlencoded string, return it to the original version.
436  */
Unencode_str(char * e_str)437 static void Unencode_str(char *e_str)
438 {
439    char *p, *e;
440 
441    for (p = e = e_str; *e; e++, p++) {
442       if (*e == '+') {
443          *p = ' ';
444       } else if (*e == '%') {
445          if (dStrnAsciiCasecmp(e, "%0D%0A", 6) == 0) {
446             *p = '\n';
447             e += 5;
448          } else {
449             *p = (isdigit(e[1]) ? (e[1] - '0') : (e[1] - 'A' + 10)) * 16 +
450                  (isdigit(e[2]) ? (e[2] - '0') : (e[2] - 'A' + 10));
451             e += 2;
452          }
453       } else {
454          *p = *e;
455       }
456    }
457    *p = 0;
458 }
459 
460 /*
461  * Send a short message to dillo's status bar.
462  */
Bmsrv_dpi_send_status_msg(Dsh * sh,char * str)463 static int Bmsrv_dpi_send_status_msg(Dsh *sh, char *str)
464 {
465    int st;
466    char *d_cmd;
467 
468    d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "send_status_message", str);
469    st = a_Dpip_dsh_write_str(sh, 1, d_cmd);
470    dFree(d_cmd);
471    return st;
472 }
473 
474 /* -- ADT for bookmarks ---------------------------------------------------- */
475 /*
476  * Compare function for searching a bookmark by its key
477  */
Bms_node_by_key_cmp(const void * node,const void * key)478 static int Bms_node_by_key_cmp(const void *node, const void *key)
479 {
480    return ((BmRec *)node)->key - VOIDP2INT(key);
481 }
482 
483 /*
484  * Compare function for searching a bookmark by section
485  */
Bms_node_by_section_cmp(const void * node,const void * key)486 static int Bms_node_by_section_cmp(const void *node, const void *key)
487 {
488    return ((BmRec *)node)->section - VOIDP2INT(key);
489 }
490 
491 /*
492  * Compare function for searching a section by its number
493  */
Bms_sec_by_number_cmp(const void * node,const void * key)494 static int Bms_sec_by_number_cmp(const void *node, const void *key)
495 {
496    return ((BmSec *)node)->section - VOIDP2INT(key);
497 }
498 
499 /*
500  * Return the Bm record by key
501  */
Bms_get(int key)502 static BmRec *Bms_get(int key)
503 {
504    return dList_find_custom(B_bms, INT2VOIDP(key), Bms_node_by_key_cmp);
505 }
506 
507 /*
508  * Return the Section record by key
509  */
Bms_get_sec(int key)510 static BmSec *Bms_get_sec(int key)
511 {
512    return dList_find_custom(B_secs, INT2VOIDP(key), Bms_sec_by_number_cmp);
513 }
514 
515 /*
516  * Add a bookmark
517  */
Bms_add(int section,char * url,char * title)518 static void Bms_add(int section, char *url, char *title)
519 {
520    BmRec *bm_node;
521 
522    bm_node = dNew(BmRec, 1);
523    bm_node->key = ++bm_key;
524    bm_node->section = section;
525    bm_node->url = Escape_uri_str(url, "'");
526    bm_node->title = Escape_html_str(title);
527    dList_append(B_bms, bm_node);
528 }
529 
530 /*
531  * Add a section
532  */
Bms_sec_add(char * title)533 static void Bms_sec_add(char *title)
534 {
535    BmSec *sec_node;
536 
537    sec_node = dNew(BmSec, 1);
538    sec_node->section = sec_key++;
539    sec_node->title = Escape_html_str(title);
540    dList_append(B_secs, sec_node);
541 }
542 
543 /*
544  * Delete a bookmark by its key
545  */
Bms_del(int key)546 static void Bms_del(int key)
547 {
548    BmRec *bm_node;
549 
550    bm_node = dList_find_custom(B_bms, INT2VOIDP(key), Bms_node_by_key_cmp);
551    if (bm_node) {
552       dList_remove(B_bms, bm_node);
553       dFree(bm_node->title);
554       dFree(bm_node->url);
555       dFree(bm_node);
556    }
557    if (dList_length(B_bms) == 0)
558       bm_key = 0;
559 }
560 
561 /*
562  * Delete a section and its bookmarks by section number
563  */
Bms_sec_del(int section)564 static void Bms_sec_del(int section)
565 {
566    BmSec *sec_node;
567    BmRec *bm_node;
568 
569    sec_node = dList_find_custom(B_secs, INT2VOIDP(section),
570                                 Bms_sec_by_number_cmp);
571    if (sec_node) {
572       dList_remove(B_secs, sec_node);
573       dFree(sec_node->title);
574       dFree(sec_node);
575 
576       /* iterate B_bms and remove those that match the section */
577       while ((bm_node = dList_find_custom(B_bms, INT2VOIDP(section),
578                                           Bms_node_by_section_cmp))) {
579          Bms_del(bm_node->key);
580       }
581    }
582    if (dList_length(B_secs) == 0)
583       sec_key = 0;
584 }
585 
586 /*
587  * Move a bookmark to another section
588  */
Bms_move(int key,int target_section)589 static void Bms_move(int key, int target_section)
590 {
591    BmRec *bm_node;
592 
593    bm_node = dList_find_custom(B_bms, INT2VOIDP(key), Bms_node_by_key_cmp);
594    if (bm_node) {
595       bm_node->section = target_section;
596    }
597 }
598 
599 /*
600  * Update a bookmark title by key
601  */
Bms_update_title(int key,char * n_title)602 static void Bms_update_title(int key, char *n_title)
603 {
604    BmRec *bm_node;
605 
606    bm_node = dList_find_custom(B_bms, INT2VOIDP(key), Bms_node_by_key_cmp);
607    if (bm_node) {
608       dFree(bm_node->title);
609       bm_node->title = Escape_html_str(n_title);
610    }
611 }
612 
613 /*
614  * Update a section title by key
615  */
Bms_update_sec_title(int key,char * n_title)616 static void Bms_update_sec_title(int key, char *n_title)
617 {
618    BmSec *sec_node;
619 
620    sec_node = dList_find_custom(B_secs, INT2VOIDP(key), Bms_sec_by_number_cmp);
621    if (sec_node) {
622       dFree(sec_node->title);
623       sec_node->title = Escape_html_str(n_title);
624    }
625 }
626 
627 /*
628  * Free all the bookmarks data (bookmarks and sections)
629  */
Bms_free(void)630 static void Bms_free(void)
631 {
632    BmRec *bm_node;
633    BmSec *sec_node;
634 
635    /* free B_bms */
636    while ((bm_node = dList_nth_data(B_bms, 0))) {
637       Bms_del(bm_node->key);
638    }
639    /* free B_secs */
640    while ((sec_node = dList_nth_data(B_secs, 0))) {
641       Bms_sec_del(sec_node->section);
642    }
643 }
644 
645 /*
646  * Enforce increasing correlative section numbers with no jumps.
647  */
Bms_normalize(void)648 static void Bms_normalize(void)
649 {
650    BmRec *bm_node;
651    BmSec *sec_node;
652    int i, j;
653 
654    /* we need at least one section */
655    if (dList_length(B_secs) == 0)
656       Bms_sec_add("Unclassified");
657 
658    /* make correlative section numbers */
659    for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
660       sec_node->o_sec = sec_node->section;
661       sec_node->section = i;
662    }
663 
664    /* iterate B_secs and make the changes in B_bms */
665    for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
666       if (sec_node->section != sec_node->o_sec) {
667          /* update section numbers */
668          for (j = 0; (bm_node = dList_nth_data(B_bms, j)); ++j) {
669             if (bm_node->section == sec_node->o_sec)
670                bm_node->section = sec_node->section;
671          }
672       }
673    }
674 }
675 
676 /* -- Load bookmarks file -------------------------------------------------- */
677 
678 /*
679  * If there's no "bm.txt", create one from "bookmarks.html".
680  */
Bms_check_import(void)681 static void Bms_check_import(void)
682 {
683    char *OldBmFile;
684    char *cmd1 =
685       "echo \":s0: Unclassified\" > %s";
686    char *cmd2 =
687       "grep -i \"href\" %s | "
688       "sed -e 's/<li><A HREF=\"/s0 /' -e 's/\">/ /' -e 's/<.*$//' >> %s";
689    Dstr *dstr = dStr_new("");
690    int rc;
691 
692 
693    if (access(BmFile, F_OK) != 0) {
694       OldBmFile = dStrconcat(dGethomedir(), "/.dillo/bookmarks.html", NULL);
695       if (access(OldBmFile, F_OK) == 0) {
696          dStr_sprintf(dstr, cmd1, BmFile);
697          rc = system(dstr->str);
698          if (rc == 127) {
699             MSG("Bookmarks: /bin/sh could not be executed\n");
700          } else if (rc == -1) {
701             MSG("Bookmarks: process creation failure: %s\n",
702                 dStrerror(errno));
703          }
704          dStr_sprintf(dstr, cmd2, OldBmFile, BmFile);
705          rc = system(dstr->str);
706          if (rc == 127) {
707             MSG("Bookmarks: /bin/sh could not be executed\n");
708          } else if (rc == -1) {
709             MSG("Bookmarks: process creation failure: %s\n",
710                 dStrerror(errno));
711          }
712 
713          dStr_free(dstr, TRUE);
714          dFree(OldBmFile);
715       }
716    }
717 }
718 
719 /*
720  * Load bookmarks data from a file
721  */
Bms_load(void)722 static int Bms_load(void)
723 {
724    FILE *BmTxt;
725    char *buf, *p, *url, *title, *u_title;
726    int section;
727    struct stat TimeStamp;
728 
729    /* clear current bookmarks */
730    Bms_free();
731 
732    /* open bm file */
733    if (!(BmTxt = fopen(BmFile, "r"))) {
734       perror("[fopen]");
735       return 1;
736    }
737 
738    /* load bm file into memory */
739    while ((buf = dGetline(BmTxt)) != NULL) {
740       if (buf[0] == 's') {
741          /* get section, url and title */
742          section = strtol(buf + 1, NULL, 10);
743          p = strchr(buf, ' '); *p = 0;
744          url = ++p;
745          p = strchr(p, ' '); *p = 0;
746          title = ++p;
747          p = strchr(p, '\n'); *p = 0;
748          u_title = Unescape_html_str(title);
749          Bms_add(section, url, u_title);
750          dFree(u_title);
751 
752       } else if (buf[0] == ':' && buf[1] == 's') {
753          /* section = strtol(buf + 2, NULL, 10); */
754          p = strchr(buf + 2, ' ');
755          title = ++p;
756          p = strchr(p, '\n'); *p = 0;
757          Bms_sec_add(title);
758 
759       } else {
760          MSG("Syntax error in bookmarks file:\n %s", buf);
761       }
762       dFree(buf);
763    }
764    fclose(BmTxt);
765 
766    /* keep track of the timestamp */
767    stat(BmFile, &TimeStamp);
768    BmFileTimeStamp = TimeStamp.st_mtime;
769 
770    return 0;
771 }
772 
773 /*
774  * Load bookmarks data if:
775  *   - file timestamp is newer than ours  or
776  *   - we haven't loaded anything yet :)
777  */
Bms_cond_load(void)778 static int Bms_cond_load(void)
779 {
780    int st = 0;
781    struct stat TimeStamp;
782 
783    if (stat(BmFile, &TimeStamp) != 0) {
784       /* try to import... */
785       Bms_check_import();
786       if (stat(BmFile, &TimeStamp) != 0)
787          TimeStamp.st_mtime = 0;
788    }
789 
790    if (!BmFileTimeStamp || !dList_length(B_bms) || !dList_length(B_secs) ||
791        BmFileTimeStamp < TimeStamp.st_mtime) {
792       Bms_load();
793       st = 1;
794    }
795    return st;
796 }
797 
798 /* -- Save bookmarks file -------------------------------------------------- */
799 
800 /*
801  * Update the bookmarks file from memory contents
802  * Return code: { 0:OK, 1:Abort }
803  */
Bms_save(void)804 static int Bms_save(void)
805 {
806    FILE *BmTxt;
807    BmRec *bm_node;
808    BmSec *sec_node;
809    struct stat BmStat;
810    char *u_title;
811    int i, j;
812    Dstr *dstr = dStr_new("");
813 
814    /* make a safety backup */
815    if (stat(BmFile, &BmStat) == 0 && BmStat.st_size > 256) {
816       char *BmFileBak = dStrconcat(BmFile, ".bak", NULL);
817       rename(BmFile, BmFileBak);
818       dFree(BmFileBak);
819    }
820 
821    /* open bm file */
822    if (!(BmTxt = fopen(BmFile, "w"))) {
823       perror("[fopen]");
824       return 1;
825    }
826 
827    /* normalize */
828    Bms_normalize();
829 
830    /* save sections */
831    for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
832       u_title = Unescape_html_str(sec_node->title);
833       dStr_sprintf(dstr, ":s%d: %s\n", sec_node->section, u_title);
834       fwrite(dstr->str, (size_t)dstr->len, 1, BmTxt);
835       dFree(u_title);
836    }
837 
838    /* save bookmarks  (section url title) */
839    for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
840       for (j = 0; (bm_node = dList_nth_data(B_bms, j)); ++j) {
841          if (bm_node->section == sec_node->section) {
842             u_title = Unescape_html_str(bm_node->title);
843             dStr_sprintf(dstr, "s%d %s %s\n",
844                          bm_node->section, bm_node->url, u_title);
845             fwrite(dstr->str, (size_t)dstr->len, 1, BmTxt);
846             dFree(u_title);
847          }
848       }
849    }
850 
851    dStr_free(dstr, TRUE);
852    fclose(BmTxt);
853 
854    /* keep track of the timestamp */
855    stat(BmFile, &BmStat);
856    BmFileTimeStamp = BmStat.st_mtime;
857 
858    return 0;
859 }
860 
861 /* -- Add bookmark --------------------------------------------------------- */
862 
863 /*
864  * Add a new bookmark to DB :)
865  */
Bmsrv_add_bm(Dsh * sh,char * url,char * title)866 static int Bmsrv_add_bm(Dsh *sh, char *url, char *title)
867 {
868    char *u_title;
869    char *msg="Added bookmark!";
870    int section = 0;
871 
872    /* Add in memory */
873    u_title = Unescape_html_str(title);
874    Bms_add(section, url, u_title);
875    dFree(u_title);
876 
877    /* Write to file */
878    Bms_save();
879 
880    if (Bmsrv_dpi_send_status_msg(sh, msg))
881       return 1;
882 
883    return 0;
884 }
885 
886 /* -- Modify --------------------------------------------------------------- */
887 
888 /*
889  * Count how many sections and urls were marked in a request
890  */
Bmsrv_count_urls_and_sections(char * url,int * n_sec,int * n_url)891 static void Bmsrv_count_urls_and_sections(char *url, int *n_sec, int *n_url)
892 {
893    char *p, *q;
894    int i;
895 
896    /* Check marked urls and sections */
897    *n_sec = *n_url = 0;
898    if ((p = strchr(url, '?'))) {
899       for (q = p; (q = strstr(q, "&url")); ++q) {
900          for (i = 0; isdigit(q[4+i]); ++i);
901          *n_url += (q[4+i] == '=') ? 1 : 0;
902       }
903       for (q = p; (q = strstr(q, "&s")); ++q) {
904          for (i = 0; isdigit(q[2+i]); ++i);
905          *n_sec += (q[2+i] == '=') ? 1 : 0;
906       }
907    }
908 }
909 
910 /*
911  * Send a dpi reload request
912  * Return code: { 0:OK, 1:Abort, 2:Close }
913  */
Bmsrv_send_reload_request(Dsh * sh,char * url)914 static int Bmsrv_send_reload_request(Dsh *sh, char *url)
915 {
916    int st;
917    char *d_cmd;
918 
919    d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "reload_request", url);
920    st = a_Dpip_dsh_write_str(sh, 1, d_cmd) ? 1 : 0;
921    dFree(d_cmd);
922    return st;
923 }
924 
925 /*
926  * Send the HTML for the modify page
927  * Return code: { 0:OK, 1:Abort, 2:Close }
928  */
Bmsrv_send_modify_page(Dsh * sh)929 static int Bmsrv_send_modify_page(Dsh *sh)
930 {
931    static Dstr *dstr = NULL;
932    char *l_title;
933    BmSec *sec_node;
934    BmRec *bm_node;
935    int i, j;
936 
937    if (!dstr)
938       dstr = dStr_new("");
939 
940    /* send modify page header */
941    if (a_Dpip_dsh_write_str(sh, 0, modifypage_header))
942       return 1;
943 
944    /* write sections header */
945    if (a_Dpip_dsh_write_str(sh, 0, modifypage_sections_header))
946       return 1;
947    /* write sections */
948    for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
949       dStr_sprintf(dstr, modifypage_sections_item,
950                    sec_node->section, sec_node->section, sec_node->title);
951       if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
952          return 1;
953    }
954    /* write sections footer */
955    if (a_Dpip_dsh_write_str(sh, 0, modifypage_sections_footer))
956       return 1;
957 
958    /* send page middle */
959    if (a_Dpip_dsh_write_str(sh, 0, modifypage_middle1))
960       return 1;
961 
962    /* send bookmark cards */
963    for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
964       /* send card header */
965       l_title = make_one_line_str(sec_node->title);
966       dStr_sprintf(dstr, modifypage_section_card_header,
967                    sec_node->section, l_title);
968       dFree(l_title);
969       if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
970          return 1;
971 
972       /* send section's bookmarks */
973       for (j = 0; (bm_node = dList_nth_data(B_bms, j)); ++j) {
974          if (bm_node->section == sec_node->section) {
975             dStr_sprintf(dstr, modifypage_section_card_item,
976                          bm_node->key, bm_node->url, bm_node->title);
977             if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
978                return 1;
979          }
980       }
981 
982       /* send card footer */
983       if (a_Dpip_dsh_write_str(sh, 0, modifypage_section_card_footer))
984          return 1;
985    }
986 
987    /* finish page */
988    if (a_Dpip_dsh_write_str(sh, 1, modifypage_footer))
989       return 1;
990 
991    return 2;
992 }
993 
994 /*
995  * Send the HTML for the modify page for "add section"
996  * Return code: { 0:OK, 1:Abort, 2:Close }
997  */
Bmsrv_send_modify_page_add_section(Dsh * sh)998 static int Bmsrv_send_modify_page_add_section(Dsh *sh)
999 {
1000    /* send modify page2 */
1001    if (a_Dpip_dsh_write_str(sh, 1, modifypage_add_section_page))
1002       return 1;
1003 
1004    return 2;
1005 }
1006 
1007 /*
1008  * Send the HTML for the modify page for "add url"
1009  * Return code: { 0:OK, 1:Abort, 2:Close }
1010  */
Bmsrv_send_modify_page_add_url(Dsh * sh)1011 static int Bmsrv_send_modify_page_add_url(Dsh *sh)
1012 {
1013    if (a_Dpip_dsh_write_str(sh, 1, modifypage_add_url))
1014       return 1;
1015    return 2;
1016 }
1017 
1018 /*
1019  * Parse a modify urls request and either:
1020  *   - make a local copy of the url
1021  *     or
1022  *   - send the modify page for the marked urls and sections
1023  * Return code: { 0:OK, 1:Abort, 2:Close }
1024  */
Bmsrv_send_modify_update(Dsh * sh,char * url)1025 static int Bmsrv_send_modify_update(Dsh *sh, char *url)
1026 {
1027    static char *url1 = NULL;
1028    static Dstr *dstr = NULL;
1029    char *p, *q;
1030    int i, key, n_sec, n_url;
1031    BmRec *bm_node;
1032    BmSec *sec_node;
1033 
1034    /* bookmarks were loaded before */
1035 
1036    if (!dstr)
1037       dstr = dStr_new("");
1038 
1039    if (sh == NULL) {
1040       /* just copy url */
1041       dFree(url1);
1042       url1 = dStrdup(url);
1043       return 0;
1044    }
1045 
1046    /* send HTML here */
1047    if (a_Dpip_dsh_write_str(sh, 0, modifypage_update_header))
1048       return 1;
1049 
1050    /* Count number of marked urls and sections */
1051    Bmsrv_count_urls_and_sections(url1, &n_sec, &n_url);
1052 
1053    if (n_sec) {
1054       dStr_sprintf(dstr, modifypage_update_title, "Update&nbsp;sections:");
1055       a_Dpip_dsh_write_str(sh, 0, dstr->str);
1056       a_Dpip_dsh_write_str(sh, 0, modifypage_update_item_header);
1057       /* send items here */
1058       p = strchr(url1, '?');
1059       for (q = p; (q = strstr(q, "&s")); ++q) {
1060          for (i = 0; isdigit(q[2+i]); ++i);
1061          if (q[2+i] == '=') {
1062             key = strtol(q + 2, NULL, 10);
1063             if ((sec_node = Bms_get_sec(key))) {
1064                dStr_sprintf(dstr, modifypage_update_item2,
1065                             sec_node->section, sec_node->title);
1066                a_Dpip_dsh_write_str(sh, 0, dstr->str);
1067             }
1068          }
1069       }
1070       a_Dpip_dsh_write_str(sh, 0, modifypage_update_item_footer);
1071    }
1072 
1073    if (n_url) {
1074       dStr_sprintf(dstr, modifypage_update_title, "Update&nbsp;titles:");
1075       a_Dpip_dsh_write_str(sh, 0, dstr->str);
1076       a_Dpip_dsh_write_str(sh, 0, modifypage_update_item_header);
1077       /* send items here */
1078       p = strchr(url1, '?');
1079       for (q = p; (q = strstr(q, "&url")); ++q) {
1080          for (i = 0; isdigit(q[4+i]); ++i);
1081          if (q[4+i] == '=') {
1082             key = strtol(q + 4, NULL, 10);
1083             bm_node = Bms_get(key);
1084             dStr_sprintf(dstr, modifypage_update_item,
1085                          bm_node->key, bm_node->title, bm_node->url);
1086             a_Dpip_dsh_write_str(sh, 0, dstr->str);
1087          }
1088       }
1089       a_Dpip_dsh_write_str(sh, 0, modifypage_update_item_footer);
1090    }
1091 
1092    a_Dpip_dsh_write_str(sh, 1, modifypage_update_footer);
1093 
1094    return 2;
1095 }
1096 
1097 /*
1098  * Make the modify-page and send it back
1099  * Return code: { 0:OK, 1:Abort, 2:Close }
1100  */
Bmsrv_send_modify_answer(Dsh * sh,char * url)1101 static int Bmsrv_send_modify_answer(Dsh *sh, char *url)
1102 {
1103    char *d_cmd;
1104    int st;
1105 
1106    d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
1107    st = a_Dpip_dsh_write_str(sh, 1, d_cmd);
1108    dFree(d_cmd);
1109    if (st != 0)
1110       return 1;
1111 
1112    /* Send HTTP header */
1113    if (a_Dpip_dsh_write_str(sh, 0, Header) != 0) {
1114       return 1;
1115    }
1116 
1117    if (MODIFY_PAGE_NUM == 2) {
1118       MODIFY_PAGE_NUM = 1;
1119       return Bmsrv_send_modify_page_add_section(sh);
1120    } else if (MODIFY_PAGE_NUM == 3) {
1121       MODIFY_PAGE_NUM = 1;
1122       return Bmsrv_send_modify_update(sh, NULL);
1123    } else if (MODIFY_PAGE_NUM == 4) {
1124       MODIFY_PAGE_NUM = 1;
1125       return Bmsrv_send_modify_page_add_url(sh);
1126    } else {
1127       return Bmsrv_send_modify_page(sh);
1128    }
1129 }
1130 
1131 
1132 /* Operations */
1133 
1134 /*
1135  * Parse a delete bms request, delete them, and update bm file.
1136  * Return code: { 0:OK, 1:Abort }
1137  */
Bmsrv_modify_delete(char * url)1138 static int Bmsrv_modify_delete(char *url)
1139 {
1140    char *p;
1141    int nb, ns, key;
1142 
1143    /* bookmarks were loaded before */
1144 
1145    /* Remove marked sections */
1146    p = strchr(url, '?');
1147    for (ns = 0; (p = strstr(p, "&s")); ++p) {
1148       if (isdigit(p[2])) {
1149          key = strtol(p + 2, NULL, 10);
1150          Bms_sec_del(key);
1151          ++ns;
1152       }
1153    }
1154 
1155    /* Remove marked urls */
1156    p = strchr(url, '?');
1157    for (nb = 0; (p = strstr(p, "&url")); ++p) {
1158       if (isdigit(p[4])) {
1159          key = strtol(p + 4, NULL, 10);
1160          Bms_del(key);
1161          ++nb;
1162       }
1163    }
1164 
1165 /* -- This doesn't work because dillo erases the message upon the
1166  *    receipt of the first data stream.
1167  *
1168    sprintf(msg, "Deleted %d bookmark%s!>", n, (n > 1) ? "s" : "");
1169    if (Bmsrv_dpi_send_status_msg(sh, msg))
1170       return 1;
1171 */
1172 
1173    /* Write new bookmarks file */
1174    if (nb || ns)
1175       Bms_save();
1176 
1177    return 0;
1178 }
1179 
1180 /*
1181  * Parse a move urls request, move and update bm file.
1182  * Return code: { 0:OK, 1:Abort }
1183  */
Bmsrv_modify_move(char * url)1184 static int Bmsrv_modify_move(char *url)
1185 {
1186    char *p;
1187    int n, section = 0, key;
1188 
1189    /* bookmarks were loaded before */
1190 
1191    /* get target section */
1192    for (p = url; (p = strstr(p, "&s")); ++p) {
1193       if (isdigit(p[2])) {
1194          section = strtol(p + 2, NULL, 10);
1195          break;
1196       }
1197    }
1198    if (!p)
1199       return 1;
1200 
1201    /* move marked urls */
1202    p = strchr(url, '?');
1203    for (n = 0; (p = strstr(p, "&url")); ++p) {
1204       if (isdigit(p[4])) {
1205          key = strtol(p + 4, NULL, 10);
1206          Bms_move(key, section);
1207          ++n;
1208       }
1209    }
1210 
1211    /* Write new bookmarks file */
1212    if (n) {
1213       Bms_save();
1214    }
1215 
1216    return 0;
1217 }
1218 
1219 /*
1220  * Parse a modify request: update urls and sections, then save.
1221  * Return code: { 0:OK, 1:Abort }
1222  */
Bmsrv_modify_update(char * url)1223 static int Bmsrv_modify_update(char *url)
1224 {
1225    char *p, *q, *title;
1226    int i, key;
1227 
1228    /* bookmarks were loaded before */
1229 
1230    p = strchr(url, '?');
1231    for (  ; (p = strstr(p, "s")); ++p) {
1232       if (p[-1] == '&' || p[-1] == '?' ) {
1233          for (i = 0; isdigit(p[1 + i]); ++i);
1234          if (i && p[1 + i] == '=') {
1235             /* we have a title/key to change */
1236             key = strtol(p + 1, NULL, 10);
1237             if ((q = strchr(p + 1, '&')))
1238                title = dStrndup(p + 2 + i, (uint_t)(q - (p + 2 + i)));
1239             else
1240                title = dStrdup(p + 2 + i);
1241 
1242             Unencode_str(title);
1243             Bms_update_sec_title(key, title);
1244             dFree(title);
1245          }
1246       }
1247    }
1248 
1249    p = strchr(url, '?');
1250    for (  ; (p = strstr(p, "title")); ++p) {
1251       if (p[-1] == '&' || p[-1] == '?' ) {
1252          for (i = 0; isdigit(p[5 + i]); ++i);
1253          if (i && p[5 + i] == '=') {
1254             /* we have a title/key to change */
1255             key = strtol(p + 5, NULL, 10);
1256             if ((q = strchr(p + 5, '&')))
1257                title = dStrndup(p + 6 + i, (uint_t)(q - (p + 6 + i)));
1258             else
1259                title = dStrdup(p + 6 + i);
1260 
1261             Unencode_str(title);
1262             Bms_update_title(key, title);
1263             dFree(title);
1264          }
1265       }
1266    }
1267 
1268    /* Write new bookmarks file */
1269    Bms_save();
1270 
1271    return 0;
1272 }
1273 
1274 /*
1275  * Parse an "add section" request, and update the bm file.
1276  * Return code: { 0:OK, 1:Abort }
1277  */
Bmsrv_modify_add_section(char * url)1278 static int Bmsrv_modify_add_section(char *url)
1279 {
1280    char *p, *title = NULL;
1281 
1282    /* bookmarks were loaded before */
1283 
1284    /* get new section's title */
1285    if ((p = strstr(url, "&title="))) {
1286       title = dStrdup (p + 7);
1287       if ((p = strchr(title, '&')))
1288          *p = 0;
1289       Unencode_str(title);
1290    } else
1291       return 1;
1292 
1293    Bms_sec_add(title);
1294    dFree(title);
1295 
1296    /* Write new bookmarks file */
1297    Bms_save();
1298 
1299    return 0;
1300 }
1301 
1302 /*
1303  * Parse an "add url" request, and update the bm file.
1304  * Return code: { 0:OK, 1:Abort }
1305  */
Bmsrv_modify_add_url(Dsh * sh,char * s_url)1306 static int Bmsrv_modify_add_url(Dsh *sh, char *s_url)
1307 {
1308    char *p, *q, *title, *u_title, *url;
1309    int i;
1310    static int section = 0;
1311 
1312    /* bookmarks were loaded before */
1313 
1314    if (sh == NULL) {
1315       /* look for section */
1316       for (q = s_url; (q = strstr(q, "&s")); ++q) {
1317          for (i = 0; isdigit(q[2+i]); ++i);
1318          if (q[2+i] == '=')
1319             section = strtol(q + 2, NULL, 10);
1320       }
1321       return 1;
1322    }
1323 
1324    if (!(p = strstr(s_url, "&title=")) ||
1325        !(q = strstr(s_url, "&url=")))
1326       return 1;
1327 
1328    title = dStrdup (p + 7);
1329    if ((p = strchr(title, '&')))
1330       *p = 0;
1331    url = dStrdup (q + 5);
1332    if ((p = strchr(url, '&')))
1333       *p = 0;
1334    if (strlen(title) && strlen(url)) {
1335       Unencode_str(title);
1336       Unencode_str(url);
1337       u_title = Unescape_html_str(title);
1338       Bms_add(section, url, u_title);
1339       dFree(u_title);
1340    }
1341    dFree(title);
1342    dFree(url);
1343    section = 0;
1344 
1345    /* TODO: we should send an "Bookmark added" message, but the
1346       msg-after-HTML functionallity is still pending, not hard though. */
1347 
1348    /* Write new bookmarks file */
1349    Bms_save();
1350 
1351    return 0;
1352 }
1353 
1354 /*
1355  * Check the parameters of a modify request, and return an error message
1356  * when it's wrong.
1357  * Return code: { 0:OK, 2:Close }
1358  */
Bmsrv_check_modify_request(Dsh * sh,char * url)1359 static int Bmsrv_check_modify_request(Dsh *sh, char *url)
1360 {
1361    char *p, *msg;
1362    int n_sec, n_url;
1363 
1364    /* Count number of marked urls and sections */
1365    Bmsrv_count_urls_and_sections(url, &n_sec, &n_url);
1366 
1367    p = strchr(url, '?');
1368    if (strstr(p, "operation=delete&")) {
1369       if (n_url || n_sec)
1370          return 0;
1371       msg = "Delete: you must mark what to delete!";
1372 
1373    } else if (strstr(url, "operation=move&")) {
1374       if (n_url && n_sec)
1375          return 0;
1376       else if (n_url)
1377          msg = "Move: you must mark a target section!";
1378       else if (n_sec)
1379          msg = "Move: can not move a section (yet).";
1380       else
1381          msg = "Move: you must mark some urls, and a target section!";
1382 
1383    } else if (strstr(url, "operation=modify&")) {
1384       if (n_url || n_sec)
1385          return 0;
1386       msg = "Modify: you must mark what to update!";
1387 
1388    } else if (strstr(url, "operation=modify2&")) {
1389       /* nothing to check here */
1390       return 0;
1391 
1392    } else if (strstr(url, "operation=add_sec&")) {
1393       /* nothing to check here */
1394       return 0;
1395 
1396    } else if (strstr(url, "operation=add_section&")) {
1397       /* nothing to check here */
1398       return 0;
1399 
1400    } else if (strstr(url, "operation=add_url&")) {
1401       if (n_sec <= 1)
1402          return 0;
1403       msg = "Add url: only one target section is allowed!";
1404 
1405    } else if (strstr(url, "operation=add_url2&")) {
1406       /* nothing to check here */
1407       return 0;
1408 
1409    } else if (strstr(url, "operation=none&")) {
1410       msg = "No operation, just do nothing!";
1411 
1412    } else {
1413       msg = "Sorry, not implemented yet.";
1414    }
1415 
1416    Bmsrv_dpi_send_status_msg(sh, msg);
1417    return 2;
1418 }
1419 
1420 /*
1421  * Parse a and process a modify request.
1422  * Return code: { 0:OK, 1:Abort, 2:Close }
1423  */
Bmsrv_process_modify_request(Dsh * sh,char * url)1424 static int Bmsrv_process_modify_request(Dsh *sh, char *url)
1425 {
1426    /* check the provided parameters */
1427    if (Bmsrv_check_modify_request(sh, url) != 0)
1428       return 2;
1429 
1430    if (strstr(url, "operation=delete&")) {
1431       if (Bmsrv_modify_delete(url) == 1)
1432          return 1;
1433       if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
1434          return 1;
1435 
1436    } else if (strstr(url, "operation=move&")) {
1437       if (Bmsrv_modify_move(url) == 1)
1438          return 1;
1439       if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
1440          return 1;
1441 
1442    } else if (strstr(url, "operation=modify&")) {
1443       /* make a local copy of 'url' */
1444       Bmsrv_send_modify_update(NULL, url);
1445       MODIFY_PAGE_NUM = 3;
1446       if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
1447          return 1;
1448 
1449    } else if (strstr(url, "operation=modify2&")) {
1450       if (Bmsrv_modify_update(url) == 1)
1451          return 1;
1452       if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
1453          return 1;
1454 
1455    } else if (strstr(url, "operation=add_sec&")) {
1456       /* this global variable tells which page to send  (--hackish...) */
1457       MODIFY_PAGE_NUM = 2;
1458       if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
1459          return 1;
1460 
1461    } else if (strstr(url, "operation=add_section&")) {
1462       if (Bmsrv_modify_add_section(url) == 1)
1463          return 1;
1464       if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
1465          return 1;
1466 
1467    } else if (strstr(url, "operation=add_url&")) {
1468       /* this global variable tells which page to send  (--hackish...) */
1469       MODIFY_PAGE_NUM = 4;
1470       /* parse section if present */
1471       Bmsrv_modify_add_url(NULL, url);
1472       if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
1473          return 1;
1474 
1475    } else if (strstr(url, "operation=add_url2&")) {
1476       if (Bmsrv_modify_add_url(sh, url) == 1)
1477          return 1;
1478       if (Bmsrv_send_reload_request(sh, "dpi:/bm/modify") == 1)
1479          return 1;
1480    }
1481 
1482    return 2;
1483 }
1484 
1485 /* -- Bookmarks ------------------------------------------------------------ */
1486 
1487 /*
1488  * Send the current bookmarks page (in HTML)
1489  */
send_bm_page(Dsh * sh)1490 static int send_bm_page(Dsh *sh)
1491 {
1492    static Dstr *dstr = NULL;
1493    char *l_title;
1494    BmSec *sec_node;
1495    BmRec *bm_node;
1496    int i, j;
1497 
1498    if (!dstr)
1499       dstr = dStr_new("");
1500 
1501    if (a_Dpip_dsh_write_str(sh, 0, mainpage_header))
1502       return 1;
1503 
1504    /* write sections header */
1505    if (a_Dpip_dsh_write_str(sh, 0, mainpage_sections_header))
1506       return 1;
1507    /* write sections */
1508    for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
1509       dStr_sprintf(dstr, mainpage_sections_item,
1510                    sec_node->section, sec_node->title);
1511       if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
1512          return 1;
1513    }
1514    /* write sections footer */
1515    if (a_Dpip_dsh_write_str(sh, 0, mainpage_sections_footer))
1516       return 1;
1517 
1518    /* send page middle */
1519    if (a_Dpip_dsh_write_str(sh, 0, mainpage_middle1))
1520       return 1;
1521 
1522    /* send bookmark cards */
1523    for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
1524       /* send card header */
1525       l_title = make_one_line_str(sec_node->title);
1526       dStr_sprintf(dstr, mainpage_section_card_header,
1527                    sec_node->section, l_title);
1528       dFree(l_title);
1529       if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
1530          return 1;
1531 
1532       /* send section's bookmarks */
1533       for (j = 0; (bm_node = dList_nth_data(B_bms, j)); ++j) {
1534          if (bm_node->section == sec_node->section) {
1535             dStr_sprintf(dstr, mainpage_section_card_item,
1536                          bm_node->url, bm_node->title);
1537             if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
1538                return 1;
1539          }
1540       }
1541 
1542       /* send card footer */
1543       if (a_Dpip_dsh_write_str(sh, 0, mainpage_section_card_footer))
1544          return 1;
1545    }
1546 
1547    /* finish page */
1548    if (a_Dpip_dsh_write_str(sh, 1, mainpage_footer))
1549       return 1;
1550 
1551    return 0;
1552 }
1553 
1554 
1555 /* -- Dpi parser ----------------------------------------------------------- */
1556 
1557 /*
1558  * Parse a data stream (dpi protocol)
1559  * Note: Buf is a dpip token (zero terminated string)
1560  * Return code: { 0:OK, 1:Abort, 2:Close }
1561  */
Bmsrv_parse_token(Dsh * sh,char * Buf)1562 static int Bmsrv_parse_token(Dsh *sh, char *Buf)
1563 {
1564    static char *msg1=NULL, *msg2=NULL, *msg3=NULL;
1565    char *cmd, *d_cmd, *url, *title, *msg;
1566    size_t BufSize;
1567    int st;
1568 
1569    if (!msg1) {
1570      /* Initialize data for the "chat" command. */
1571      msg1 = a_Dpip_build_cmd("cmd=%s msg=%s", "chat", "Hi browser");
1572      msg2 = a_Dpip_build_cmd("cmd=%s msg=%s", "chat", "Is it worth?");
1573      msg3 = a_Dpip_build_cmd("cmd=%s msg=%s", "chat", "Ok, send it");
1574    }
1575 
1576    if (sh->mode & DPIP_RAW) {
1577       MSG("ERROR: Unhandled DPIP_RAW mode!\n");
1578       return 1;
1579    }
1580 
1581    BufSize = strlen(Buf);
1582    cmd = a_Dpip_get_attr_l(Buf, BufSize, "cmd");
1583 
1584    if (cmd && strcmp(cmd, "chat") == 0) {
1585       dFree(cmd);
1586       msg = a_Dpip_get_attr_l(Buf, BufSize, "msg");
1587       if (*msg == 'H') {
1588          /* "Hi server" */
1589          if (a_Dpip_dsh_write_str(sh, 1, msg1))
1590             return 1;
1591       } else if (*msg == 'I') {
1592          /* "I want to set abookmark" */
1593          if (a_Dpip_dsh_write_str(sh, 1, msg2))
1594             return 1;
1595       } else if (*msg == 'S') {
1596          /* "Sure" */
1597          if (a_Dpip_dsh_write_str(sh, 1, msg3))
1598             return 1;
1599       }
1600       dFree(msg);
1601       return 0;
1602    }
1603 
1604    /* sync with the bookmarks file */
1605    Bms_cond_load();
1606 
1607    if (cmd && strcmp(cmd, "DpiBye") == 0) {
1608       MSG("(pid %d): Got DpiBye.\n", (int)getpid());
1609       exit(0);
1610 
1611    } else if (cmd && strcmp(cmd, "add_bookmark") == 0) {
1612       dFree(cmd);
1613       url = a_Dpip_get_attr_l(Buf, BufSize, "url");
1614       title = a_Dpip_get_attr_l(Buf, BufSize, "title");
1615       if (strlen(title) == 0) {
1616          dFree(title);
1617          title = dStrdup("(Untitled)");
1618       }
1619       if (url && title)
1620          Bmsrv_add_bm(sh, url, title);
1621       dFree(url);
1622       dFree(title);
1623       return 2;
1624 
1625    } else if (cmd && strcmp(cmd, "open_url") == 0) {
1626       dFree(cmd);
1627       url = a_Dpip_get_attr_l(Buf, BufSize, "url");
1628 
1629       if (dStrnAsciiCasecmp(url, "dpi:", 4) == 0) {
1630          if (strcmp(url+4, "/bm/modify") == 0) {
1631             st = Bmsrv_send_modify_answer(sh, url);
1632             dFree(url);
1633             return st;
1634          } else if (strncmp(url+4, "/bm/modify?", 11) == 0) {
1635             /* process request */
1636             st = Bmsrv_process_modify_request(sh, url);
1637             dFree(url);
1638             return st;
1639          }
1640       }
1641 
1642 
1643       d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
1644       dFree(url);
1645       st = a_Dpip_dsh_write_str(sh, 1, d_cmd);
1646       dFree(d_cmd);
1647       if (st != 0)
1648          return 1;
1649 
1650       /* Send HTTP header */
1651       if (a_Dpip_dsh_write_str(sh, 1, Header) != 0) {
1652          return 1;
1653       }
1654 
1655       st = send_bm_page(sh);
1656       if (st != 0) {
1657          char *err =
1658             DOCTYPE
1659             "<HTML><body id='dillo_bm'> Error on the bookmarks server..."
1660             "      </body></html>";
1661          if (a_Dpip_dsh_write_str(sh, 1, err) != 0) {
1662             return 1;
1663          }
1664       }
1665       return 2;
1666    }
1667 
1668    return 0;
1669 }
1670 
1671 /* --  Termination handlers ----------------------------------------------- */
1672 /*
1673  * (was to delete the local namespace socket),
1674  *  but this is handled by 'dpid' now.
1675  */
cleanup(void)1676 static void cleanup(void)
1677 {
1678   /* no cleanup required */
1679 }
1680 
1681 /*
1682  * Perform any necessary cleanups upon abnormal termination
1683  */
termination_handler(int signum)1684 static void termination_handler(int signum)
1685 {
1686   exit(signum);
1687 }
1688 
1689 
1690 /*
1691  * -- MAIN -------------------------------------------------------------------
1692  */
main(void)1693 int main(void) {
1694    struct sockaddr_un spun;
1695    int sock_fd, code;
1696    socklen_t address_size;
1697    char *tok;
1698    Dsh *sh;
1699 
1700    /* Arrange the cleanup function for terminations via exit() */
1701    atexit(cleanup);
1702 
1703    /* Arrange the cleanup function for abnormal terminations */
1704    if (signal (SIGINT, termination_handler) == SIG_IGN)
1705      signal (SIGINT, SIG_IGN);
1706    if (signal (SIGHUP, termination_handler) == SIG_IGN)
1707      signal (SIGHUP, SIG_IGN);
1708    if (signal (SIGTERM, termination_handler) == SIG_IGN)
1709      signal (SIGTERM, SIG_IGN);
1710 
1711    /* We may receive SIGPIPE (e.g. socket is closed early by our client) */
1712    signal(SIGPIPE, SIG_IGN);
1713 
1714    /* Initialize local data */
1715    B_bms = dList_new(512);
1716    B_secs = dList_new(32);
1717    BmFile = dStrconcat(dGethomedir(), "/.dillo/bm.txt", NULL);
1718    /* some OSes may need this... */
1719    address_size = sizeof(struct sockaddr_un);
1720 
1721    MSG("(v.13): accepting connections...\n");
1722 
1723    while (1) {
1724       sock_fd = accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size);
1725       if (sock_fd == -1) {
1726          perror("[accept]");
1727          exit(1);
1728       }
1729 
1730       /* create the Dsh structure */
1731       sh = a_Dpip_dsh_new(sock_fd, sock_fd, 8*1024);
1732 
1733       /* Authenticate our client... */
1734       if (!(tok = a_Dpip_dsh_read_token(sh, 1)) ||
1735           a_Dpip_check_auth(tok) < 0) {
1736          MSG("can't authenticate request: %s\n", dStrerror(errno));
1737          a_Dpip_dsh_close(sh);
1738          exit(1);
1739       }
1740       dFree(tok);
1741 
1742       while (1) {
1743          code = 1;
1744          if ((tok = a_Dpip_dsh_read_token(sh, 1)) != NULL) {
1745             /* Let's see what we fished... */
1746             code = Bmsrv_parse_token(sh, tok);
1747          }
1748          dFree(tok);
1749 
1750          if (code != 0) {
1751             /* socket is not operative (e.g. closed by client) */
1752             break;
1753          }
1754       }
1755 
1756       a_Dpip_dsh_close(sh);
1757       a_Dpip_dsh_free(sh);
1758 
1759    }/*while*/
1760 }
1761