summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrobertabcd <robertabcd@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>2014-10-05 23:42:36 +0800
committerrobertabcd <robertabcd@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>2014-10-05 23:42:36 +0800
commit3b0f284ef9e66dab6f43785873b8bb97389c996f (patch)
tree69f77901ed70fb5826b81fe7a65668477c50a83a
parent779c686861cb2c8d4183c2a362b5856dd8b56f27 (diff)
downloadpttbbs-3b0f284ef9e66dab6f43785873b8bb97389c996f.tar
pttbbs-3b0f284ef9e66dab6f43785873b8bb97389c996f.tar.gz
pttbbs-3b0f284ef9e66dab6f43785873b8bb97389c996f.tar.bz2
pttbbs-3b0f284ef9e66dab6f43785873b8bb97389c996f.tar.lz
pttbbs-3b0f284ef9e66dab6f43785873b8bb97389c996f.tar.xz
pttbbs-3b0f284ef9e66dab6f43785873b8bb97389c996f.tar.zst
pttbbs-3b0f284ef9e66dab6f43785873b8bb97389c996f.zip
boardd: article{stat,part,head,tail} keys.
"<bid>.articlestat.<filename>" => "<cachekey>,<filesize>" "<bid>.article<method>.<cachekey>.<offset>.<maxlen>.<filename>" => "<metaline>\n<content>" method = "part" | "head" | "tail" metaline = "<cachekey>,<filesize>,<offset>,<length>" Cache key is a key that can be used to check if the file is edit by user. Currently, "<st_dev>-<st_ino>" is used for approximation. Each method operates content in file described by <offset> and <length>. The function of methods is described as below: part - output the desired part of file. head - select a good portion from the beginning of the part. (eg. end with a new-line; not inside a DBCS char) tail - select a good portion from the end of the part. Note that <offset> and <length> correspond to the original file, not to be confused with conversions. (eg. UTF-8) git-svn-id: http://opensvn.csie.org/pttbbs/trunk@6077 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
-rw-r--r--pttbbs/daemon/boardd/boardd.c223
1 files changed, 217 insertions, 6 deletions
diff --git a/pttbbs/daemon/boardd/boardd.c b/pttbbs/daemon/boardd/boardd.c
index fd4200d1..e5273438 100644
--- a/pttbbs/daemon/boardd/boardd.c
+++ b/pttbbs/daemon/boardd/boardd.c
@@ -69,6 +69,199 @@ article_list(struct evbuffer *buf, boardheader_t *bptr, int offset, int length)
close(fd);
}
+static int
+is_valid_article_filename(const char *filename)
+{
+ return !strncmp(filename, "M.", 2);
+}
+
+static int
+answer_file(struct evbuffer *buf, const char *path, struct stat *st,
+ const char *ck, int cklen, int offset, int maxlen)
+{
+ struct stat local_st;
+ int fd;
+
+ if (st == NULL)
+ st = &local_st;
+
+ if ((fd = open(path, O_RDONLY)) < 0 || fstat(fd, st) < 0)
+ goto answer_file_errout;
+
+ if (ck && cklen) {
+ char ckbuf[128];
+ snprintf(ckbuf, sizeof(ckbuf), "%d-%d", st->st_dev, st->st_ino);
+ if (strncmp(ck, ckbuf, cklen) != 0)
+ goto answer_file_errout;
+ }
+
+ if (offset < 0)
+ offset += st->st_size;
+ if (offset < 0)
+ offset = 0;
+ if (offset > st->st_size)
+ goto answer_file_errout;
+
+ if (maxlen < 0 || offset + maxlen > st->st_size)
+ maxlen = st->st_size - offset;
+
+ if (evbuffer_add_file(buf, fd, offset, maxlen) == 0)
+ return 0;
+
+answer_file_errout:
+ if (fd >= 0)
+ close(fd);
+ return -1;
+}
+
+static int
+parse_articlepart_key(const char *key, const char **ck, int *cklen,
+ int *offset, int *maxlen, const char **filename)
+{
+ // <key> = <cache_key>.<offset>.<maxlen>.<filename>
+ *ck = key;
+ int i;
+ for (i = 0; key[i]; i++) {
+ if (key[i] == '.') {
+ *cklen = i;
+ break;
+ }
+ }
+ if (key[i] != '.')
+ return 0;
+ key += i + 1;
+
+ char *p;
+ *offset = strtol(key, &p, 10);
+ if (*p != '.')
+ return 0;
+ key = p + 1;
+
+ *maxlen = strtol(key, &p, 10);
+ if (*p != '.')
+ return 0;
+
+ *filename = p + 1;
+ return 1;
+}
+
+static int
+find_good_truncate_point_from_begin(const char *content, int size)
+{
+ int last_startline = 0;
+ int last_charend = 0;
+ int last_dbcstail = 0;
+ int i;
+ const char *p;
+ for (i = 1, p = content; i <= size; i++, p++) {
+ if (i > last_dbcstail) {
+ if (IS_DBCSLEAD(*p)) {
+ last_dbcstail = i + 1;
+ if (i + 1 <= size)
+ last_charend = i + 1;
+ } else
+ last_charend = i;
+ }
+ if (*p == '\n')
+ last_startline = i;
+ }
+ return last_startline > 0 ? last_startline : last_charend;
+}
+
+static int
+find_good_truncate_point_from_end(const char *content, int size)
+{
+ int i;
+ const char *p;
+ for (i = 1, p = content; i <= size; i++, p++)
+ if (*p == '\n')
+ return i;
+ return 0;
+}
+
+static int
+select_article_head(const char *data, int len, int *offset, int *size, void *ctx)
+{
+ *offset = 0;
+ *size = find_good_truncate_point_from_begin(data, len);
+ return 0;
+}
+
+static int
+select_article_tail(const char *data, int len, int *offset, int *size, void *ctx)
+{
+ *offset = find_good_truncate_point_from_end(data, len);
+ *size = find_good_truncate_point_from_begin(data + *offset, len - *offset);
+ return 0;
+}
+
+static int
+select_article_part(const char *data, int len, int *offset, int *size, void *ctx)
+{
+ *offset = 0;
+ *size = len;
+ return 0;
+}
+
+static void
+cleanup_evbuffer(const void *data, size_t datalen, void *extra)
+{
+ evbuffer_free((struct evbuffer *)extra);
+}
+
+static int
+evbuffer_slice(struct evbuffer *buf, int offset, int size)
+{
+ int len = evbuffer_get_length(buf);
+ if (offset + size > len)
+ return -1;
+
+ struct evbuffer *back = evbuffer_new();
+ evbuffer_add_buffer(back, buf);
+
+ if (evbuffer_add_reference(buf, evbuffer_pullup(back, len) + offset,
+ size, cleanup_evbuffer, back) == 0)
+ return 0;
+
+ evbuffer_free(back);
+ return -1;
+}
+
+typedef int (*select_part_func)(const char *data, int len, int *offset, int *size, void *ctx);
+
+static int
+answer_articleselect(struct evbuffer *buf, const boardheader_t *bptr,
+ const char *rest_key, select_part_func sfunc, void *ctx)
+{
+ char path[PATH_MAX];
+ const char *ck, *filename;
+ int cklen, offset, maxlen;
+ struct stat st;
+
+ if (!parse_articlepart_key(rest_key, &ck, &cklen, &offset, &maxlen, &filename))
+ return -1;
+
+ if (!is_valid_article_filename(filename))
+ return -1;
+
+ setbfile(path, bptr->brdname, filename);
+ if (answer_file(buf, path, &st, ck, cklen, offset, maxlen) < 0)
+ return -1;
+
+ int sel_offset, sel_size;
+ int len = evbuffer_get_length(buf);
+ if (sfunc(evbuffer_pullup(buf, len), len, &sel_offset, &sel_size, ctx) != 0 ||
+ evbuffer_slice(buf, sel_offset, sel_size) != 0)
+ return -1;
+
+ struct evbuffer *meta = evbuffer_new();
+ evbuffer_add_printf(meta, "%d-%d,%lu,%d,%d\n",
+ st.st_dev, st.st_ino, st.st_size, sel_offset, sel_size);
+ evbuffer_prepend_buffer(buf, meta);
+ evbuffer_free(meta);
+ return 0;
+}
+
static void
answer_key(struct evbuffer *buf, const char *key)
{
@@ -133,9 +326,9 @@ answer_key(struct evbuffer *buf, const char *key)
if (!p || (length = atoi(p+1)) == 0)
length = DEFAULT_ARTICLE_LIST;
- return article_list(buf, bptr, offset, length);
+ article_list(buf, bptr, offset, length);
} else if (strncmp(key, "article.", 8) == 0) {
- if (strncmp(key + 8, "M.", 2) != 0)
+ if (!is_valid_article_filename(key + 8))
return;
char path[PATH_MAX];
@@ -145,10 +338,28 @@ answer_key(struct evbuffer *buf, const char *key)
setbfile(path, bptr->brdname, key + 8);
if ((fd = open(path, O_RDONLY)) < 0)
return;
- if (fstat(fd, &st) < 0 ||
- st.st_size == 0 ||
- evbuffer_add_file(buf, fd, 0, st.st_size) != 0)
- close(fd);
+ if (fstat(fd, &st) < 0 ||
+ st.st_size == 0 ||
+ evbuffer_add_file(buf, fd, 0, st.st_size) != 0)
+ close(fd);
+ } else if (strncmp(key, "articlestat.", 12) == 0) {
+ if (!is_valid_article_filename(key + 12))
+ return;
+
+ char path[PATH_MAX];
+ struct stat st;
+
+ setbfile(path, bptr->brdname, key + 12);
+ if (stat(path, &st) < 0)
+ return;
+
+ evbuffer_add_printf(buf, "%d-%d,%ld", st.st_dev, st.st_ino, st.st_size);
+ } else if (strncmp(key, "articlepart.", 12) == 0) {
+ answer_articleselect(buf, bptr, key + 12, select_article_part, NULL);
+ } else if (strncmp(key, "articlehead.", 12) == 0) {
+ answer_articleselect(buf, bptr, key + 12, select_article_head, NULL);
+ } else if (strncmp(key, "articletail.", 12) == 0) {
+ answer_articleselect(buf, bptr, key + 12, select_article_tail, NULL);
} else
return;
} else if (strncmp(key, "tobid.", 6) == 0) {