From 51b913eb2ff91adfe8cd5e417759a6057c5a8a21 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Thu, 13 Nov 2008 16:17:55 +0000 Subject: XBitTorrent --- meta.c | 265 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 meta.c (limited to 'meta.c') diff --git a/meta.c b/meta.c new file mode 100644 index 0000000..6255e2e --- /dev/null +++ b/meta.c @@ -0,0 +1,265 @@ +/* $Id$ */ +/* + * Copyright (c) 2005 Dimitri Sokolyuk + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "bencode.h" +#include "meta.h" +#include "files.h" +#include "tools.h" + +#ifndef lint +static const char rcsid[] = "@(#)$Id$"; +#endif /* not lint */ + +static char *encpath(struct btnode *, char *); +static struct btmeta *parsemeta(struct btnode *); +static struct btannounce *getannounce(char *); +static void freeannounce(struct btannounce *); + +struct btmeta * +btreadmeta(char *fname) +{ + /* TODO: realloc btmeta, if the next torrent is to read */ + struct btnode *np; + struct btmeta *mp; + struct stat st; + char *buf; + + if (stat(fname, &st) == -1 || !(st.st_mode & S_IFREG)) + return NULL; + +#if MMAP + off_t len; + if ((buf = (char *) btmmapfile(fname, &len)) == NULL) +#else + if ((buf = btfreadall(fname)) == NULL) +#endif + return NULL; + + /* the first node must be a dictionary followed by a digit */ + if (*buf != 'd' || !(isascii(*(buf + 1)) && isdigit(*(buf + 1)))) + return NULL; + + np = btbencode(buf); + if ((mp = parsemeta(np)) == NULL) { + fprintf(stderr, "parsemeta\n"); + return NULL; + } + +#if DEBUG + btdumpnode(np); +#endif + + mp->fname = strdup(fname); + +#if MMAP + btunmapfile((void *) buf, len); +#else + free(buf); +#endif + btfreenode(np); + + return mp; +} + +void +btfreemeta(struct btmeta *mp) +{ + struct btfile *fp, *nxt; + + if (mp == NULL) + return; + + freeannounce(mp->announce); + free(mp->name); + free(mp->fname); + for (fp = SIMPLEQ_FIRST(&mp->flist); fp != SIMPLEQ_END(&mp->flist); fp = nxt) { + nxt = SIMPLEQ_NEXT(fp, link); + free(fp->path); + free(fp); + } + SIMPLEQ_INIT(&mp->flist); + free(mp->pieces); + if (mp->comment != NULL) + free(mp->comment); + if (mp->by != NULL) + free(mp->by); + free(mp->bitfield); + free(mp); +} + +static char * +encpath(struct btnode *np, char *parent) +{ + char buf[MAXPATHLEN]; + + if (np->list == NULL) + return NULL; + + strlcpy(buf, parent, sizeof(buf)); + strlcat(buf, "/", sizeof(buf)); + while (np != NULL) { + strlcat(buf, np->list->str, sizeof(buf)); + strlcat(buf, "/", sizeof(buf)); + np = np->next; + } + buf[strlen(buf) - 1] = '\0'; + + return strdup(buf); +} + +/* FIXME: should it all be there or in main? */ +static struct btmeta * +parsemeta(struct btnode *np) +{ + struct btnode *found, *info, *files; + struct btmeta *mp; + struct btfile *fp; + + if ((mp = calloc(1, sizeof(struct btmeta))) == NULL) + return NULL; + + if ((found = btfinddict(np, "announce")) != NULL) + if ((mp->announce = getannounce(found->str)) == NULL) + return NULL; + + /* TODO: announce-list */ + + if ((found = btfinddict(np, "comment")) != NULL) + mp->comment = strdup(found->str); + + if ((found = btfinddict(np, "creation date")) != NULL) + mp->date = found->num; + + if ((found = btfinddict(np, "created by")) != NULL) + mp->by = strdup(found->str); + + if ((info = btfinddict(np, "info")) == NULL) + return NULL; + + SHA1(info->aux.off, info->aux.len, mp->infohash); + + if ((found = btfinddict(info, "name")) != NULL) + mp->name = strdup(found->str); + + SIMPLEQ_INIT(&mp->flist); + + if ((found = btfinddict(info, "length")) != NULL) { + if ((fp = calloc(1, sizeof(struct btfile))) == NULL) + return NULL; + + fp->length = mp->size = found->num; + fp->path = strdup(mp->name); + + SIMPLEQ_INSERT_TAIL(&mp->flist, fp, link); + mp->nfiles = 1; + } else { + mp->size = 0; + mp->nfiles = 0; + files = btfinddict(info, "files"); + while (files != NULL) { + if ((fp = calloc(1, sizeof(struct btfile))) == NULL) + return NULL; + if ((found = btfinddict(files->list, "length")) != NULL) { + fp->length = found->num; + mp->size += found->num; + } + + /* TODO: change encpath so that it returns (char **) */ + if ((found = btfinddict(files->list, "path")) != NULL) + fp->path = encpath(found, mp->name); + + SIMPLEQ_INSERT_TAIL(&mp->flist, fp, link); + ++mp->nfiles; + + files = files->next; + } + } + + if ((found = btfinddict(info, "piece length")) != NULL) + mp->plength = found->num; + + if ((found = btfinddict(info, "pieces")) != NULL) { + int i; + mp->npieces = howmany(mp->size, mp->plength); + if ((mp->pieces = calloc(mp->npieces, sizeof(*mp->pieces))) == NULL) + return NULL; + for (i = 0; i < mp->npieces; i++) + memcpy(mp->pieces[i], &found->str[i * SHA1LEN], SHA1LEN); + } + + /* initialize bitfield */ + mp->bitlen = howmany(mp->npieces, sizeof(char) * NBBY); + if ((mp->bitfield = calloc(mp->bitlen, sizeof(char))) == NULL) + return NULL; + + return mp; +} + +static struct btannounce * +getannounce(char *src) +{ +#define HTTP_URL "http://" +#define HTTP_PORT 80 + + struct btannounce *ap; + char *buf, *host, *port, *path; + + buf = strdup(src); + + if ((ap = calloc(1, sizeof(struct btannounce))) == NULL) + return NULL; + + if ((host = strcasestr(buf, HTTP_URL)) == NULL) { + free(buf); + return NULL; + } else + host += sizeof(HTTP_URL) - 1; + + if ((path = strchr(host, '/')) != NULL) + *path++ = '\0'; + ap->path = path ? strdup(path) : NULL; + + if ((port = strrchr(host, ':')) != NULL) + *port++ = '\0'; + ap->port = port ? atoi(port) : HTTP_PORT; + + ap->host = strdup(host); /* have to be last */ + + free(buf); + + return ap; +} + +static void +freeannounce(struct btannounce *ap) +{ + if (ap->path) + free(ap->path); /* XXX */ + free(ap->host); + free(ap); +} -- cgit v1.2.3