summaryrefslogtreecommitdiff
path: root/meta.c
diff options
context:
space:
mode:
Diffstat (limited to 'meta.c')
-rw-r--r--meta.c265
1 files changed, 265 insertions, 0 deletions
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 <demon@vhost.dyndns.org>
+ *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <openssl/sha.h>
+
+#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);
+}