diff options
author | robertabcd <robertabcd@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2014-10-05 23:42:36 +0800 |
---|---|---|
committer | robertabcd <robertabcd@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2014-10-05 23:42:36 +0800 |
commit | 3b0f284ef9e66dab6f43785873b8bb97389c996f (patch) | |
tree | 69f77901ed70fb5826b81fe7a65668477c50a83a | |
parent | 779c686861cb2c8d4183c2a362b5856dd8b56f27 (diff) | |
download | pttbbs-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.c | 223 |
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) { |