~ubuntu-branches/ubuntu/hardy/hyperestraier/hardy

« back to all changes in this revision

Viewing changes to wavermod.c

  • Committer: Bazaar Package Importer
  • Author(s): Steve Langasek
  • Date: 2006-11-14 05:28:32 UTC
  • mfrom: (2.1.4 feisty)
  • Revision ID: james.westby@ubuntu.com-20061114052832-0lzqzcefn8mt4yqe
Tags: 1.4.9-1.1
* Non-maintainer upload.
* High-urgency upload for RC bugfix.
* Set HOME=$(CURDIR)/junkhome when building, otherwise the package build
  will incorrectly look for headers there -- and fail when the directory
  exists and is unreadable, as happens sometimes on sudo-using
  autobuilders!

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*************************************************************************************************
 
2
 * Implementation of wavermod
 
3
 *                                                      Copyright (C) 2004-2006 Mikio Hirabayashi
 
4
 * This file is part of Hyper Estraier.
 
5
 * Hyper Estraier is free software; you can redistribute it and/or modify it under the terms of
 
6
 * the GNU Lesser General Public License as published by the Free Software Foundation; either
 
7
 * version 2.1 of the License or any later version.  Hyper Estraier is distributed in the hope
 
8
 * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 
10
 * License for more details.
 
11
 * You should have received a copy of the GNU Lesser General Public License along with Hyper
 
12
 * Estraier; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 
13
 * Boston, MA 02111-1307 USA.
 
14
 *************************************************************************************************/
 
15
 
 
16
 
 
17
#include "wavermod.h"
 
18
 
 
19
 
 
20
/* private function prototypes */
 
21
static void log_close(void);
 
22
static void db_informer(const char *message, void *opaque);
 
23
static const char *skiplabel(const char *str);
 
24
static char *makeabspath(const char *rootdir, const char *path);
 
25
static int queue_compare(const char *aptr, int asiz, const char *bptr, int bsiz);
 
26
static int keysc_compare(const void *ap, const void *bp);
 
27
static void make_doc_from_draft(const char *buf, int size, ESTDOC *doc, CBLIST *links);
 
28
static void make_doc_from_text(const char *buf, int size, const char *penc, int plang,
 
29
                               ESTDOC *doc, CBLIST *links);
 
30
static int check_binary(const char *buf, int size);
 
31
static void make_doc_from_html(const char *buf, int size, const char *penc, int plang,
 
32
                               ESTDOC *doc, CBLIST *links);
 
33
static char *html_enc(const char *str);
 
34
static char *html_raw_text(const char *html);
 
35
static void make_doc_from_mime(const char *buf, int size, const char *penc, int plang,
 
36
                               ESTDOC *doc, CBLIST *links);
 
37
static void doc_add_attr_mime(ESTDOC *doc, const char *name, const char *value);
 
38
static void make_doc_with_xcmd(const char *xcmd, const char *url, const char *buf, int size,
 
39
                               const char *penc, int plang, ESTDOC *doc, CBLIST *links);
 
40
 
 
41
 
 
42
 
 
43
/*************************************************************************************************
 
44
 * pseudo API
 
45
 *************************************************************************************************/
 
46
 
 
47
 
 
48
/* The handles of the log file. */
 
49
FILE *log_fp = NULL;
 
50
 
 
51
 
 
52
/* Level of logging. */
 
53
int log_level = LL_INFO;
 
54
 
 
55
 
 
56
/* Open the log file. */
 
57
int log_open(const char *rootdir, const char *path, int level, int trunc){
 
58
  char mypath[URIBUFSIZ];
 
59
  assert(rootdir && path);
 
60
  log_level = level;
 
61
  if(log_fp) return TRUE;
 
62
  if((ESTPATHCHR == '/' && path[0] == ESTPATHCHR) ||
 
63
     (ESTPATHCHR == '\\' && ((path[0] >= 'A' && path[0] <= 'Z') ||
 
64
                             (path[0] >= 'a' && path[0] <= 'z')) && path[1] == ':' &&
 
65
      path[2] == '\\')){
 
66
    sprintf(mypath, "%s", path);
 
67
  } else {
 
68
    sprintf(mypath, "%s%c%s", rootdir, ESTPATHCHR, path);
 
69
  }
 
70
  if(!(log_fp = fopen(mypath, trunc ? "wb" : "ab"))) return FALSE;
 
71
  if(level == LL_CHECK){
 
72
    fclose(log_fp);
 
73
    log_fp = NULL;
 
74
    return TRUE;
 
75
  }
 
76
  atexit(log_close);
 
77
  return TRUE;
 
78
}
 
79
 
 
80
 
 
81
/* Print formatted string into the log file. */
 
82
void log_print(int level, const char *format, ...){
 
83
  static pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
 
84
  va_list ap, aq;
 
85
  const char *lvstr;
 
86
  char *date;
 
87
  if(level < log_level) return;
 
88
  if(pthread_mutex_lock(&mymutex) != 0) return;
 
89
  va_start(ap, format);
 
90
  est_va_copy(aq, ap);
 
91
  switch(level){
 
92
  case LL_DEBUG: lvstr = "DEBUG"; break;
 
93
  case LL_INFO: lvstr = "INFO"; break;
 
94
  case LL_WARN: lvstr = "WARN"; break;
 
95
  default: lvstr = "ERROR"; break;
 
96
  }
 
97
  date = cbdatestrwww(time(NULL), 0);
 
98
  printf("%s\t%s\t", date, lvstr);
 
99
  vprintf(format, ap);
 
100
  putchar('\n');
 
101
  fflush(stdout);
 
102
  if(log_fp){
 
103
    fprintf(log_fp, "%s\t%s\t", date, lvstr);
 
104
    vfprintf(log_fp, format, aq);
 
105
    fputc('\n', log_fp);
 
106
    fflush(log_fp);
 
107
  }
 
108
  free(date);
 
109
  va_end(aq);
 
110
  va_end(ap);
 
111
  pthread_mutex_unlock(&mymutex);
 
112
}
 
113
 
 
114
 
 
115
/* Rotete the log file. */
 
116
int log_rotate(const char *rootdir, const char *path){
 
117
  FILE *ifp, *ofp;
 
118
  char mypath[URIBUFSIZ], *wp, iobuf[IOBUFSIZ];
 
119
  int err, year, month, day, hour, minute, second, len;
 
120
  assert(rootdir && path);
 
121
  if(!log_fp || fflush(log_fp) == -1) return FALSE;
 
122
  err = FALSE;
 
123
  wp = mypath;
 
124
  if((ESTPATHCHR == '/' && path[0] == ESTPATHCHR) ||
 
125
     (ESTPATHCHR == '\\' && ((path[0] >= 'A' && path[0] <= 'Z') ||
 
126
                             (path[0] >= 'a' && path[0] <= 'z')) && path[1] == ':' &&
 
127
      path[2] == '\\')){
 
128
    wp += sprintf(wp, "%s", path);
 
129
  } else {
 
130
    wp += sprintf(wp, "%s%c%s", rootdir, ESTPATHCHR, path);
 
131
  }
 
132
  if(!(ifp = fopen(mypath, "rb"))) return FALSE;
 
133
  cbcalendar(-1, 0, &year, &month, &day, &hour, &minute, &second);
 
134
  sprintf(wp, "-%04d%02d%02d%02d%02d%02d", year, month, day, hour, minute, second);
 
135
  if(!(ofp = fopen(mypath, "wb"))){
 
136
    fclose(ifp);
 
137
    return FALSE;
 
138
  }
 
139
  while((len = fread(iobuf, 1, IOBUFSIZ, ifp)) > 0){
 
140
    fwrite(iobuf, 1, len, ofp);
 
141
  }
 
142
  if(fclose(ofp) == -1) err = TRUE;
 
143
  if(fclose(ifp) == -1) err = TRUE;
 
144
  if(fseek(log_fp, 0, SEEK_SET) == -1 || fflush(log_fp) == -1) err = TRUE;
 
145
  if(ftruncate(fileno(log_fp), 0) == -1) err = TRUE;
 
146
  return err ? FALSE : TRUE;
 
147
}
 
148
 
 
149
 
 
150
/* Initialize the root directory. */
 
151
int waver_init(const char *rootdir, int options){
 
152
  DEPOT *metadb;
 
153
  CURIA *trace;
 
154
  QUEUE *queue;
 
155
  ESTMTDB *index;
 
156
  FILE *ofp;
 
157
  char path[URIBUFSIZ];
 
158
  int err, tracebnum, estopts, ecode;
 
159
  assert(rootdir);
 
160
  if(est_mkdir(rootdir) == -1 && errno != EEXIST) return FALSE;
 
161
  err = FALSE;
 
162
  sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, METAFILE);
 
163
  if((metadb = dpopen(path, DP_OWRITER | DP_OCREAT | DP_OTRUNC, MINIBNUM))){
 
164
    if(!dpput(metadb, MMKMAGIC, -1, MMKMAGVAL, -1, DP_DKEEP)) err = TRUE;
 
165
    if(!dpclose(metadb)) err = TRUE;
 
166
  } else {
 
167
    err = TRUE;
 
168
  }
 
169
  sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, CONFFILE);
 
170
  if((ofp = fopen(path, "wb")) != NULL){
 
171
    fprintf(ofp, "# seed documents (weight and URL)\n");
 
172
    fprintf(ofp, "seed: 1.5|http://hyperestraier.sourceforge.net/uguide-en.html\n");
 
173
    fprintf(ofp, "seed: 1.0|http://hyperestraier.sourceforge.net/pguide-en.html\n");
 
174
    fprintf(ofp, "seed: 1.0|http://hyperestraier.sourceforge.net/nguide-en.html\n");
 
175
    fprintf(ofp, "seed: 0.0|http://qdbm.sourceforge.net/\n");
 
176
    fprintf(ofp, "\n");
 
177
    fprintf(ofp, "# host name of the proxy\n");
 
178
    fprintf(ofp, "proxyhost:\n");
 
179
    fprintf(ofp, "\n");
 
180
    fprintf(ofp, "# port number of the proxy\n");
 
181
    fprintf(ofp, "proxyport:\n");
 
182
    fprintf(ofp, "\n");
 
183
    fprintf(ofp, "# waiting interval of each request (in milliseconds)\n");
 
184
    fprintf(ofp, "interval: 500\n");
 
185
    fprintf(ofp, "\n");
 
186
    fprintf(ofp, "# timeout of each request (in seconds)\n");
 
187
    fprintf(ofp, "timeout: 30\n");
 
188
    fprintf(ofp, "\n");
 
189
    fprintf(ofp, "# strategy of crawling path"
 
190
            " (0:balanced, 1:similarity, 2:depth, 3:width, 4:random)\n");
 
191
    fprintf(ofp, "strategy: %d\n", CS_BALANCED);
 
192
    fprintf(ofp, "\n");
 
193
    fprintf(ofp, "# inheritance ratio of similarity from the parent\n");
 
194
    fprintf(ofp, "inherit: 0.4\n");
 
195
    fprintf(ofp, "\n");
 
196
    fprintf(ofp, "# maximum depth of seed documents\n");
 
197
    fprintf(ofp, "seeddepth: 0\n");
 
198
    fprintf(ofp, "\n");
 
199
    fprintf(ofp, "# maximum depth of recursion\n");
 
200
    fprintf(ofp, "maxdepth: 20\n");
 
201
    fprintf(ofp, "\n");
 
202
    fprintf(ofp, "# standard value for checking mass sites\n");
 
203
    fprintf(ofp, "masscheck: 500\n");
 
204
    fprintf(ofp, "\n");
 
205
    fprintf(ofp, "# maximum number of records of the priority queue\n");
 
206
    fprintf(ofp, "queuesize: 50000\n");
 
207
    fprintf(ofp, "\n");
 
208
    fprintf(ofp, "# regular expressions and replacement strings to normalize URLs\n");
 
209
    fprintf(ofp, "replace: ^http://127.0.0.1/{{!}}http://localhost/\n");
 
210
    fprintf(ofp, "\n");
 
211
    fprintf(ofp, "# allowing regular expressions of URLs to be visited\n");
 
212
    fprintf(ofp, "allowrx: ^http://\n");
 
213
    fprintf(ofp, "\n");
 
214
    fprintf(ofp, "# denying regular expressions of URLs to be visited\n");
 
215
    fprintf(ofp, "denyrx: \\.(css|js|csv|tsv|log|md5|crc|conf|ini|inf|lnk|sys|tmp|bak)$\n");
 
216
    fprintf(ofp, "denyrx: \\.(xml|xsl|xslt|rdf|rss|dtd|sgml|sgm)$\n");
 
217
    fprintf(ofp, "denyrx: \\.(pgp|sig|cer|csr|pem|key|b64|uu|uue|[0-9])$\n");
 
218
    fprintf(ofp, "denyrx: \\.(rtf|pdf|ps|eps|ai|doc|xls|ppt|sxw|sxc|sxi|xdw|jtd|oas|swf)$\n");
 
219
    fprintf(ofp, "denyrx: \\.(zip|tar|tgz|gz|bz2|tbz2|z|lha|lzh)(\\?.*)?$\n");
 
220
    fprintf(ofp, "denyrx: \\.(7z|lzo|lzma|cpio|shar|cab|rar|sit|ace|hqx)(\\?.*)?$\n");
 
221
    fprintf(ofp, "denyrx: \\.(bin|o|a|so|exe|dll|lib|obj|ocx|class|jar|war)(\\?.*)?$\n");
 
222
    fprintf(ofp, "denyrx: \\.(rpm|deb|qdb|qdb|dbx|dbf|dat|msi|bat|com|iso)(\\?.*)?$\n");
 
223
    fprintf(ofp, "denyrx: \\.(png|gif|jpg|jpeg|tif|tiff|bmp|ico|pbm|pgm|ppm|xbm|xpm|dvi)$\n");
 
224
    fprintf(ofp, "denyrx: \\.(au|snd|mid|midi|kar|smf|mp2|mp3|m3u|wav|wma|wmp|asx|at3|aif)$\n");
 
225
    fprintf(ofp, "denyrx: \\.(mpg|mpeg|qt|mov|avi|wmv|wvx|asf|ram|rm)$\n");
 
226
    fprintf(ofp, "denyrx: (/core$|/core\\.[0-9]*$|/casket/)\n");
 
227
    fprintf(ofp, "denyrx: ://(localhost|[a-z]*\\.localdomain|127\\.0\\.0\\.1)/\n");
 
228
    fprintf(ofp, "\n");
 
229
    fprintf(ofp, "# denying regular expressions of URLs to be indexed\n");
 
230
    fprintf(ofp, "noidxrx: /\\?[a-z]=[a-z](;|$)\n");
 
231
    fprintf(ofp, "\n");
 
232
    fprintf(ofp, "# URL rules (regular expressions and media types)\n");
 
233
    fprintf(ofp, "urlrule: \\.est${{!}}text/x-estraier-draft\n");
 
234
    fprintf(ofp, "urlrule: \\.(eml|mime|mht|mhtml)${{!}}message/rfc822\n");
 
235
    fprintf(ofp, "\n");
 
236
    fprintf(ofp, "# media type rules (regular expressions and filter commands)\n");
 
237
    fprintf(ofp, "typerule: ^text/x-estraier-draft${{!}}%s\n", DRAFTCMD);
 
238
    fprintf(ofp, "typerule: ^text/plain${{!}}%s\n", TEXTCMD);
 
239
    fprintf(ofp, "typerule: ^(text/html|application/xhtml+xml)${{!}}%s\n", HTMLCMD);
 
240
    fprintf(ofp, "typerule: ^message/rfc822${{!}}%s\n", MIMECMD);
 
241
    fprintf(ofp, "\n");
 
242
    fprintf(ofp, "# preferred language (0:English, 1:Japanese, 2:Chinese, 3:Korean, 4:misc)\n");
 
243
    fprintf(ofp, "language: 0\n");
 
244
    fprintf(ofp, "\n");
 
245
    fprintf(ofp, "# text size limitation (in kilobytes)\n");
 
246
    fprintf(ofp, "textlimit: 128\n");
 
247
    fprintf(ofp, "\n");
 
248
    fprintf(ofp, "# total number of keywords for seed documents\n");
 
249
    fprintf(ofp, "seedkeynum: 256\n");
 
250
    fprintf(ofp, "\n");
 
251
    fprintf(ofp, "# number of keywords saved for each document\n");
 
252
    fprintf(ofp, "savekeynum: 32\n");
 
253
    fprintf(ofp, "\n");
 
254
    fprintf(ofp, "# number of threads running in parallel\n");
 
255
    fprintf(ofp, "threadnum: 10\n");
 
256
    fprintf(ofp, "\n");
 
257
    fprintf(ofp, "# number of documents to collect\n");
 
258
    fprintf(ofp, "docnum: 10000\n");
 
259
    fprintf(ofp, "\n");
 
260
    fprintf(ofp, "# running time period (in s:seconds, m:minutes, h:hours, d:days)\n");
 
261
    fprintf(ofp, "period: 10000s\n");
 
262
    fprintf(ofp, "\n");
 
263
    fprintf(ofp, "# revisit span (in s:seconds, m:minutes, h:hours, d:days)\n");
 
264
    fprintf(ofp, "revisit: 7d\n");
 
265
    fprintf(ofp, "\n");
 
266
    fprintf(ofp, "# maximum size of the index cache (in megabytes)\n");
 
267
    fprintf(ofp, "cachesize: 256\n");
 
268
    fprintf(ofp, "\n");
 
269
    fprintf(ofp, "# remote nodes for alternative indexes (ID number and URL)\n");
 
270
    fprintf(ofp, "#nodeserv: 1|http://admin:admin@localhost:1978/node/node1\n");
 
271
    fprintf(ofp, "#nodeserv: 2|http://admin:admin@localhost:1978/node/node2\n");
 
272
    fprintf(ofp, "#nodeserv: 3|http://admin:admin@localhost:1978/node/node3\n");
 
273
    fprintf(ofp, "\n");
 
274
    fprintf(ofp, "# path of the log file (relative path or absolute path)\n");
 
275
    fprintf(ofp, "logfile: %s\n", LOGFILE);
 
276
    fprintf(ofp, "\n");
 
277
    fprintf(ofp, "# logging level (1:debug, 2:information, 3:warning, 4:error, 5:none)\n");
 
278
    fprintf(ofp, "loglevel: 2\n");
 
279
    fprintf(ofp, "\n");
 
280
    fprintf(ofp, "# path of the draft directory (relative path or absolute path)\n");
 
281
    fprintf(ofp, "draftdir:\n");
 
282
    fprintf(ofp, "\n");
 
283
    fprintf(ofp, "# path of the entity directory (relative path or absolute path)\n");
 
284
    fprintf(ofp, "entitydir:\n");
 
285
    fprintf(ofp, "\n");
 
286
    fprintf(ofp, "# postprocessor for retrieved files\n");
 
287
    fprintf(ofp, "postproc:\n");
 
288
    fprintf(ofp, "\n");
 
289
    if(fclose(ofp) == EOF) err = TRUE;
 
290
  } else {
 
291
    err = TRUE;
 
292
  }
 
293
  tracebnum = TRACEBNUM;
 
294
  estopts = 0;
 
295
  if(options & WI_SMALL){
 
296
    tracebnum = TRACEBNUM / 2;
 
297
    estopts = ESTDBSMALL;
 
298
  } else if(options & WI_LARGE){
 
299
    tracebnum = TRACEBNUM * 2;
 
300
    estopts = ESTDBLARGE;
 
301
  } else if(options & WI_HUGE){
 
302
    tracebnum = TRACEBNUM * 4;
 
303
    estopts = ESTDBHUGE;
 
304
  }
 
305
  sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, QUEUEFILE);
 
306
  unlink(path);
 
307
  if((queue = queue_open(path)) != NULL){
 
308
    if(!queue_close(queue)) err = TRUE;
 
309
  } else {
 
310
    err = TRUE;
 
311
  }
 
312
  sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, TRACEFILE);
 
313
  if((trace = cropen(path, CR_OWRITER | CR_OCREAT | CR_OTRUNC, tracebnum, TRACEDNUM)) != NULL){
 
314
    if(!crclose(trace)) err = TRUE;
 
315
  } else {
 
316
    err = TRUE;
 
317
  }
 
318
  sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, INDEXDIR);
 
319
  if((index = est_mtdb_open(path, ESTDBWRITER | ESTDBCREAT | ESTDBTRUNC | estopts,
 
320
                            &ecode)) != NULL){
 
321
    if(!est_mtdb_add_attr_index(index, ESTDATTRURI, ESTIDXATTRSTR)) err = TRUE;
 
322
    if(!est_mtdb_add_attr_index(index, ESTDATTRTITLE, ESTIDXATTRSTR)) err = TRUE;
 
323
    if(!est_mtdb_add_attr_index(index, ESTDATTRMDATE, ESTIDXATTRSEQ)) err = TRUE;
 
324
    if(!est_mtdb_close(index, &ecode)) err = TRUE;
 
325
  } else {
 
326
    err = TRUE;
 
327
  }
 
328
  sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, MYTMPDIR);
 
329
  est_mkdir(path);
 
330
  sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, LOGFILE);
 
331
  if((ofp = fopen(path, "wb")) != NULL){
 
332
    if(fclose(ofp) == EOF) err = TRUE;
 
333
  } else {
 
334
    err = TRUE;
 
335
  }
 
336
  return err ? FALSE : TRUE;
 
337
}
 
338
 
 
339
 
 
340
/* Open a waver handle. */
 
341
WAVER *waver_open(const char *rootdir){
 
342
  WAVER *waver;
 
343
  UNRULE unrule;
 
344
  PMRULE pmrule;
 
345
  URLRULE urlrule;
 
346
  MTRULE mtrule;
 
347
  DEPOT *metadb;
 
348
  CURIA *trace;
 
349
  QUEUE *queue;
 
350
  ESTMTDB *index;
 
351
  ESTNODE *node;
 
352
  CBLIST *lines;
 
353
  const char *rp, *pv, *logfile;
 
354
  char path[URIBUFSIZ], *tmp;
 
355
  int i, ecode, loglevel, num;
 
356
  struct stat sbuf;
 
357
  assert(rootdir);
 
358
  if(stat(rootdir, &sbuf) == -1) return NULL;
 
359
  sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, CONFFILE);
 
360
  lines = cbreadlines(path);
 
361
  sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, METAFILE);
 
362
  metadb = dpopen(path, DP_OWRITER, -1);
 
363
  sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, QUEUEFILE);
 
364
  queue = queue_open(path);
 
365
  sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, TRACEFILE);
 
366
  trace = cropen(path, CR_OWRITER, -1, -1);
 
367
  sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, INDEXDIR);
 
368
  index = est_mtdb_open(path, ESTDBWRITER, &ecode);
 
369
  if(!lines || !metadb || !queue || !trace || !index){
 
370
    if(index) est_mtdb_close(index, &ecode);
 
371
    if(trace) crclose(trace);
 
372
    if(queue) queue_close(queue);
 
373
    if(metadb) dpclose(metadb);
 
374
    if(lines) cblistclose(lines);
 
375
    return NULL;
 
376
  }
 
377
  waver = cbmalloc(sizeof(WAVER));
 
378
  waver->rootdir = cbmemdup(rootdir, -1);
 
379
  waver->metadb = metadb;
 
380
  waver->queue = queue;
 
381
  waver->trace = trace;
 
382
  waver->index = index;
 
383
  waver->seeds = cbmapopen();
 
384
  waver->kwords = cbmapopen();
 
385
  waver->sites = cbmapopen();
 
386
  waver->pxhost = NULL;
 
387
  waver->pxport = 80;
 
388
  waver->timeout = -1;
 
389
  waver->interval = 0;
 
390
  waver->strategy = CS_BALANCED;
 
391
  waver->inherit = 0.0;
 
392
  waver->seeddepth = 0;
 
393
  waver->maxdepth = INT_MAX;
 
394
  waver->masscheck = INT_MAX;
 
395
  waver->queuesize = INT_MAX;
 
396
  waver->unrules = cblistopen();
 
397
  waver->pmrules = cblistopen();
 
398
  waver->urlrules = cblistopen();
 
399
  waver->mtrules = cblistopen();
 
400
  waver->language = ESTLANGEN;
 
401
  waver->textlimit = 0;
 
402
  waver->seedkeynum = 0;
 
403
  waver->savekeynum = 0;
 
404
  waver->thnum = 1;
 
405
  waver->docnum = 0;
 
406
  waver->period = 0;
 
407
  waver->revisit = 0;
 
408
  waver->cachesize = 0;
 
409
  waver->nodes = cbmapopenex(MINIBNUM);
 
410
  waver->draftdir = NULL;
 
411
  waver->entitydir = NULL;
 
412
  waver->postproc = NULL;
 
413
  waver->stime = time(NULL);
 
414
  waver->curnum = 0;
 
415
  waver->curnode = 0;
 
416
  waver->minload = 1.0;
 
417
  logfile = LOGFILE;
 
418
  loglevel = LL_INFO;
 
419
  for(i = 0; i < cblistnum(lines); i++){
 
420
    rp = cblistval(lines, i, NULL);
 
421
    if(cbstrfwimatch(rp, "seed:")){
 
422
      rp = skiplabel(rp);
 
423
      if((pv = strchr(rp, '|')) != NULL){
 
424
        tmp = cburlresolve(pv + 1, "");
 
425
        cbmapput(waver->seeds, tmp, -1, rp, pv - rp, FALSE);
 
426
        free(tmp);
 
427
      }
 
428
    } else if(cbstrfwimatch(rp, "proxyhost:")){
 
429
      rp = skiplabel(rp);
 
430
      if(rp[0] != '\0') waver->pxhost = cbmemdup(rp, -1);
 
431
    } else if(cbstrfwimatch(rp, "proxyport:")){
 
432
      rp = skiplabel(rp);
 
433
      if(rp[0] != '\0') waver->pxport = atoi(rp);
 
434
    } else if(cbstrfwimatch(rp, "interval:")){
 
435
      waver->interval = atoi(skiplabel(rp));
 
436
    } else if(cbstrfwimatch(rp, "timeout:")){
 
437
      waver->timeout = atoi(skiplabel(rp));
 
438
    } else if(cbstrfwimatch(rp, "strategy:")){
 
439
      waver->strategy = atoi(skiplabel(rp));
 
440
    } else if(cbstrfwimatch(rp, "inherit:")){
 
441
      waver->inherit = strtod(skiplabel(rp), NULL);
 
442
    } else if(cbstrfwimatch(rp, "seeddepth:")){
 
443
      waver->seeddepth = atoi(skiplabel(rp));
 
444
    } else if(cbstrfwimatch(rp, "maxdepth:")){
 
445
      waver->maxdepth = atoi(skiplabel(rp));
 
446
    } else if(cbstrfwimatch(rp, "masscheck:")){
 
447
      waver->masscheck = atoi(skiplabel(rp));
 
448
    } else if(cbstrfwimatch(rp, "queuesize:")){
 
449
      waver->queuesize = atoi(skiplabel(rp));
 
450
    } else if(cbstrfwimatch(rp, "replace:")){
 
451
      rp = skiplabel(rp);
 
452
      if((pv = strstr(rp, "{{!}}")) != NULL){
 
453
        tmp = cbmemdup(rp, pv - rp);
 
454
        if((unrule.regex = est_regex_new(tmp)) != NULL){
 
455
          unrule.before = tmp;
 
456
          unrule.after = cbmemdup(pv + 5, -1);
 
457
          cblistpush(waver->unrules, (char *)&unrule, sizeof(UNRULE));
 
458
        } else {
 
459
          free(tmp);
 
460
        }
 
461
      }
 
462
    } else if(cbstrfwimatch(rp, "allowrx:")){
 
463
      tmp = cbsprintf("*I:%s", skiplabel(rp));
 
464
      if((pmrule.regex = est_regex_new(tmp)) != NULL){
 
465
        pmrule.visit = 1;
 
466
        pmrule.index = 1;
 
467
        cblistpush(waver->pmrules, (char *)&pmrule, sizeof(PMRULE));
 
468
      }
 
469
      free(tmp);
 
470
    } else if(cbstrfwimatch(rp, "denyrx:")){
 
471
      tmp = cbsprintf("*I:%s", skiplabel(rp));
 
472
      if((pmrule.regex = est_regex_new(tmp)) != NULL){
 
473
        pmrule.visit = -1;
 
474
        pmrule.index = 0;
 
475
        cblistpush(waver->pmrules, (char *)&pmrule, sizeof(PMRULE));
 
476
      }
 
477
      free(tmp);
 
478
    } else if(cbstrfwimatch(rp, "noidxrx:")){
 
479
      tmp = cbsprintf("*I:%s", skiplabel(rp));
 
480
      if((pmrule.regex = est_regex_new(tmp)) != NULL){
 
481
        pmrule.visit = 0;
 
482
        pmrule.index = -1;
 
483
        cblistpush(waver->pmrules, (char *)&pmrule, sizeof(PMRULE));
 
484
      }
 
485
      free(tmp);
 
486
    } else if(cbstrfwimatch(rp, "urlrule:")){
 
487
      rp = skiplabel(rp);
 
488
      if((pv = strstr(rp, "{{!}}")) != NULL){
 
489
        tmp = cbmemdup(rp, pv - rp);
 
490
        if((urlrule.regex = est_regex_new(tmp)) != NULL){
 
491
          urlrule.type = cbmemdup(pv + 5, -1);
 
492
          cblistpush(waver->urlrules, (char *)&urlrule, sizeof(URLRULE));
 
493
        }
 
494
        free(tmp);
 
495
      }
 
496
    } else if(cbstrfwimatch(rp, "typerule:")){
 
497
      rp = skiplabel(rp);
 
498
      if((pv = strstr(rp, "{{!}}")) != NULL){
 
499
        tmp = cbmemdup(rp, pv - rp);
 
500
        if((mtrule.regex = est_regex_new(tmp)) != NULL){
 
501
          mtrule.filter = cbmemdup(pv + 5, -1);
 
502
          cblistpush(waver->mtrules, (char *)&mtrule, sizeof(MTRULE));
 
503
        }
 
504
        free(tmp);
 
505
      }
 
506
    } else if(cbstrfwimatch(rp, "language:")){
 
507
      waver->language = atoi(skiplabel(rp));
 
508
    } else if(cbstrfwimatch(rp, "textlimit:")){
 
509
      waver->textlimit = atoi(skiplabel(rp)) * 1024;
 
510
    } else if(cbstrfwimatch(rp, "seedkeynum:")){
 
511
      waver->seedkeynum = atoi(skiplabel(rp));
 
512
    } else if(cbstrfwimatch(rp, "savekeynum:")){
 
513
      waver->savekeynum = atoi(skiplabel(rp));
 
514
    } else if(cbstrfwimatch(rp, "threadnum:")){
 
515
      waver->thnum = atoi(skiplabel(rp));
 
516
    } else if(cbstrfwimatch(rp, "docnum:")){
 
517
      waver->docnum = atoi(skiplabel(rp));
 
518
    } else if(cbstrfwimatch(rp, "period:")){
 
519
      rp = skiplabel(rp);
 
520
      waver->period = atoi(rp);
 
521
      if(cbstrbwimatch(rp, "m")){
 
522
        waver->period *= 60;
 
523
      } else if(cbstrbwimatch(rp, "h")){
 
524
        waver->period *= 3600;
 
525
      } else if(cbstrbwimatch(rp, "d")){
 
526
        waver->period *= 86400;
 
527
      }
 
528
    } else if(cbstrfwimatch(rp, "revisit:")){
 
529
      rp = skiplabel(rp);
 
530
      waver->revisit = atoi(rp);
 
531
      if(cbstrbwimatch(rp, "m")){
 
532
        waver->revisit *= 60;
 
533
      } else if(cbstrbwimatch(rp, "h")){
 
534
        waver->revisit *= 3600;
 
535
      } else if(cbstrbwimatch(rp, "d")){
 
536
        waver->revisit *= 86400;
 
537
      }
 
538
    } else if(cbstrfwimatch(rp, "cachesize:")){
 
539
      waver->cachesize = atoi(skiplabel(rp)) * 1024 * 1024;
 
540
    } else if(cbstrfwimatch(rp, "nodeserv:")){
 
541
      rp = skiplabel(rp);
 
542
      if((pv = strchr(rp, '|')) != NULL){
 
543
        pv++;
 
544
        num = atoi(rp);
 
545
        if(num > 0 && !cbmapget(waver->nodes, (char *)&num, sizeof(int), NULL)){
 
546
          node = est_node_new(pv);
 
547
          cbmapput(waver->nodes, (char *)&num, sizeof(int),
 
548
                   (char *)&node, sizeof(ESTNODE *), FALSE);
 
549
        }
 
550
      }
 
551
    } else if(cbstrfwimatch(rp, "logfile:")){
 
552
      logfile = skiplabel(rp);
 
553
    } else if(cbstrfwimatch(rp, "loglevel:")){
 
554
      loglevel = atoi(skiplabel(rp));
 
555
    } else if(cbstrfwimatch(rp, "draftdir:")){
 
556
      rp = skiplabel(rp);
 
557
      if(rp[0] != '\0' && !waver->draftdir) waver->draftdir = makeabspath(rootdir, rp);
 
558
    } else if(cbstrfwimatch(rp, "entitydir:")){
 
559
      rp = skiplabel(rp);
 
560
      if(rp[0] != '\0' && !waver->entitydir) waver->entitydir = makeabspath(rootdir, rp);
 
561
    } else if(cbstrfwimatch(rp, "postproc:")){
 
562
      rp = skiplabel(rp);
 
563
      if(rp[0] != '\0' && !waver->postproc) waver->postproc = cbmemdup(rp, -1);
 
564
    }
 
565
  }
 
566
  if(!log_open(rootdir, logfile, loglevel, FALSE)){
 
567
    cblistclose(lines);
 
568
    waver_close(waver);
 
569
    return NULL;
 
570
  }
 
571
  if(waver->pxport < 1) waver->pxport = 80;
 
572
  if(waver->seeddepth < 0) waver->seeddepth = 0;
 
573
  if(waver->maxdepth < 0) waver->maxdepth = 0;
 
574
  if(waver->masscheck < 1) waver->masscheck = 1;
 
575
  if(waver->queuesize < 1) waver->queuesize = 1;
 
576
  if(waver->textlimit < 0) waver->textlimit = 0;
 
577
  if(waver->seedkeynum < 0) waver->seedkeynum = 0;
 
578
  if(waver->savekeynum < 0) waver->savekeynum = 0;
 
579
  if(waver->thnum < 1) waver->thnum = 1;
 
580
  if(waver->period < 1) waver->period = 1;
 
581
  if(waver->revisit < 1) waver->revisit = 1;
 
582
  if(waver->cachesize < 1) waver->cachesize = 1;
 
583
  cblistclose(lines);
 
584
  est_mtdb_set_informer(index, db_informer, NULL);
 
585
  est_mtdb_set_cache_size(index, waver->cachesize, -1, -1, -1);
 
586
  return waver;
 
587
}
 
588
 
 
589
 
 
590
/* Close a waver handle. */
 
591
int waver_close(WAVER *waver){
 
592
  UNRULE *unrule;
 
593
  PMRULE *pmrule;
 
594
  URLRULE *urlrule;
 
595
  MTRULE *mtrule;
 
596
  ESTNODE *node;
 
597
  const char *kbuf;
 
598
  int i, err, ecode;
 
599
  assert(waver);
 
600
  err = FALSE;
 
601
  free(waver->postproc);
 
602
  free(waver->entitydir);
 
603
  free(waver->draftdir);
 
604
  cbmapiterinit(waver->nodes);
 
605
  while((kbuf = cbmapiternext(waver->nodes, NULL)) != NULL){
 
606
    node = *(ESTNODE **)cbmapget(waver->nodes, kbuf, sizeof(int), NULL);
 
607
    est_node_delete(node);
 
608
  }
 
609
  cbmapclose(waver->nodes);
 
610
  for(i = 0; i < cblistnum(waver->mtrules); i++){
 
611
    mtrule = (MTRULE *)cblistval(waver->mtrules, i, NULL);
 
612
    est_regex_delete(mtrule->regex);
 
613
    free(mtrule->filter);
 
614
  }
 
615
  cblistclose(waver->mtrules);
 
616
  for(i = 0; i < cblistnum(waver->urlrules); i++){
 
617
    urlrule = (URLRULE *)cblistval(waver->urlrules, i, NULL);
 
618
    est_regex_delete(urlrule->regex);
 
619
    free(urlrule->type);
 
620
  }
 
621
  cblistclose(waver->urlrules);
 
622
  for(i = 0; i < cblistnum(waver->pmrules); i++){
 
623
    pmrule = (PMRULE *)cblistval(waver->pmrules, i, NULL);
 
624
    est_regex_delete(pmrule->regex);
 
625
  }
 
626
  cblistclose(waver->pmrules);
 
627
  for(i = 0; i < cblistnum(waver->unrules); i++){
 
628
    unrule = (UNRULE *)cblistval(waver->unrules, i, NULL);
 
629
    est_regex_delete(unrule->regex);
 
630
    free(unrule->before);
 
631
    free(unrule->after);
 
632
  }
 
633
  cblistclose(waver->unrules);
 
634
  free(waver->pxhost);
 
635
  cbmapclose(waver->sites);
 
636
  cbmapclose(waver->kwords);
 
637
  cbmapclose(waver->seeds);
 
638
  if(!est_mtdb_close(waver->index, &ecode)) err = TRUE;
 
639
  if(!crclose(waver->trace)) err = TRUE;
 
640
  if(!queue_close(waver->queue)) err = TRUE;
 
641
  if(!dpclose(waver->metadb)) err = TRUE;
 
642
  free(waver->rootdir);
 
643
  free(waver);
 
644
  return err ? FALSE : TRUE;
 
645
}
 
646
 
 
647
 
 
648
/* Set the current node. */
 
649
void waver_set_current_node(WAVER *waver){
 
650
  ESTNODE *node;
 
651
  const char *kbuf;
 
652
  int i;
 
653
  double ratio;
 
654
  assert(waver);
 
655
  waver->curnode = 0;
 
656
  cbmapiterinit(waver->nodes);
 
657
  waver->minload = 1.0;
 
658
  for(i = 0; i < 600; i++){
 
659
    while((kbuf = cbmapiternext(waver->nodes, NULL)) != NULL){
 
660
      node = *(ESTNODE **)cbmapget(waver->nodes, kbuf, sizeof(int), NULL);
 
661
      if((ratio = est_node_cache_usage(node)) >= 0.0 && ratio <= waver->minload){
 
662
        waver->curnode = *(int *)kbuf;
 
663
        waver->minload = ratio;
 
664
      }
 
665
    }
 
666
    if(waver->minload < 1.0){
 
667
      cbmapmove(waver->nodes, (char *)&(waver->curnode), sizeof(int), TRUE);
 
668
      break;
 
669
    }
 
670
    est_usleep(1000 * 1000);
 
671
  }
 
672
}
 
673
 
 
674
 
 
675
/* Get the load of the current node. */
 
676
double waver_current_node_load(WAVER *waver){
 
677
  ESTNODE *node;
 
678
  double ratio;
 
679
  if(!(node = *(ESTNODE **)cbmapget(waver->nodes, (char *)&(waver->curnode), sizeof(int), NULL)))
 
680
    return 1.0;
 
681
  ratio = est_node_cache_usage(node);
 
682
  return ratio > 0.0 ? ratio : 1.0;
 
683
}
 
684
 
 
685
 
 
686
/* Add a document to a node. */
 
687
int waver_node_put_doc(WAVER *waver, ESTDOC *doc, int *codep){
 
688
  ESTNODE *node;
 
689
  int rv;
 
690
  assert(waver && doc);
 
691
  if(!(node = *(ESTNODE **)cbmapget(waver->nodes, (char *)&(waver->curnode), sizeof(int), NULL))){
 
692
    if(codep) *codep = -1;
 
693
    return FALSE;
 
694
  }
 
695
  rv = est_node_put_doc(node, doc);
 
696
  if(codep) *codep = est_node_status(node);
 
697
  return rv;
 
698
}
 
699
 
 
700
 
 
701
/* Remove a document from a node. */
 
702
int waver_node_out_doc(WAVER *waver, const char *url, int *codep){
 
703
  ESTNODE *node;
 
704
  const char *rp;
 
705
  char *vbuf;
 
706
  int nid, rv;
 
707
  assert(waver && url);
 
708
  nid = 0;
 
709
  if((vbuf = crget(waver->trace, url, -1, 0, -1, NULL)) != NULL){
 
710
    if((rp = strchr(vbuf, '#')) != NULL) nid = atoi(rp + 1);
 
711
    free(vbuf);
 
712
  }
 
713
  if(nid < 1 || !(node = *(ESTNODE **)cbmapget(waver->nodes, (char *)&nid, sizeof(int), NULL))){
 
714
    if(codep) *codep = -1;
 
715
    return FALSE;
 
716
  }
 
717
  rv = est_node_out_doc_by_uri(node, url);
 
718
  if(codep) *codep = est_node_status(node);
 
719
  return rv;
 
720
}
 
721
 
 
722
 
 
723
/* Open a priority queue. */
 
724
QUEUE *queue_open(const char *name){
 
725
  QUEUE *queue;
 
726
  VILLA *db;
 
727
  const char *vbuf;
 
728
  int vomode, vsiz;
 
729
  double max;
 
730
  assert(name);
 
731
  vomode = VL_OWRITER | VL_OCREAT;
 
732
  if(ESTUSEBZIP){
 
733
    vomode |= VL_OXCOMP;
 
734
  } else if(ESTUSELZO){
 
735
    vomode |= VL_OYCOMP;
 
736
  } else if(ESTUSEZLIB){
 
737
    vomode |= VL_OZCOMP;
 
738
  }
 
739
  if(!(db = vlopen(name, vomode, queue_compare))) return NULL;
 
740
  vlsettuning(db, QUEUELRM, QUEUENIM, QUEUELCN, QUEUENCN);
 
741
  max = 1.0;
 
742
  vlcurlast(db);
 
743
  if((vbuf = vlcurkeycache(db, &vsiz)) != NULL && vsiz == sizeof(double))
 
744
    max = *(double *)vbuf;
 
745
  if(max < 1.0) max = 1.0;
 
746
  queue = cbmalloc(sizeof(QUEUE));
 
747
  queue->db = db;
 
748
  queue->max = max;
 
749
  return queue;
 
750
}
 
751
 
 
752
 
 
753
/* Close a priority queue. */
 
754
int queue_close(QUEUE *queue){
 
755
  int err;
 
756
  assert(queue);
 
757
  err = FALSE;
 
758
  if(!vlclose(queue->db)) err = TRUE;
 
759
  free(queue);
 
760
  return err ? FALSE : TRUE;
 
761
}
 
762
 
 
763
 
 
764
/* Set the range of the priority space of a priority queue. */
 
765
void queue_set_range(QUEUE *queue, double range){
 
766
  assert(queue);
 
767
  queue->max = range;
 
768
  if(queue->max < 1.0) queue->max = 1.0;
 
769
}
 
770
 
 
771
 
 
772
/* Enqueue a record into a priority queue. */
 
773
int queue_enqueue(QUEUE *queue, const char *str, double priority){
 
774
  int err;
 
775
  assert(queue && str);
 
776
  if(priority < 0.0) priority = 0.0;
 
777
  if(priority > 1.0) priority = 1.0;
 
778
  priority *= queue->max;
 
779
  err = FALSE;
 
780
  if(!vlput(queue->db, (char *)&priority, sizeof(double), str, -1, VL_DDUP)) err = TRUE;
 
781
  return err ? FALSE : TRUE;
 
782
}
 
783
 
 
784
 
 
785
/* Dequeue a record from a priority queue. */
 
786
char *queue_dequeue(QUEUE *queue){
 
787
  char *vbuf;
 
788
  assert(queue);
 
789
  vlcurfirst(queue->db);
 
790
  if(!(vbuf = vlcurval(queue->db, NULL))) return NULL;
 
791
  vlcurout(queue->db);
 
792
  return vbuf;
 
793
}
 
794
 
 
795
 
 
796
/* Get the number of records in a priority queue. */
 
797
int queue_rnum(QUEUE *queue){
 
798
  assert(queue);
 
799
  return vlrnum(queue->db);
 
800
}
 
801
 
 
802
 
 
803
/* Discard inferior records in a priority queue. */
 
804
int queue_slim(QUEUE *queue, int num){
 
805
  int i, diff;
 
806
  assert(queue && num >= 0);
 
807
  if((diff = vlrnum(queue->db) - num) < 1) return TRUE;
 
808
  vlcurlast(queue->db);
 
809
  for(i = 1; i < diff; i++){
 
810
    vlcurprev(queue->db);
 
811
  }
 
812
  while(TRUE){
 
813
    if(!vlcurout(queue->db)) break;
 
814
  }
 
815
  return vlrnum(queue->db) == num && !vlfatalerror(queue->db);
 
816
}
 
817
 
 
818
 
 
819
/* Add a word to a keyword map. */
 
820
void kwords_add(CBMAP *kwords, const char *word, int frequency){
 
821
  const char *vbuf;
 
822
  char numbuf[NUMBUFSIZ];
 
823
  int wlen, nlen;
 
824
  assert(kwords && word && frequency >= 0);
 
825
  wlen = strlen(word);
 
826
  if((vbuf = cbmapget(kwords, word, wlen, NULL)) != NULL) frequency += atoi(vbuf);
 
827
  nlen = sprintf(numbuf, "%d", frequency);
 
828
  cbmapput(kwords, word, wlen, numbuf, nlen, TRUE);
 
829
}
 
830
 
 
831
 
 
832
/* Reduce elements of a keyword map. */
 
833
void kwords_reduce(CBMAP *kwords, int num, int fadeout){
 
834
  KEYSC *scores;
 
835
  const char *vbuf;
 
836
  char numbuf[NUMBUFSIZ];
 
837
  int i, snum, vsiz;
 
838
  double basis;
 
839
  assert(kwords && num >= 0);
 
840
  if(num < 1) num = 1;
 
841
  snum = cbmaprnum(kwords);
 
842
  scores = cbmalloc(snum * sizeof(KEYSC) + 1);
 
843
  cbmapiterinit(kwords);
 
844
  for(i = 0; i < snum; i++){
 
845
    vbuf = cbmapiternext(kwords, &vsiz);
 
846
    scores[i].word = vbuf;
 
847
    scores[i].wsiz = vsiz;
 
848
    scores[i].pt = atoi(cbmapget(kwords, vbuf, vsiz, NULL));
 
849
  }
 
850
  qsort(scores, snum, sizeof(KEYSC), keysc_compare);
 
851
  basis = num * 1.1 + 1.0;
 
852
  for(i = 0; i < snum; i++){
 
853
    if(i < num){
 
854
      vsiz = sprintf(numbuf, "%d",
 
855
                     fadeout ? (int)(scores[i].pt * (basis - i) / basis) : scores[i].pt);
 
856
      cbmapput(kwords, scores[i].word, scores[i].wsiz, numbuf, vsiz, TRUE);
 
857
      cbmapmove(kwords, scores[i].word, scores[i].wsiz, FALSE);
 
858
    } else {
 
859
      cbmapout(kwords, scores[i].word, scores[i].wsiz);
 
860
    }
 
861
  }
 
862
  free(scores);
 
863
}
 
864
 
 
865
 
 
866
/* Fetch a document of a URL. */
 
867
int fetch_document(const char *url, const char *pxhost, int pxport, int outsec, time_t mdate,
 
868
                   const CBLIST *urlrules, const CBLIST *mtrules,
 
869
                   int *codep, CBDATUM *raw, CBMAP *heads,
 
870
                   CBLIST *links, const CBLIST *unrules, ESTDOC *doc, int lang){
 
871
  URLRULE *urlrule;
 
872
  MTRULE *mtrule;
 
873
  UNRULE *unrule;
 
874
  ESTDOC *rdoc;
 
875
  CBMAP *rheads;
 
876
  CBLIST *reqheads;
 
877
  CBDATUM *rbuf;
 
878
  const char *vbuf, *cmd;
 
879
  char *dstr, *tbuf, *type, *enc, *pv;
 
880
  int i, j, rescode;
 
881
  assert(url);
 
882
  rescode = -1;
 
883
  if(raw){
 
884
    rbuf = NULL;
 
885
  } else {
 
886
    rbuf = cbdatumopen(NULL, -1);
 
887
    raw = rbuf;
 
888
  }
 
889
  if(heads){
 
890
    rheads = NULL;
 
891
  } else {
 
892
    rheads = cbmapopenex(MINIBNUM);
 
893
    heads = rheads;
 
894
  }
 
895
  if(doc){
 
896
    rdoc = NULL;
 
897
  } else {
 
898
    rdoc = est_doc_new();
 
899
    doc = rdoc;
 
900
  }
 
901
  reqheads = cblistopen();
 
902
  if(mdate > 0){
 
903
    dstr = cbdatestrhttp(mdate, 0);
 
904
    tbuf = cbsprintf("If-Modified-Since: %s", dstr);
 
905
    cblistpush(reqheads, tbuf, -1);
 
906
    free(tbuf);
 
907
    free(dstr);
 
908
  }
 
909
  switch(lang){
 
910
  case ESTLANGEN:
 
911
    cblistpush(reqheads, "Accept-Language: en,ja", -1);
 
912
    break;
 
913
  case ESTLANGJA:
 
914
    cblistpush(reqheads, "Accept-Language: ja,en", -1);
 
915
    break;
 
916
  case ESTLANGZH:
 
917
    cblistpush(reqheads, "Accept-Language: zh,en", -1);
 
918
    break;
 
919
  case ESTLANGKO:
 
920
    cblistpush(reqheads, "Accept-Language: ko,en", -1);
 
921
    break;
 
922
  }
 
923
  if(!est_url_shuttle(url, pxhost, pxport, outsec, RESLIMSIZE, NULL, reqheads, NULL, 0,
 
924
                      &rescode, heads, raw) || rescode != 200){
 
925
    if(links && (rescode == 301 || rescode == 302) &&
 
926
       (vbuf = cbmapget(heads, "location", -1, NULL)) != NULL){
 
927
      tbuf = cburlresolve(url, vbuf);
 
928
      cblistpush(links, tbuf, -1);
 
929
      free(tbuf);
 
930
    }
 
931
    cblistclose(reqheads);
 
932
    if(rdoc) est_doc_delete(rdoc);
 
933
    if(rheads) cbmapclose(rheads);
 
934
    if(rbuf) cbdatumclose(rbuf);
 
935
    if(codep) *codep = rescode;
 
936
    return FALSE;
 
937
  }
 
938
  if(urlrules){
 
939
    for(i = 0; i < cblistnum(urlrules); i++){
 
940
      urlrule = (URLRULE *)cblistval(urlrules, i, NULL);
 
941
      if(est_regex_match(urlrule->regex, url)){
 
942
        cbmapput(heads, "content-type", -1, urlrule->type, -1, TRUE);
 
943
        break;
 
944
      }
 
945
    }
 
946
  }
 
947
  if(!(vbuf = cbmapget(heads, "content-type", -1, NULL))) vbuf = "text/plain";
 
948
  type = cbmemdup(vbuf, -1);
 
949
  if((pv = strchr(type, ';')) != NULL) *pv = '\0';
 
950
  cbstrtolower(type);
 
951
  enc = NULL;
 
952
  if((pv = strstr(vbuf, "charset=")) != NULL || (pv = strstr(vbuf, "CHARSET=")) != NULL){
 
953
    pv = strchr(pv, '=') + 1;
 
954
    if(*pv == '"') pv++;
 
955
    enc = cbmemdup(pv, -1);
 
956
    if((pv = strchr(enc, '"')) != NULL) *pv = '\0';
 
957
  }
 
958
  cmd = "";
 
959
  if(mtrules){
 
960
    for(i = 0; i < cblistnum(mtrules); i++){
 
961
      mtrule = (MTRULE *)cblistval(mtrules, i, NULL);
 
962
      if(est_regex_match(mtrule->regex, type)){
 
963
        cmd = mtrule->filter;
 
964
        break;
 
965
      }
 
966
    }
 
967
  } else if(!strcmp(type, "text/plain")){
 
968
    cmd = TEXTCMD;
 
969
  } else if(!strcmp(type, "text/html") || !strcmp(vbuf, "application/xhtml+xml")){
 
970
    cmd = HTMLCMD;
 
971
  } else if(!strcmp(type, "message/rfc822")){
 
972
    cmd = MIMECMD;
 
973
  }
 
974
  if(!strcmp(cmd, DRAFTCMD)){
 
975
    make_doc_from_draft(cbdatumptr(raw), cbdatumsize(raw), doc, links);
 
976
    if(!est_doc_attr(doc, ESTDATTRURI)) est_doc_add_attr(doc, ESTDATTRURI, url);
 
977
  } else if(!strcmp(cmd, TEXTCMD)){
 
978
    make_doc_from_text(cbdatumptr(raw), cbdatumsize(raw), enc, lang, doc, links);
 
979
    est_doc_add_attr(doc, ESTDATTRURI, url);
 
980
  } else if(!strcmp(cmd, HTMLCMD)){
 
981
    make_doc_from_html(cbdatumptr(raw), cbdatumsize(raw), enc, lang, doc, links);
 
982
    est_doc_add_attr(doc, ESTDATTRURI, url);
 
983
  } else if(!strcmp(cmd, MIMECMD)){
 
984
    make_doc_from_mime(cbdatumptr(raw), cbdatumsize(raw), enc, lang, doc, links);
 
985
    est_doc_add_attr(doc, ESTDATTRURI, url);
 
986
  } else if(cmd[0] != '\0'){
 
987
    make_doc_with_xcmd(cmd, url, cbdatumptr(raw), cbdatumsize(raw), enc, lang, doc, links);
 
988
    if(!est_doc_attr(doc, ESTDATTRURI)) est_doc_add_attr(doc, ESTDATTRURI, url);
 
989
  }
 
990
  free(enc);
 
991
  free(type);
 
992
  if((vbuf = cbmapget(heads, "last-modified", -1, NULL)) != NULL)
 
993
    est_doc_add_attr(doc, ESTDATTRMDATE, vbuf);
 
994
  if(links){
 
995
    for(i = 0; i < cblistnum(links); i++){
 
996
      tbuf = cburlresolve(url, cblistval(links, i, NULL));
 
997
      if(unrules){
 
998
        for(j = 0; j < cblistnum(unrules); j++){
 
999
          unrule = (UNRULE *)cblistval(unrules, j, NULL);
 
1000
          if(!est_regex_match(unrule->regex, tbuf)) continue;
 
1001
          dstr = est_regex_replace(tbuf, unrule->before, unrule->after);
 
1002
          free(tbuf);
 
1003
          tbuf = dstr;
 
1004
        }
 
1005
      }
 
1006
      if(cbstrfwmatch(tbuf, "http://")){
 
1007
        pv = tbuf + 7;
 
1008
        if((pv = strchr(pv, '/')) != NULL && cbstrfwmatch(pv, "/%E2%80%BE")){
 
1009
          pv[1] = '~';
 
1010
          memmove(pv + 2, pv + 10, strlen(pv + 10) + 1);
 
1011
        }
 
1012
        cblistover(links, i, tbuf, -1);
 
1013
      }
 
1014
      free(tbuf);
 
1015
    }
 
1016
  }
 
1017
  cblistclose(reqheads);
 
1018
  if(rdoc) est_doc_delete(rdoc);
 
1019
  if(rheads) cbmapclose(rheads);
 
1020
  if(rbuf) cbdatumclose(rbuf);
 
1021
  if(codep) *codep = rescode;
 
1022
  return TRUE;
 
1023
}
 
1024
 
 
1025
 
 
1026
 
 
1027
/*************************************************************************************************
 
1028
 * private objects
 
1029
 *************************************************************************************************/
 
1030
 
 
1031
 
 
1032
/* Close the log file. */
 
1033
static void log_close(void){
 
1034
  if(log_fp) fclose(log_fp);
 
1035
}
 
1036
 
 
1037
 
 
1038
/* Output the log message of a DB event.
 
1039
   `message' specifies the log message of a DB event.
 
1040
   `opaque' is simply ignored. */
 
1041
static void db_informer(const char *message, void *opaque){
 
1042
  assert(message);
 
1043
  log_print(LL_INFO, "DB-EVENT: %s", message);
 
1044
}
 
1045
 
 
1046
 
 
1047
/* Skip the label of a line.
 
1048
   `str' specifies a string of a line.
 
1049
   The return value is the pointer to the first character of the line. */
 
1050
static const char *skiplabel(const char *str){
 
1051
  assert(str);
 
1052
  if(!(str = strchr(str, ':'))) return "";
 
1053
  str++;
 
1054
  while(*str != '\0' && (*str == ' ' || *str == '\t')){
 
1055
    str++;
 
1056
  }
 
1057
  return str;
 
1058
}
 
1059
 
 
1060
 
 
1061
/* Make the absolute path of a relative path.
 
1062
   `rootdir' specifies the path of the root directory.
 
1063
   `path' specifies a ralative path of the a file. */
 
1064
static char *makeabspath(const char *rootdir, const char *path){
 
1065
  char mypath[URIBUFSIZ];
 
1066
  assert(rootdir && path);
 
1067
  if((ESTPATHCHR == '/' && path[0] == ESTPATHCHR) ||
 
1068
     (ESTPATHCHR == '\\' && ((path[0] >= 'A' && path[0] <= 'Z') ||
 
1069
                             (path[0] >= 'a' && path[0] <= 'z')) && path[1] == ':' &&
 
1070
      path[2] == '\\')){
 
1071
    sprintf(mypath, "%s", path);
 
1072
  } else {
 
1073
    sprintf(mypath, "%s%c%s", rootdir, ESTPATHCHR, path);
 
1074
  }
 
1075
  return est_realpath(mypath);
 
1076
}
 
1077
 
 
1078
 
 
1079
/* Compare keys of two records as double type objects.
 
1080
   `aptr' specifies the pointer to the region of one key.
 
1081
   `asiz' specifies the size of the region of one key.
 
1082
   `bptr' specifies the pointer to the region of the other key.
 
1083
   `bsiz' specifies the size of the region of the other key.
 
1084
   The return value is positive if the former is big, negative if the latter is big, 0 if both
 
1085
   are equivalent. */
 
1086
static int queue_compare(const char *aptr, int asiz, const char *bptr, int bsiz){
 
1087
  double anum, bnum;
 
1088
  assert(aptr && asiz >= 0 && bptr && bsiz >= 0);
 
1089
  if(asiz != bsiz) return asiz - bsiz;
 
1090
  anum = (asiz == sizeof(double) ? *(double *)aptr : INT_MIN);
 
1091
  bnum = (bsiz == sizeof(double) ? *(double *)bptr : INT_MIN);
 
1092
  if(anum > bnum) return 1;
 
1093
  if(anum < bnum) return -1;
 
1094
  return 0;
 
1095
}
 
1096
 
 
1097
 
 
1098
/* Compare two keywords by scores in descending order.
 
1099
   `ap' specifies the pointer to one keyword.
 
1100
   `bp' specifies the pointer to the other keyword.
 
1101
   The return value is negative if one is small, positive if one is big, 0 if both are equal. */
 
1102
static int keysc_compare(const void *ap, const void *bp){
 
1103
  assert(ap && bp);
 
1104
  return ((KEYSC *)bp)->pt - ((KEYSC *)ap)->pt;
 
1105
}
 
1106
 
 
1107
 
 
1108
/* Create a document object from docuemnt draft.
 
1109
   `buf' specifies the pointer to a data buffer.  It should be trailed by zero code.
 
1110
   `size' specifies the size of the buffer.
 
1111
   `doc' specifies a document handle to store attributes and texts.
 
1112
   `link' specifies a list handle to store links.  If it is `NULL', it is not used. */
 
1113
static void make_doc_from_draft(const char *buf, int size, ESTDOC *doc, CBLIST *links){
 
1114
  CBLIST *lines;
 
1115
  const char *line;
 
1116
  char *pv;
 
1117
  int i;
 
1118
  assert(buf && size >= 0 && doc);
 
1119
  lines = cbsplit(buf, -1, "\n");
 
1120
  for(i = 0; i < CB_LISTNUM(lines); i++){
 
1121
    line = CB_LISTVAL(lines, i);
 
1122
    while(*line > '\0' && *line <= ' '){
 
1123
      line++;
 
1124
    }
 
1125
    if(*line == '\0'){
 
1126
      i++;
 
1127
      break;
 
1128
    }
 
1129
    if(*line != '%' && (pv = strchr(line, '=')) != NULL){
 
1130
      *(pv++) = '\0';
 
1131
      est_doc_add_attr(doc, line, pv);
 
1132
    }
 
1133
  }
 
1134
  for(; i < CB_LISTNUM(lines); i++){
 
1135
    line = CB_LISTVAL(lines, i);
 
1136
    if(*line == '\t'){
 
1137
      est_doc_add_hidden_text(doc, line + 1);
 
1138
    } else {
 
1139
      est_doc_add_text(doc, line);
 
1140
    }
 
1141
  }
 
1142
  cblistclose(lines);
 
1143
}
 
1144
 
 
1145
 
 
1146
/* Create a document object from plain text.
 
1147
   `buf' specifies the pointer to a data buffer.  It should be trailed by zero code.
 
1148
   `size' specifies the size of the buffer.
 
1149
   `penc' specifies the name of preferred encoding.  If it is `NULL', it is not used.
 
1150
   `plang' specifies the code of preferred language.
 
1151
   `doc' specifies a document handle to store attributes and texts.
 
1152
   `link' specifies a list handle to store links.  If it is `NULL', it is not used. */
 
1153
static void make_doc_from_text(const char *buf, int size, const char *penc, int plang,
 
1154
                               ESTDOC *doc, CBLIST *links){
 
1155
  CBLIST *lines;
 
1156
  CBDATUM *datum;
 
1157
  const char *enc, *text, *line;
 
1158
  char *nbuf, numbuf[NUMBUFSIZ];
 
1159
  int i;
 
1160
  assert(buf && size >= 0 && doc);
 
1161
  if(check_binary(buf, size)) return;
 
1162
  enc = penc ? penc : est_enc_name(buf, size, plang);
 
1163
  if(!strcmp(enc, "UTF-8")){
 
1164
    nbuf = NULL;
 
1165
    text = buf;
 
1166
  } else {
 
1167
    text = buf;
 
1168
    nbuf = est_iconv(buf, size, enc, "UTF-8", NULL, NULL);
 
1169
    if(nbuf) text = nbuf;
 
1170
  }
 
1171
  lines = cbsplit(text, -1, "\n");
 
1172
  CB_DATUMOPEN(datum);
 
1173
  for(i = 0; i < CB_LISTNUM(lines); i++){
 
1174
    line = CB_LISTVAL(lines, i);
 
1175
    while(*line == ' ' || *line == '\t' || *line == '\r'){
 
1176
      line++;
 
1177
    }
 
1178
    if(line[0] == '\0'){
 
1179
      est_doc_add_text(doc, CB_DATUMPTR(datum));
 
1180
      CB_DATUMSETSIZE(datum, 0);
 
1181
    } else {
 
1182
      CB_DATUMCAT(datum, " ", 1);
 
1183
      CB_DATUMCAT(datum, line, strlen(line));
 
1184
    }
 
1185
  }
 
1186
  est_doc_add_text(doc, CB_DATUMPTR(datum));
 
1187
  CB_DATUMCLOSE(datum);
 
1188
  CB_LISTCLOSE(lines);
 
1189
  est_doc_add_attr(doc, ESTDATTRTYPE, "text/plain");
 
1190
  sprintf(numbuf, "%d", size);
 
1191
  est_doc_add_attr(doc, ESTDATTRSIZE, numbuf);
 
1192
  if(nbuf) free(nbuf);
 
1193
}
 
1194
 
 
1195
 
 
1196
/* Check whether a buffer is binary.
 
1197
   `buf' specifies the pointer to a data buffer.  It should be trailed by zero code.
 
1198
   `size' specifies the size of the buffer. */
 
1199
static int check_binary(const char *buf, int size){
 
1200
  int i, bin;
 
1201
  assert(buf && size >= 0);
 
1202
  if(size < 32) return FALSE;
 
1203
  /* PDF */
 
1204
  if(!memcmp(buf, "%PDF-", 5)) return TRUE;
 
1205
  /* PostScript */
 
1206
  if(!memcmp(buf, "%!PS-Adobe", 10)) return TRUE;
 
1207
  /* generic binary */
 
1208
  size -= 5;
 
1209
  if(size >= 256) size = 256;
 
1210
  bin = FALSE;
 
1211
  for(i = 0; i < size; i++){
 
1212
    if(buf[i] == 0x0){
 
1213
      if(buf[i+1] == 0x0 && buf[i+2] == 0x0 && buf[i+3] == 0x0 && buf[i+4] == 0x0) return TRUE;
 
1214
      bin = TRUE;
 
1215
    }
 
1216
  }
 
1217
  if(!bin) return FALSE;
 
1218
  /* PNG */
 
1219
  if(!memcmp(buf, "\x89PNG", 4)) return TRUE;
 
1220
  /* GIF(87a) */
 
1221
  if(!memcmp(buf, "GIF87a", 6)) return TRUE;
 
1222
  /* GIF(89a) */
 
1223
  if(!memcmp(buf, "GIF89a", 6)) return TRUE;
 
1224
  /* JFIF */
 
1225
  if(!memcmp(buf, "\xff\xd8JFIF", 6)) return TRUE;
 
1226
  /* TIFF(Intel) */
 
1227
  if(!memcmp(buf, "MM\x00\x2a", 4)) return TRUE;
 
1228
  /* TIFF(Motorola) */
 
1229
  if(!memcmp(buf, "II\x2a\x00", 4)) return TRUE;
 
1230
  /* BMP */
 
1231
  if(!memcmp(buf, "BM", 2)) return TRUE;
 
1232
  /* GZIP */
 
1233
  if(!memcmp(buf, "\x1f\x8b\x08", 3)) return TRUE;
 
1234
  /* BZIP2 */
 
1235
  if(!memcmp(buf, "BZh", 3)) return TRUE;
 
1236
  /* ZIP */
 
1237
  if(!memcmp(buf, "PK\x03\x04", 4)) return TRUE;
 
1238
  /* MP3(with ID3) */
 
1239
  if(!memcmp(buf, "ID3", 3)) return TRUE;
 
1240
  /* MP3 */
 
1241
  if(((buf[0] * 0x100 + buf[1]) & 0xfffe) == 0xfffa) return TRUE;
 
1242
  /* MIDI */
 
1243
  if(!memcmp(buf, "MThd", 4)) return TRUE;
 
1244
  /* RPM package*/
 
1245
  if(!memcmp(buf, "0xed0xab", 2)) return TRUE;
 
1246
  /* Debian package */
 
1247
  if(!memcmp(buf, "!<arch>\ndebian", 14)) return TRUE;
 
1248
  /* ELF */
 
1249
  if(!memcmp(buf, "\x7f\x45\x4c\x46", 4)) return TRUE;
 
1250
  /* MS-DOS executable */
 
1251
  if(!memcmp(buf, "MZ", 2)) return TRUE;
 
1252
  /* MS-Office */
 
1253
  if(!memcmp(buf, "\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1", 8)) return TRUE;
 
1254
  if(!memcmp(buf, "\xfe\x37\x00\x23", 4)) return TRUE;
 
1255
  if(!memcmp(buf, "\xdb\xa5-\x00\x00\x00", 6)) return TRUE;
 
1256
  return FALSE;
 
1257
}
 
1258
 
 
1259
 
 
1260
/* Create a document object from HTML.
 
1261
   `buf' specifies the pointer to a data buffer.  It should be trailed by zero code.
 
1262
   `size' specifies the size of the buffer.
 
1263
   `penc' specifies the name of preferred encoding.  If it is `NULL', it is not used.
 
1264
   `plang' specifies the code of preferred language.
 
1265
   `doc' specifies a document handle to store attributes and texts.
 
1266
   `link' specifies a list handle to store links.  If it is `NULL', it is not used. */
 
1267
static void make_doc_from_html(const char *buf, int size, const char *penc, int plang,
 
1268
                               ESTDOC *doc, CBLIST *links){
 
1269
  CBLIST *elems;
 
1270
  CBMAP *attrs;
 
1271
  CBDATUM *datum;
 
1272
  const char *enc, *html, *elem, *next, *value, *name, *content, *rp;
 
1273
  char *nbuf, *nenc, *rbuf, *lbuf, numbuf[NUMBUFSIZ];
 
1274
  int i, esiz;
 
1275
  assert(buf && size >= 0 && doc);
 
1276
  enc = est_enc_name(buf, size, plang);
 
1277
  html = NULL;
 
1278
  nbuf = NULL;
 
1279
  if(!strcmp(enc, "UTF-16") || !strcmp(enc, "UTF-16BE") || !strcmp(enc, "UTF-16LE")){
 
1280
    nbuf = est_iconv(buf, size, enc, "UTF-8", NULL, NULL);
 
1281
  } else if(!strcmp(enc, "US-ASCII")){
 
1282
    nbuf = NULL;
 
1283
  } else {
 
1284
    if((nenc = penc ? cbmemdup(penc, -1) : html_enc(buf)) != NULL){
 
1285
      if(cbstricmp(nenc, "UTF-8")){
 
1286
        nbuf = est_iconv(buf, size, nenc, "UTF-8", NULL, NULL);
 
1287
        if(!nbuf) nbuf = est_iconv(buf, size, enc, "UTF-8", NULL, NULL);
 
1288
      }
 
1289
      free(nenc);
 
1290
    } else {
 
1291
      nbuf = est_iconv(buf, size, enc, "UTF-8", NULL, NULL);
 
1292
    }
 
1293
  }
 
1294
  if(nbuf) html = nbuf;
 
1295
  if(!html) html = buf;
 
1296
  CB_DATUMOPEN(datum);
 
1297
  elems = cbxmlbreak(html, TRUE);
 
1298
  for(i = 0; i < CB_LISTNUM(elems); i++){
 
1299
    elem = CB_LISTVAL2(elems, i, esiz);
 
1300
    if(!(next = cblistval(elems, i + 1, NULL))) next = "";
 
1301
    if(elem[0] == '<'){
 
1302
      if(cbstrfwimatch(elem, "<html")){
 
1303
        attrs = cbxmlattrs(elem);
 
1304
        value = cbmapget(attrs, "lang", -1, NULL);
 
1305
        if(!value) value = cbmapget(attrs, "Lang", -1, NULL);
 
1306
        if(!value) value = cbmapget(attrs, "LANG", -1, NULL);
 
1307
        if(!value) value = cbmapget(attrs, "xml:lang", -1, NULL);
 
1308
        if(value && value[0] != '\0') est_doc_add_attr(doc, ESTDATTRLANG, value);
 
1309
        cbmapclose(attrs);
 
1310
      } else if(cbstrfwimatch(elem, "<meta")){
 
1311
        attrs = cbxmlattrs(elem);
 
1312
        name = cbmapget(attrs, "name", -1, NULL);
 
1313
        if(!name) name = cbmapget(attrs, "Name", -1, NULL);
 
1314
        if(!name) name = cbmapget(attrs, "NAME", -1, NULL);
 
1315
        if(!name) name = cbmapget(attrs, "http-equiv", -1, NULL);
 
1316
        if(!name) name = cbmapget(attrs, "Http-equiv", -1, NULL);
 
1317
        if(!name) name = cbmapget(attrs, "Http-Equiv", -1, NULL);
 
1318
        if(!name) name = cbmapget(attrs, "HTTP-EQUIV", -1, NULL);
 
1319
        content = cbmapget(attrs, "content", -1, NULL);
 
1320
        if(!content) content = cbmapget(attrs, "Content", -1, NULL);
 
1321
        if(!content) content = cbmapget(attrs, "CONTENT", -1, NULL);
 
1322
        if(name && content){
 
1323
          lbuf = cbmemdup(name, -1);
 
1324
          cbstrtolower(lbuf);
 
1325
          cbstrsqzspc(lbuf);
 
1326
          if(!strcmp(lbuf, "author")){
 
1327
            if(strchr(content, '&')){
 
1328
              rbuf = html_raw_text(content);
 
1329
              est_doc_add_attr(doc, ESTDATTRAUTHOR, rbuf);
 
1330
              free(rbuf);
 
1331
            } else {
 
1332
              est_doc_add_attr(doc, ESTDATTRAUTHOR, content);
 
1333
            }
 
1334
          } else if(!strcmp(lbuf, "refresh")){
 
1335
            if(strchr(content, '&')){
 
1336
              rbuf = html_raw_text(content);
 
1337
              rp = rbuf;
 
1338
              while((*rp >= '0' && *rp <= '9') || *rp == ' ' || *rp == '\t' || *rp == ';'){
 
1339
                rp++;
 
1340
              }
 
1341
              if(cbstrfwmatch(rp, "url=")) rp += 4;
 
1342
              if(*rp != '\0') cblistpush(links, rp, -1);
 
1343
              free(rbuf);
 
1344
            } else {
 
1345
              rp = content;
 
1346
              while((*rp >= '0' && *rp <= '9') || *rp == ' ' || *rp == '\t' || *rp == ';'){
 
1347
                rp++;
 
1348
              }
 
1349
              if(cbstrfwmatch(rp, "url=")) rp += 4;
 
1350
              if(*rp != '\0') cblistpush(links, rp, -1);
 
1351
            }
 
1352
          }
 
1353
          if(name[0] != '@' && name[0] != '_'){
 
1354
            if(strchr(content, '&')){
 
1355
              rbuf = html_raw_text(content);
 
1356
              est_doc_add_attr(doc, lbuf, rbuf);
 
1357
              free(rbuf);
 
1358
            } else {
 
1359
              est_doc_add_attr(doc, lbuf, content);
 
1360
            }
 
1361
          }
 
1362
          free(lbuf);
 
1363
        }
 
1364
        cbmapclose(attrs);
 
1365
      } else if(cbstrfwimatch(elem, "<title") && next[0] != '\0' && next[0] != '<'){
 
1366
        if(strchr(next, '&')){
 
1367
          rbuf = html_raw_text(next);
 
1368
          est_doc_add_attr(doc, ESTDATTRTITLE, rbuf);
 
1369
          est_doc_add_hidden_text(doc, rbuf);
 
1370
          free(rbuf);
 
1371
        } else {
 
1372
          est_doc_add_attr(doc, ESTDATTRTITLE, next);
 
1373
          est_doc_add_hidden_text(doc, next);
 
1374
        }
 
1375
        i++;
 
1376
      } else if(cbstrfwimatch(elem, "<style") || cbstrfwimatch(elem, "<script")){
 
1377
        while((next = cblistval(elems, i + 1, NULL)) != NULL &&
 
1378
              !(next[0] == '<' && next[1] != '!' && next[1] != ' ' && next[1] != '=')){
 
1379
          i++;
 
1380
        }
 
1381
      } else if(cbstrfwimatch(elem, "<h1") || cbstrfwimatch(elem, "<h2") ||
 
1382
                cbstrfwimatch(elem, "<h3") || cbstrfwimatch(elem, "<h4") ||
 
1383
                cbstrfwimatch(elem, "<h5") || cbstrfwimatch(elem, "<h6") ||
 
1384
                cbstrfwimatch(elem, "<p>") || cbstrfwimatch(elem, "<p ") ||
 
1385
                cbstrfwimatch(elem, "<div") || cbstrfwimatch(elem, "<hr") ||
 
1386
                cbstrfwimatch(elem, "<ul") || cbstrfwimatch(elem, "<ol") ||
 
1387
                cbstrfwimatch(elem, "<dl") || cbstrfwimatch(elem, "<li") ||
 
1388
                cbstrfwimatch(elem, "<dt") || cbstrfwimatch(elem, "<dd") ||
 
1389
                cbstrfwimatch(elem, "<th") || cbstrfwimatch(elem, "<td") ||
 
1390
                cbstrfwimatch(elem, "<pre")){
 
1391
        if(strchr(CB_DATUMPTR(datum), '&')){
 
1392
          rbuf = html_raw_text(CB_DATUMPTR(datum));
 
1393
          est_doc_add_text(doc, rbuf);
 
1394
          free(rbuf);
 
1395
        } else {
 
1396
          est_doc_add_text(doc, CB_DATUMPTR(datum));
 
1397
        }
 
1398
        CB_DATUMSETSIZE(datum, 0);
 
1399
      } else if(links && (cbstrfwimatch(elem, "<a") || cbstrfwimatch(elem, "<link"))){
 
1400
        attrs = cbxmlattrs(elem);
 
1401
        value = cbmapget(attrs, "href", -1, NULL);
 
1402
        if(!value) value = cbmapget(attrs, "HREF", -1, NULL);
 
1403
        if(value && !cbstrfwimatch(value, "https:") && !cbstrfwimatch(value, "ftp:") &&
 
1404
           !cbstrfwimatch(value, "mailto:") && !cbstrfwimatch(value, "javascript:"))
 
1405
          cblistpush(links, value, -1);
 
1406
        cbmapclose(attrs);
 
1407
      } else if(links && cbstrfwimatch(elem, "<frame")){
 
1408
        attrs = cbxmlattrs(elem);
 
1409
        value = cbmapget(attrs, "src", -1, NULL);
 
1410
        if(!value) value = cbmapget(attrs, "SRC", -1, NULL);
 
1411
        if(value && !cbstrfwimatch(value, "https:") && !cbstrfwimatch(value, "ftp:") &&
 
1412
           !cbstrfwimatch(value, "mailto:") && !cbstrfwimatch(value, "javascript:"))
 
1413
          cblistpush(links, value, -1);
 
1414
        cbmapclose(attrs);
 
1415
      } else if(links && cbstrfwimatch(elem, "<object")){
 
1416
        attrs = cbxmlattrs(elem);
 
1417
        value = cbmapget(attrs, "data", -1, NULL);
 
1418
        if(!value) value = cbmapget(attrs, "DATA", -1, NULL);
 
1419
        if(value && !cbstrfwimatch(value, "https:") && !cbstrfwimatch(value, "ftp:") &&
 
1420
           !cbstrfwimatch(value, "mailto:") && !cbstrfwimatch(value, "javascript:"))
 
1421
          cblistpush(links, value, -1);
 
1422
        cbmapclose(attrs);
 
1423
      } else if(links && (cbstrfwimatch(elem, "<embed") || cbstrfwimatch(elem, "<iframe"))){
 
1424
        attrs = cbxmlattrs(elem);
 
1425
        value = cbmapget(attrs, "src", -1, NULL);
 
1426
        if(!value) value = cbmapget(attrs, "SRC", -1, NULL);
 
1427
        if(value && !cbstrfwimatch(value, "https:") && !cbstrfwimatch(value, "ftp:") &&
 
1428
           !cbstrfwimatch(value, "mailto:") && !cbstrfwimatch(value, "javascript:"))
 
1429
          cblistpush(links, value, -1);
 
1430
        cbmapclose(attrs);
 
1431
      }
 
1432
    } else {
 
1433
      CB_DATUMCAT(datum, " ", 1);
 
1434
      CB_DATUMCAT(datum, elem, esiz);
 
1435
    }
 
1436
  }
 
1437
  CB_LISTCLOSE(elems);
 
1438
  if(strchr(CB_DATUMPTR(datum), '&')){
 
1439
    rbuf = html_raw_text(CB_DATUMPTR(datum));
 
1440
    est_doc_add_text(doc, rbuf);
 
1441
    free(rbuf);
 
1442
  } else {
 
1443
    est_doc_add_text(doc, CB_DATUMPTR(datum));
 
1444
  }
 
1445
  CB_DATUMCLOSE(datum);
 
1446
  if(nbuf) free(nbuf);
 
1447
  est_doc_add_attr(doc, ESTDATTRTYPE, "text/html");
 
1448
  sprintf(numbuf, "%d", size);
 
1449
  est_doc_add_attr(doc, ESTDATTRSIZE, numbuf);
 
1450
}
 
1451
 
 
1452
 
 
1453
/* Get the encoding of an HTML string.
 
1454
   `str' specifies string of HTML.
 
1455
   The return value is the name of the character encoding.
 
1456
   Because the region of the return value is allocated with the `malloc' call, it should be
 
1457
   released with the `free' call if it is no longer in use. */
 
1458
static char *html_enc(const char *str){
 
1459
  CBLIST *elems;
 
1460
  CBMAP *attrs;
 
1461
  const char *elem, *equiv, *content;
 
1462
  char *enc, *pv;
 
1463
  int i;
 
1464
  assert(str);
 
1465
  elems = cbxmlbreak(str, TRUE);
 
1466
  for(i = 0; i < CB_LISTNUM(elems); i++){
 
1467
    elem = CB_LISTVAL(elems, i);
 
1468
    if(elem[0] != '<' || !cbstrfwimatch(elem, "<meta")) continue;
 
1469
    enc = NULL;
 
1470
    attrs = cbxmlattrs(elem);
 
1471
    equiv = cbmapget(attrs, "http-equiv", -1, NULL);
 
1472
    if(!equiv) equiv = cbmapget(attrs, "HTTP-EQUIV", -1, NULL);
 
1473
    if(!equiv) equiv = cbmapget(attrs, "Http-Equiv", -1, NULL);
 
1474
    if(!equiv) equiv = cbmapget(attrs, "Http-equiv", -1, NULL);
 
1475
    if(equiv && !cbstricmp(equiv, "Content-Type")){
 
1476
      content = cbmapget(attrs, "content", -1, NULL);
 
1477
      if(!content) content = cbmapget(attrs, "Content", -1, NULL);
 
1478
      if(!content) content = cbmapget(attrs, "CONTENT", -1, NULL);
 
1479
      if(content && ((pv = strstr(content, "charset")) != NULL ||
 
1480
                     (pv = strstr(content, "Charset")) != NULL ||
 
1481
                     (pv = strstr(content, "CHARSET")) != NULL)){
 
1482
        enc = cbmemdup(pv + 8, -1);
 
1483
        if((pv = strchr(enc, ';')) != NULL || (pv = strchr(enc, '\r')) != NULL ||
 
1484
           (pv = strchr(enc, '\n')) != NULL || (pv = strchr(enc, ' ')) != NULL) *pv = '\0';
 
1485
      }
 
1486
    }
 
1487
    cbmapclose(attrs);
 
1488
    if(enc){
 
1489
      CB_LISTCLOSE(elems);
 
1490
      return enc;
 
1491
    }
 
1492
  }
 
1493
  CB_LISTCLOSE(elems);
 
1494
  return NULL;
 
1495
}
 
1496
 
 
1497
 
 
1498
/* Unescape entity references of HTML.
 
1499
   `str' specifies string of HTML.
 
1500
   The return value is the result string.
 
1501
   Because the region of the return value is allocated with the `malloc' call, it should be
 
1502
   released with the `free' call if it is no longer in use. */
 
1503
static char *html_raw_text(const char *html){
 
1504
  static const char *pairs[] = {
 
1505
    /* basic symbols */
 
1506
    "&amp;", "&", "&lt;", "<", "&gt;", ">", "&quot;", "\"", "&apos;", "'",
 
1507
    /* ISO-8859-1 */
 
1508
    "&nbsp;", "\xc2\xa0", "&iexcl;", "\xc2\xa1", "&cent;", "\xc2\xa2",
 
1509
    "&pound;", "\xc2\xa3", "&curren;", "\xc2\xa4", "&yen;", "\xc2\xa5",
 
1510
    "&brvbar;", "\xc2\xa6", "&sect;", "\xc2\xa7", "&uml;", "\xc2\xa8",
 
1511
    "&copy;", "\xc2\xa9", "&ordf;", "\xc2\xaa", "&laquo;", "\xc2\xab",
 
1512
    "&not;", "\xc2\xac", "&shy;", "\xc2\xad", "&reg;", "\xc2\xae",
 
1513
    "&macr;", "\xc2\xaf", "&deg;", "\xc2\xb0", "&plusmn;", "\xc2\xb1",
 
1514
    "&sup2;", "\xc2\xb2", "&sup3;", "\xc2\xb3", "&acute;", "\xc2\xb4",
 
1515
    "&micro;", "\xc2\xb5", "&para;", "\xc2\xb6", "&middot;", "\xc2\xb7",
 
1516
    "&cedil;", "\xc2\xb8", "&sup1;", "\xc2\xb9", "&ordm;", "\xc2\xba",
 
1517
    "&raquo;", "\xc2\xbb", "&frac14;", "\xc2\xbc", "&frac12;", "\xc2\xbd",
 
1518
    "&frac34;", "\xc2\xbe", "&iquest;", "\xc2\xbf", "&Agrave;", "\xc3\x80",
 
1519
    "&Aacute;", "\xc3\x81", "&Acirc;", "\xc3\x82", "&Atilde;", "\xc3\x83",
 
1520
    "&Auml;", "\xc3\x84", "&Aring;", "\xc3\x85", "&AElig;", "\xc3\x86",
 
1521
    "&Ccedil;", "\xc3\x87", "&Egrave;", "\xc3\x88", "&Eacute;", "\xc3\x89",
 
1522
    "&Ecirc;", "\xc3\x8a", "&Euml;", "\xc3\x8b", "&Igrave;", "\xc3\x8c",
 
1523
    "&Iacute;", "\xc3\x8d", "&Icirc;", "\xc3\x8e", "&Iuml;", "\xc3\x8f",
 
1524
    "&ETH;", "\xc3\x90", "&Ntilde;", "\xc3\x91", "&Ograve;", "\xc3\x92",
 
1525
    "&Oacute;", "\xc3\x93", "&Ocirc;", "\xc3\x94", "&Otilde;", "\xc3\x95",
 
1526
    "&Ouml;", "\xc3\x96", "&times;", "\xc3\x97", "&Oslash;", "\xc3\x98",
 
1527
    "&Ugrave;", "\xc3\x99", "&Uacute;", "\xc3\x9a", "&Ucirc;", "\xc3\x9b",
 
1528
    "&Uuml;", "\xc3\x9c", "&Yacute;", "\xc3\x9d", "&THORN;", "\xc3\x9e",
 
1529
    "&szlig;", "\xc3\x9f", "&agrave;", "\xc3\xa0", "&aacute;", "\xc3\xa1",
 
1530
    "&acirc;", "\xc3\xa2", "&atilde;", "\xc3\xa3", "&auml;", "\xc3\xa4",
 
1531
    "&aring;", "\xc3\xa5", "&aelig;", "\xc3\xa6", "&ccedil;", "\xc3\xa7",
 
1532
    "&egrave;", "\xc3\xa8", "&eacute;", "\xc3\xa9", "&ecirc;", "\xc3\xaa",
 
1533
    "&euml;", "\xc3\xab", "&igrave;", "\xc3\xac", "&iacute;", "\xc3\xad",
 
1534
    "&icirc;", "\xc3\xae", "&iuml;", "\xc3\xaf", "&eth;", "\xc3\xb0",
 
1535
    "&ntilde;", "\xc3\xb1", "&ograve;", "\xc3\xb2", "&oacute;", "\xc3\xb3",
 
1536
    "&ocirc;", "\xc3\xb4", "&otilde;", "\xc3\xb5", "&ouml;", "\xc3\xb6",
 
1537
    "&divide;", "\xc3\xb7", "&oslash;", "\xc3\xb8", "&ugrave;", "\xc3\xb9",
 
1538
    "&uacute;", "\xc3\xba", "&ucirc;", "\xc3\xbb", "&uuml;", "\xc3\xbc",
 
1539
    "&yacute;", "\xc3\xbd", "&thorn;", "\xc3\xbe", "&yuml;", "\xc3\xbf",
 
1540
    /* ISO-10646 */
 
1541
    "&fnof;", "\xc6\x92", "&Alpha;", "\xce\x91", "&Beta;", "\xce\x92",
 
1542
    "&Gamma;", "\xce\x93", "&Delta;", "\xce\x94", "&Epsilon;", "\xce\x95",
 
1543
    "&Zeta;", "\xce\x96", "&Eta;", "\xce\x97", "&Theta;", "\xce\x98",
 
1544
    "&Iota;", "\xce\x99", "&Kappa;", "\xce\x9a", "&Lambda;", "\xce\x9b",
 
1545
    "&Mu;", "\xce\x9c", "&Nu;", "\xce\x9d", "&Xi;", "\xce\x9e",
 
1546
    "&Omicron;", "\xce\x9f", "&Pi;", "\xce\xa0", "&Rho;", "\xce\xa1",
 
1547
    "&Sigma;", "\xce\xa3", "&Tau;", "\xce\xa4", "&Upsilon;", "\xce\xa5",
 
1548
    "&Phi;", "\xce\xa6", "&Chi;", "\xce\xa7", "&Psi;", "\xce\xa8",
 
1549
    "&Omega;", "\xce\xa9", "&alpha;", "\xce\xb1", "&beta;", "\xce\xb2",
 
1550
    "&gamma;", "\xce\xb3", "&delta;", "\xce\xb4", "&epsilon;", "\xce\xb5",
 
1551
    "&zeta;", "\xce\xb6", "&eta;", "\xce\xb7", "&theta;", "\xce\xb8",
 
1552
    "&iota;", "\xce\xb9", "&kappa;", "\xce\xba", "&lambda;", "\xce\xbb",
 
1553
    "&mu;", "\xce\xbc", "&nu;", "\xce\xbd", "&xi;", "\xce\xbe",
 
1554
    "&omicron;", "\xce\xbf", "&pi;", "\xcf\x80", "&rho;", "\xcf\x81",
 
1555
    "&sigmaf;", "\xcf\x82", "&sigma;", "\xcf\x83", "&tau;", "\xcf\x84",
 
1556
    "&upsilon;", "\xcf\x85", "&phi;", "\xcf\x86", "&chi;", "\xcf\x87",
 
1557
    "&psi;", "\xcf\x88", "&omega;", "\xcf\x89", "&thetasym;", "\xcf\x91",
 
1558
    "&upsih;", "\xcf\x92", "&piv;", "\xcf\x96", "&bull;", "\xe2\x80\xa2",
 
1559
    "&hellip;", "\xe2\x80\xa6", "&prime;", "\xe2\x80\xb2", "&Prime;", "\xe2\x80\xb3",
 
1560
    "&oline;", "\xe2\x80\xbe", "&frasl;", "\xe2\x81\x84", "&weierp;", "\xe2\x84\x98",
 
1561
    "&image;", "\xe2\x84\x91", "&real;", "\xe2\x84\x9c", "&trade;", "\xe2\x84\xa2",
 
1562
    "&alefsym;", "\xe2\x84\xb5", "&larr;", "\xe2\x86\x90", "&uarr;", "\xe2\x86\x91",
 
1563
    "&rarr;", "\xe2\x86\x92", "&darr;", "\xe2\x86\x93", "&harr;", "\xe2\x86\x94",
 
1564
    "&crarr;", "\xe2\x86\xb5", "&lArr;", "\xe2\x87\x90", "&uArr;", "\xe2\x87\x91",
 
1565
    "&rArr;", "\xe2\x87\x92", "&dArr;", "\xe2\x87\x93", "&hArr;", "\xe2\x87\x94",
 
1566
    "&forall;", "\xe2\x88\x80", "&part;", "\xe2\x88\x82", "&exist;", "\xe2\x88\x83",
 
1567
    "&empty;", "\xe2\x88\x85", "&nabla;", "\xe2\x88\x87", "&isin;", "\xe2\x88\x88",
 
1568
    "&notin;", "\xe2\x88\x89", "&ni;", "\xe2\x88\x8b", "&prod;", "\xe2\x88\x8f",
 
1569
    "&sum;", "\xe2\x88\x91", "&minus;", "\xe2\x88\x92", "&lowast;", "\xe2\x88\x97",
 
1570
    "&radic;", "\xe2\x88\x9a", "&prop;", "\xe2\x88\x9d", "&infin;", "\xe2\x88\x9e",
 
1571
    "&ang;", "\xe2\x88\xa0", "&and;", "\xe2\x88\xa7", "&or;", "\xe2\x88\xa8",
 
1572
    "&cap;", "\xe2\x88\xa9", "&cup;", "\xe2\x88\xaa", "&int;", "\xe2\x88\xab",
 
1573
    "&there4;", "\xe2\x88\xb4", "&sim;", "\xe2\x88\xbc", "&cong;", "\xe2\x89\x85",
 
1574
    "&asymp;", "\xe2\x89\x88", "&ne;", "\xe2\x89\xa0", "&equiv;", "\xe2\x89\xa1",
 
1575
    "&le;", "\xe2\x89\xa4", "&ge;", "\xe2\x89\xa5", "&sub;", "\xe2\x8a\x82",
 
1576
    "&sup;", "\xe2\x8a\x83", "&nsub;", "\xe2\x8a\x84", "&sube;", "\xe2\x8a\x86",
 
1577
    "&supe;", "\xe2\x8a\x87", "&oplus;", "\xe2\x8a\x95", "&otimes;", "\xe2\x8a\x97",
 
1578
    "&perp;", "\xe2\x8a\xa5", "&sdot;", "\xe2\x8b\x85", "&lceil;", "\xe2\x8c\x88",
 
1579
    "&rceil;", "\xe2\x8c\x89", "&lfloor;", "\xe2\x8c\x8a", "&rfloor;", "\xe2\x8c\x8b",
 
1580
    "&lang;", "\xe2\x8c\xa9", "&rang;", "\xe2\x8c\xaa", "&loz;", "\xe2\x97\x8a",
 
1581
    "&spades;", "\xe2\x99\xa0", "&clubs;", "\xe2\x99\xa3", "&hearts;", "\xe2\x99\xa5",
 
1582
    "&diams;", "\xe2\x99\xa6", "&OElig;", "\xc5\x92", "&oelig;", "\xc5\x93",
 
1583
    "&Scaron;", "\xc5\xa0", "&scaron;", "\xc5\xa1", "&Yuml;", "\xc5\xb8",
 
1584
    "&circ;", "\xcb\x86", "&tilde;", "\xcb\x9c", "&ensp;", "\xe2\x80\x82",
 
1585
    "&emsp;", "\xe2\x80\x83", "&thinsp;", "\xe2\x80\x89", "&zwnj;", "\xe2\x80\x8c",
 
1586
    "&zwj;", "\xe2\x80\x8d", "&lrm;", "\xe2\x80\x8e", "&rlm;", "\xe2\x80\x8f",
 
1587
    "&ndash;", "\xe2\x80\x93", "&mdash;", "\xe2\x80\x94", "&lsquo;", "\xe2\x80\x98",
 
1588
    "&rsquo;", "\xe2\x80\x99", "&sbquo;", "\xe2\x80\x9a", "&ldquo;", "\xe2\x80\x9c",
 
1589
    "&rdquo;", "\xe2\x80\x9d", "&bdquo;", "\xe2\x80\x9e", "&dagger;", "\xe2\x80\xa0",
 
1590
    "&Dagger;", "\xe2\x80\xa1", "&permil;", "\xe2\x80\xb0", "&lsaquo;", "\xe2\x80\xb9",
 
1591
    "&rsaquo;", "\xe2\x80\xba", "&euro;", "\xe2\x82\xac",
 
1592
    NULL
 
1593
  };
 
1594
  char *raw, *wp, buf[2], *tmp;
 
1595
  int i, j, hit, num, tsiz;
 
1596
  assert(html);
 
1597
  CB_MALLOC(raw, strlen(html) * 3 + 1);
 
1598
  wp = raw;
 
1599
  while(*html != '\0'){
 
1600
    if(*html == '&'){
 
1601
      if(*(html + 1) == '#'){
 
1602
        if(*(html + 2) == 'x' || *(html + 2) == 'X'){
 
1603
          num = strtol(html + 3, NULL, 16);
 
1604
        } else {
 
1605
          num = atoi(html + 2);
 
1606
        }
 
1607
        buf[0] = num / 256;
 
1608
        buf[1] = num % 256;
 
1609
        if((tmp = est_uconv_out(buf, 2, &tsiz)) != NULL){
 
1610
          for(j = 0; j < tsiz; j++){
 
1611
            *wp = ((unsigned char *)tmp)[j];
 
1612
            wp++;
 
1613
          }
 
1614
          free(tmp);
 
1615
        }
 
1616
        while(*html != ';' && *html != ' ' && *html != '\n' && *html != '\0'){
 
1617
          html++;
 
1618
        }
 
1619
        if(*html == ';') html++;
 
1620
      } else {
 
1621
        hit = FALSE;
 
1622
        for(i = 0; pairs[i] != NULL; i += 2){
 
1623
          if(cbstrfwmatch(html, pairs[i])){
 
1624
            wp += sprintf(wp, "%s", pairs[i+1]);
 
1625
            html += strlen(pairs[i]);
 
1626
            hit = TRUE;
 
1627
            break;
 
1628
          }
 
1629
        }
 
1630
        if(!hit){
 
1631
          *wp = *html;
 
1632
          wp++;
 
1633
          html++;
 
1634
        }
 
1635
      }
 
1636
    } else {
 
1637
      *wp = *html;
 
1638
      wp++;
 
1639
      html++;
 
1640
    }
 
1641
  }
 
1642
  *wp = '\0';
 
1643
  return raw;
 
1644
}
 
1645
 
 
1646
 
 
1647
/* Create a document object from MIME.
 
1648
   `buf' specifies the pointer to a data buffer.  It should be trailed by zero code.
 
1649
   `size' specifies the size of the buffer.
 
1650
   `penc' specifies the name of preferred encoding.  If it is `NULL', it is not used.
 
1651
   `plang' specifies the code of preferred language.
 
1652
   `doc' specifies a document handle to store attributes and texts.
 
1653
   `link' specifies a list handle to store links.  If it is `NULL', it is not used. */
 
1654
static void make_doc_from_mime(const char *buf, int size, const char *penc, int plang,
 
1655
                               ESTDOC *doc, CBLIST *links){
 
1656
  ESTDOC *tdoc;
 
1657
  CBMAP *attrs;
 
1658
  const CBLIST *texts;
 
1659
  CBLIST *parts, *lines;
 
1660
  CBDATUM *datum;
 
1661
  const char *key, *val, *bound, *part, *text, *line;
 
1662
  char *body, *swap, numbuf[NUMBUFSIZ];
 
1663
  int i, j, bsiz, psiz, ssiz, mht;
 
1664
  assert(buf && size >= 0 && doc);
 
1665
  attrs = cbmapopenex(MINIBNUM);
 
1666
  body = cbmimebreak(buf, size, attrs, &bsiz);
 
1667
  if((val = cbmapget(attrs, "subject", -1, NULL)) != NULL){
 
1668
    doc_add_attr_mime(doc, ESTDATTRTITLE, val);
 
1669
    if((val = est_doc_attr(doc, ESTDATTRTITLE)) != NULL) est_doc_add_hidden_text(doc, val);
 
1670
  }
 
1671
  if((val = cbmapget(attrs, "from", -1, NULL)) != NULL)
 
1672
    doc_add_attr_mime(doc, ESTDATTRAUTHOR, val);
 
1673
  if((val = cbmapget(attrs, "date", -1, NULL)) != NULL){
 
1674
    doc_add_attr_mime(doc, ESTDATTRCDATE, val);
 
1675
    doc_add_attr_mime(doc, ESTDATTRMDATE, val);
 
1676
  }
 
1677
  est_doc_add_attr(doc, ESTDATTRTYPE, "message/rfc822");
 
1678
  sprintf(numbuf, "%d", size);
 
1679
  est_doc_add_attr(doc, ESTDATTRSIZE, numbuf);
 
1680
  cbmapiterinit(attrs);
 
1681
  while((key = cbmapiternext(attrs, NULL)) != NULL){
 
1682
    if((key[0] >= 'A' && key[0] <= 'Z') || key[0] == '@' || key[0] == '_') continue;
 
1683
    val = cbmapget(attrs, key, -1, NULL);
 
1684
    doc_add_attr_mime(doc, key, val);
 
1685
  }
 
1686
  if((key = cbmapget(attrs, "TYPE", -1, NULL)) != NULL && cbstrfwimatch(key, "multipart/")){
 
1687
    mht = cbstrfwimatch(key, "multipart/related");
 
1688
    if((bound = cbmapget(attrs, "BOUNDARY", -1, NULL)) != NULL){
 
1689
      parts = cbmimeparts(body, bsiz, bound);
 
1690
      for(i = 0; i < CB_LISTNUM(parts) && i < 8; i++){
 
1691
        part = CB_LISTVAL2(parts, i, psiz);
 
1692
        tdoc = est_doc_new();
 
1693
        make_doc_from_mime(part, psiz, penc, plang, tdoc, links);
 
1694
        if(mht){
 
1695
          if((text = est_doc_attr(tdoc, ESTDATTRTITLE)) != NULL)
 
1696
            est_doc_add_attr(doc, ESTDATTRTITLE, text);
 
1697
          if((text = est_doc_attr(tdoc, ESTDATTRAUTHOR)) != NULL)
 
1698
            est_doc_add_attr(doc, ESTDATTRAUTHOR, text);
 
1699
        }
 
1700
        texts = est_doc_texts(tdoc);
 
1701
        for(j = 0; j < CB_LISTNUM(texts); j++){
 
1702
          text = CB_LISTVAL(texts, j);
 
1703
          est_doc_add_text(doc, text);
 
1704
        }
 
1705
        est_doc_delete(tdoc);
 
1706
      }
 
1707
      CB_LISTCLOSE(parts);
 
1708
    }
 
1709
  } else {
 
1710
    key = cbmapget(attrs, "content-transfer-encoding", -1, NULL);
 
1711
    if(key && cbstrfwimatch(key, "base64")){
 
1712
      swap = cbbasedecode(body, &ssiz);
 
1713
      free(body);
 
1714
      body = swap;
 
1715
      bsiz = ssiz;
 
1716
    } else if(key && cbstrfwimatch(key, "quoted-printable")){
 
1717
      swap = cbquotedecode(body, &ssiz);
 
1718
      free(body);
 
1719
      body = swap;
 
1720
      bsiz = ssiz;
 
1721
    }
 
1722
    key = cbmapget(attrs, "content-encoding", -1, NULL);
 
1723
    if(key && (cbstrfwimatch(key, "x-gzip") || cbstrfwimatch(key, "gzip")) &&
 
1724
       (swap = cbgzdecode(body, bsiz, &ssiz)) != NULL){
 
1725
      free(body);
 
1726
      body = swap;
 
1727
      bsiz = ssiz;
 
1728
    } else if(key && (cbstrfwimatch(key, "x-deflate") || cbstrfwimatch(key, "deflate")) &&
 
1729
              (swap = cbinflate(body, bsiz, &ssiz)) != NULL){
 
1730
      free(body);
 
1731
      body = swap;
 
1732
      bsiz = ssiz;
 
1733
    }
 
1734
    if(!(key = cbmapget(attrs, "TYPE", -1, NULL)) || cbstrfwimatch(key, "text/plain")){
 
1735
      if(!check_binary(body, bsiz)){
 
1736
        if(penc && (swap = est_iconv(body, bsiz, penc, "UTF-8", &ssiz, NULL)) != NULL){
 
1737
          free(body);
 
1738
          body = swap;
 
1739
          bsiz = ssiz;
 
1740
        } else if((key = cbmapget(attrs, "CHARSET", -1, NULL)) != NULL &&
 
1741
                  (swap = est_iconv(body, bsiz, key, "UTF-8", &ssiz, NULL)) != NULL){
 
1742
          free(body);
 
1743
          body = swap;
 
1744
          bsiz = ssiz;
 
1745
        }
 
1746
        lines = cbsplit(body, bsiz, "\n");
 
1747
        CB_DATUMOPEN(datum);
 
1748
        for(i = 0; i < CB_LISTNUM(lines); i++){
 
1749
          line = CB_LISTVAL(lines, i);
 
1750
          while(*line == ' ' || *line == '>' || *line == '|' || *line == '\t' || *line == '\r'){
 
1751
            line++;
 
1752
          }
 
1753
          if(line[0] == '\0'){
 
1754
            est_doc_add_text(doc, CB_DATUMPTR(datum));
 
1755
            CB_DATUMSETSIZE(datum, 0);
 
1756
          } else {
 
1757
            CB_DATUMCAT(datum, " ", 1);
 
1758
            CB_DATUMCAT(datum, line, strlen(line));
 
1759
          }
 
1760
        }
 
1761
        est_doc_add_text(doc, CB_DATUMPTR(datum));
 
1762
        CB_DATUMCLOSE(datum);
 
1763
        CB_LISTCLOSE(lines);
 
1764
      }
 
1765
    } else if(cbstrfwimatch(key, "text/html") || cbstrfwimatch(key, "application/xhtml+xml")){
 
1766
      tdoc = est_doc_new();
 
1767
      make_doc_from_html(body, bsiz, penc, plang, tdoc, links);
 
1768
      if((text = est_doc_attr(tdoc, ESTDATTRTITLE)) != NULL){
 
1769
        if(!est_doc_attr(doc, ESTDATTRTITLE)) est_doc_add_attr(doc, ESTDATTRTITLE, text);
 
1770
        est_doc_add_text(doc, text);
 
1771
      }
 
1772
      if((text = est_doc_attr(tdoc, ESTDATTRAUTHOR)) != NULL){
 
1773
        if(!est_doc_attr(doc, ESTDATTRAUTHOR)) est_doc_add_attr(doc, ESTDATTRAUTHOR, text);
 
1774
        est_doc_add_text(doc, text);
 
1775
      }
 
1776
      texts = est_doc_texts(tdoc);
 
1777
      for(i = 0; i < CB_LISTNUM(texts); i++){
 
1778
        text = CB_LISTVAL(texts, i);
 
1779
        est_doc_add_text(doc, text);
 
1780
      }
 
1781
      est_doc_delete(tdoc);
 
1782
    } else if(cbstrfwimatch(key, "message/rfc822")){
 
1783
      tdoc = est_doc_new();
 
1784
      make_doc_from_mime(body, bsiz, penc, plang, tdoc, links);
 
1785
      if((text = est_doc_attr(tdoc, ESTDATTRTITLE)) != NULL){
 
1786
        if(!est_doc_attr(doc, ESTDATTRTITLE)) est_doc_add_attr(doc, ESTDATTRTITLE, text);
 
1787
        est_doc_add_text(doc, text);
 
1788
      }
 
1789
      if((text = est_doc_attr(tdoc, ESTDATTRAUTHOR)) != NULL){
 
1790
        if(!est_doc_attr(doc, ESTDATTRAUTHOR)) est_doc_add_attr(doc, ESTDATTRAUTHOR, text);
 
1791
        est_doc_add_text(doc, text);
 
1792
      }
 
1793
      texts = est_doc_texts(tdoc);
 
1794
      for(i = 0; i < CB_LISTNUM(texts); i++){
 
1795
        text = CB_LISTVAL(texts, i);
 
1796
        est_doc_add_text(doc, text);
 
1797
      }
 
1798
      est_doc_delete(tdoc);
 
1799
    } else if(cbstrfwimatch(key, "text/")){
 
1800
      tdoc = est_doc_new();
 
1801
      make_doc_from_text(body, bsiz, penc, plang, tdoc, links);
 
1802
      texts = est_doc_texts(tdoc);
 
1803
      for(i = 0; i < CB_LISTNUM(texts); i++){
 
1804
        text = CB_LISTVAL(texts, i);
 
1805
        est_doc_add_text(doc, text);
 
1806
      }
 
1807
      est_doc_delete(tdoc);
 
1808
    }
 
1809
  }
 
1810
  free(body);
 
1811
  cbmapclose(attrs);
 
1812
}
 
1813
 
 
1814
 
 
1815
/* set mime value as an attribute of a document */
 
1816
static void doc_add_attr_mime(ESTDOC *doc, const char *name, const char *value){
 
1817
  char enc[64], *ebuf, *rbuf;
 
1818
  assert(doc && name && value);
 
1819
  ebuf = cbmimedecode(value, enc);
 
1820
  if((rbuf = est_iconv(ebuf, -1, enc, "UTF-8", NULL, NULL)) != NULL){
 
1821
    est_doc_add_attr(doc, name, rbuf);
 
1822
    free(rbuf);
 
1823
  }
 
1824
  free(ebuf);
 
1825
}
 
1826
 
 
1827
 
 
1828
/* Create a document object with an outer command.
 
1829
   `xcmd' specifies an outer command line.
 
1830
   `buf' specifies the pointer to a data buffer.  It should be trailed by zero code.
 
1831
   `size' specifies the size of the buffer.
 
1832
   `url' specifies the URL of the target document.
 
1833
   `penc' specifies the name of preferred encoding.  If it is `NULL', it is not used.
 
1834
   `plang' specifies the code of preferred language.
 
1835
   `doc' specifies a document handle to store attributes and texts.
 
1836
   `link' specifies a list handle to store links.  If it is `NULL', it is not used. */
 
1837
static void make_doc_with_xcmd(const char *xcmd, const char *url, const char *buf, int size,
 
1838
                               const char *penc, int plang, ESTDOC *doc, CBLIST *links){
 
1839
  const char *tmpdir, *pv, *ext, *fmt;
 
1840
  char iname[URIBUFSIZ], oname[URIBUFSIZ], cmd[URIBUFSIZ];
 
1841
  char *rbuf, numbuf[NUMBUFSIZ];
 
1842
  int rnd, pid, rsiz;
 
1843
  struct stat sbuf;
 
1844
  assert(buf && size >= 0 && url && xcmd);
 
1845
  if(ESTPATHCHR == '/' && stat("/tmp", &sbuf) == 0){
 
1846
    tmpdir = "/tmp";
 
1847
  } else if(ESTPATHCHR == '\\' &&
 
1848
            ((pv = getenv("TMP")) != NULL || (pv = getenv("TEMP")) != NULL) &&
 
1849
            stat(pv, &sbuf) == 0){
 
1850
    tmpdir = pv;
 
1851
  } else {
 
1852
    tmpdir = ESTCDIRSTR;
 
1853
  }
 
1854
  ext = NULL;
 
1855
  if((pv = strrchr(url, ESTPATHCHR)) != NULL) url = pv;
 
1856
  if((pv = strrchr(url, ESTEXTCHR)) != NULL) ext = pv;
 
1857
  if(!ext || strlen(ext) >= 32 || strchr(ext, '"') || strchr(ext, '\\')) ext = "";
 
1858
  rnd = dpouterhash(url, -1) & 0xffff;
 
1859
  pid = (int)getpid() & 0xffff;
 
1860
  sprintf(iname, "%s%cxcmd-in-%04X%04X%s", tmpdir, ESTPATHCHR, pid, rnd, ext);
 
1861
  sprintf(oname, "%s%cxcmd-out-%04X%04X%cest", tmpdir, ESTPATHCHR, pid, rnd, ESTEXTCHR);
 
1862
  fmt = DRAFTCMD;
 
1863
  if(cbstrfwmatch(xcmd, "T@")){
 
1864
    fmt = TEXTCMD;
 
1865
    xcmd += 2;
 
1866
  } else if(cbstrfwmatch(xcmd, "H@")){
 
1867
    fmt = HTMLCMD;
 
1868
    xcmd += 2;
 
1869
  } else if(cbstrfwmatch(xcmd, "M@")){
 
1870
    fmt = MIMECMD;
 
1871
    xcmd += 2;
 
1872
  }
 
1873
  cbwritefile(iname, buf, size);
 
1874
  sprintf(cmd, "%s \"%s\" \"%s\"", xcmd, iname, oname);
 
1875
  system(cmd);
 
1876
  if((rbuf = cbreadfile(oname, &rsiz)) != NULL){
 
1877
    if(fmt == DRAFTCMD){
 
1878
      make_doc_from_draft(rbuf, rsiz, doc, links);
 
1879
    } else if(fmt == TEXTCMD){
 
1880
      make_doc_from_text(rbuf, rsiz, penc, plang, doc, links);
 
1881
    } else if(fmt == HTMLCMD){
 
1882
      make_doc_from_html(rbuf, rsiz, penc, plang, doc, links);
 
1883
    } else if(fmt == MIMECMD){
 
1884
      make_doc_from_mime(rbuf, rsiz, penc, plang, doc, links);
 
1885
    }
 
1886
    free(rbuf);
 
1887
  }
 
1888
  if(doc && fmt != NULL){
 
1889
    sprintf(numbuf, "%d", size);
 
1890
    est_doc_add_attr(doc, ESTDATTRSIZE, numbuf);
 
1891
    est_doc_add_attr(doc, ESTDATTRTYPE, est_ext_type(ext));
 
1892
  }
 
1893
  unlink(oname);
 
1894
  unlink(iname);
 
1895
}
 
1896
 
 
1897
 
 
1898
 
 
1899
/* END OF FILE */