/* $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 != NULL; 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 = strstr(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); }