From 22c887bcf77c0b40aaeffbe18a8a5afcc77b03e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Wed, 3 Sep 2008 09:34:50 +0000 Subject: added stephen sinclairs "urloader" svn path=/trunk/externals/loaders/urloader/; revision=10275 --- Makefile | 167 ++++++++++++++++++++ another.pd | 7 + baseurl.c | 58 +++++++ baseurl.h | 15 ++ md5.c | 452 +++++++++++++++++++++++++++++++++++++++++++++++++++++ md5.h | 136 ++++++++++++++++ pd.index | 2 + test.pd | 13 ++ unknown.pd | 5 + urlloader.c | 509 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 1364 insertions(+) create mode 100644 Makefile create mode 100644 another.pd create mode 100644 baseurl.c create mode 100644 baseurl.h create mode 100644 md5.c create mode 100644 md5.h create mode 100644 pd.index create mode 100644 test.pd create mode 100644 unknown.pd create mode 100644 urlloader.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d85ac8d --- /dev/null +++ b/Makefile @@ -0,0 +1,167 @@ +# Makefile +# based on hexloader Makefile (c) 2007 IOhannes m zmölnig + +# path to pd +## change this according to your setup! +PDROOT=../../../pd + +# here we find the sources of pd (and evtl. the pd.lib) +PDSRCDIR=$(PDROOT)/src +PDLIBDIR=$(PDROOT)/bin + +# this is the filename-extension +# people have to specify it at the cmdline: eg "make pd_linux" +EXTENSION=$(MAKECMDGOALS) + + +DEFINES=-DPD + +# if no filename-extension is supplied by the user +# try to guess one, based on what "uname" tells us +UNAME := $(shell uname -s) +ifeq ($(UNAME),Linux) + DEFAULTEXTENSION= pd_linux + DEFINES+=-DDL_OPEN +else + ifeq ($(UNAME),Darwin) + DEFAULTEXTENSION= pd_darwin + DEFINES+=-DDL_OPEN + else + ifeq (MINGW,$(findstring MINGW,$(UNAME))) + DEFAULTEXTENSION=pd_nt + else + ifeq ($(UNAME),IRIX) + UNAMEV := $(shell uname -R) + ifeq (6.,$(findstring 6.,$(UNAMEV))) + DEFAULTEXTENSION= pd_irix6 + DEFINES+=-DDL_OPEN + else + DEFAULTEXTENSION= pd_irix5 + DEFINES+=-DDL_OPEN + endif + else + DEFAULTEXTENSION=help + endif + endif + endif +endif + +# if no extension is given, call "make" again with a guessed extension +auto: + make $(DEFAULTEXTENSION) + +# just a stupid fallback +help: + @echo "choose one command: make pd_linux (linux), make pd_darwin (osX), make pd_irix5 (IRIX5), make pd_irix6 (IRIX6), make dll (MSVC), make pd_nt (MinWG)" + +# delete old build files +clean: + -rm -f *.dll *.pd_* *.o *.obj *~ + +# we want to compile all C-files we find in the current directory +SOURCES=$(sort $(filter %.c, $(wildcard *.c))) +# some C-files maps will become an external with the given filename-extension +TARGETS=urlloader.pd_linux baseurl.pd_linux + + +# ----------------------- Linux ----------------------- + +pd_linux: $(TARGETS) + +LINUXCFLAGS = $(DEFINES) -funroll-loops -fomit-frame-pointer -fPIC \ + -Wall -W -Wshadow -Wstrict-prototypes -Werror \ + -Wno-unused -Wno-parentheses -Wno-switch + +ifeq ($(DEBUG),1) +LINUXCFLAGS += -ggdb +else +LINUXCFLAGS += -O2 +endif + +LINUXLDFLAGS = -export_dynamic -shared -lc -lm +LINUXLIBS = -lcurl + +LINUXINCLUDE = -I$(PDSRCDIR) + +%.pd_linux: %.c + $(CC) $(LINUXLDFLAGS) $(LINUXCFLAGS) $(LINUXINCLUDE) -o $*.pd_linux $*.c $(LINUXLIBS) + strip --strip-unneeded $*.pd_linux + + + +# ----------------------- Mac OSX ----------------------- + +pd_darwin: $(TARGETS) + +DARWINCFLAGS = $(DEFINES) -O2 -Wall -W -Wshadow -Wstrict-prototypes \ + -Wno-unused -Wno-parentheses -Wno-switch + +DARWININCLUDE = -I$(PDSRCDIR) + +DARWINLDFLAGS = -bundle -undefined suppress -flat_namespace + +%.pd_darwin: %.c + $(CC) $(DARWINCFLAGS) $(DARWININCLUDE) $(DARWINLDFLAGS) -o $*.pd_darwin $*.c + + +# ----------------------- IRIX 5.x ----------------------- +pd_irix5: $(TARGETS) + +SGICFLAGS5 = -o32 $(DEFINES) -DSGI -O2 + +SGIINCLUDE = -I$(PDSRCDIR) + +SGILDFLAGS = -elf -shared -rdata_shared + +%.pd_irix5: %.c + $(CC) $(SGICFLAGS5) $(SGIINCLUDE) -o $*.o -c $*.c + $(LD) $(SGILDFLAGS) -o $*.pd_irix5 $*.o + rm $*.o + + +# ----------------------- IRIX 6.x ----------------------- +pd_irix6: $(TARGETS) + +SGICFLAGS6 = $(DEFINES) -DSGI -n32 \ + -OPT:roundoff=3 -OPT:IEEE_arithmetic=3 -OPT:cray_ivdep=true \ + -Ofast=ip32 + +%.pd_irix6: %.c + $(CC) $(SGICFLAGS6) $(SGIINCLUDE) -o $*.o -c $*.c + $(LD) $(SGILDFLAGS) -o $*.pd_irix6 $*.o + rm $*.o + + +# ----------------------- NT ----------------------- +dll: $(TARGETS) + +PDNTCFLAGS = /W3 /WX /DPD /DNT /D__WIN32__ /DMSW /nologo + +VC="C:\Programme\Microsoft Visual Studio\Vc98" + +PDNTINCLUDE = /I. /I$(PDROOT)\tcl\include /I$(PDSRCDIR)\src /I$(VC)\include + +PDNTLDIR = $(VC)\lib + +PDNTLIB = $(PDNTLDIR)\libc.lib \ + $(PDNTLDIR)\oldnames.lib \ + $(PDNTLDIR)\kernel32.lib \ + $(PDLIBDIR)\pd.lib + +%.dll: %.c + cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c $*.c + link /dll /export:$*_setup $*.obj $(PDNTLIB) + + +pd_nt: $(TARGETS) + +MINGWCFLAGS = $(DEFINES) -O2 -funroll-loops -fomit-frame-pointer \ + -Wall -W -Wshadow -Wstrict-prototypes -Werror \ + -Wno-unused -Wno-parentheses -Wno-switch -mms-bitfields + +MINGWLDFLAGS = -export_dynamic -shared -lm -lkernel32 -lcoldname -lcrtdll -lpd -L$(PDLIBDIR) + +MINGWINCLUDE = -I$(PDSRCDIR) + +%.pd_nt: %.c + $(CC) $(MINGWLDFLAGS) $(MINGWCFLAGS) $(MINGWINCLUDE) -o $*.dll $*.c diff --git a/another.pd b/another.pd new file mode 100644 index 0000000..de28664 --- /dev/null +++ b/another.pd @@ -0,0 +1,7 @@ +#N canvas 1 44 450 300 10; +#X obj 35 50 inlet; +#X obj 35 96 outlet; +#X obj 35 73 * 2; +#X text 141 50 Another patch on the web.; +#X connect 0 0 2 0; +#X connect 2 0 1 0; diff --git a/baseurl.c b/baseurl.c new file mode 100644 index 0000000..a0fce40 --- /dev/null +++ b/baseurl.c @@ -0,0 +1,58 @@ +/* Copyright (c) 2008 Steve Sinclair + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," that comes with Pd. + */ + +/* + * This code provides a canvas with a base URL to inform the baseurl + * where to look. + */ + + +#ifdef __WIN32__ +# define MSW +#endif + +#include "m_pd.h" + +#if (PD_MINOR_VERSION >= 40) + +#include "s_stuff.h" +#include "g_canvas.h" +#include +#include +#include + +#include "baseurl.h" + +typedef void (*t_baseurl_setup)(void); + +/* definitions taken from s_loader.c */ +typedef int (*loader_t)(t_canvas *canvas, char *classname); +void sys_register_loader(loader_t loader); +void class_set_extern_dir(t_symbol *s); + +/* ==================================================== */ + +static t_class *baseurl_class; + +static char *version = "0.0.1"; + +#endif /* PD_MINOR_VERSION>=40 */ + +static void*baseurl_new(t_symbol *s, int argc, t_atom *argv) +{ + t_baseurl *x = (t_baseurl*)pd_new(baseurl_class); + SETSYMBOL(&x->url, argv[0].a_w.w_symbol); + return (x); +} + +void baseurl_setup(void) +{ + post("baseurl %s",version); + post("\twritten by Steve Sinclair"); + post("\tcompiled on "__DATE__" at "__TIME__ " "); + post("\tcompiled against Pd version %d.%d.%d.%s", PD_MAJOR_VERSION, PD_MINOR_VERSION, PD_BUGFIX_VERSION, PD_TEST_VERSION); + + baseurl_class = class_new(gensym("baseurl"), (t_newmethod)baseurl_new, 0, sizeof(t_baseurl), CLASS_NOINLET, A_GIMME, 0); +} diff --git a/baseurl.h b/baseurl.h new file mode 100644 index 0000000..3e76538 --- /dev/null +++ b/baseurl.h @@ -0,0 +1,15 @@ +/* Copyright (c) 2008 Steve Sinclair + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," that comes with Pd. + */ + +#ifndef BASEURL_H +#define BASEURL_H + +typedef struct _baseurl +{ + t_object x_obj; + t_atom url; +} t_baseurl; + +#endif // BASEURL_H diff --git a/md5.c b/md5.c new file mode 100644 index 0000000..87db8f9 --- /dev/null +++ b/md5.c @@ -0,0 +1,452 @@ +/* md5.c - Functions to compute MD5 message digest of files or memory blocks + according to the definition of MD5 in RFC 1321 from April 1992. + Copyright (C) 1995, 1996, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + NOTE: The canonical source of this file is maintained with the GNU C + Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Ulrich Drepper , 1995. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "md5.h" + +#include +#include + +#if USE_UNLOCKED_IO +# include "unlocked-io.h" +#endif + +#ifdef _LIBC +# include +# if __BYTE_ORDER == __BIG_ENDIAN +# define WORDS_BIGENDIAN 1 +# endif +/* We need to keep the namespace clean so define the MD5 function + protected using leading __ . */ +# define md5_init_ctx __md5_init_ctx +# define md5_process_block __md5_process_block +# define md5_process_bytes __md5_process_bytes +# define md5_finish_ctx __md5_finish_ctx +# define md5_read_ctx __md5_read_ctx +# define md5_stream __md5_stream +# define md5_buffer __md5_buffer +#endif + +#ifdef WORDS_BIGENDIAN +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +#else +# define SWAP(n) (n) +#endif + +#define BLOCKSIZE 4096 +#if BLOCKSIZE % 64 != 0 +# error "invalid BLOCKSIZE" +#endif + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +void +md5_init_ctx (struct md5_ctx *ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + +/* Put result from CTX in first 16 bytes following RESBUF. The result + must be in little endian byte order. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +void * +md5_read_ctx (const struct md5_ctx *ctx, void *resbuf) +{ + ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A); + ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B); + ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C); + ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D); + + return resbuf; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +void * +md5_finish_ctx (struct md5_ctx *ctx, void *resbuf) +{ + /* Take yet unprocessed bytes into account. */ + md5_uint32 bytes = ctx->buflen; + size_t pad; + + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; + memcpy (&ctx->buffer[bytes], fillbuf, pad); + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3); + *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) | + (ctx->total[0] >> 29)); + + /* Process last bytes. */ + md5_process_block (ctx->buffer, bytes + pad + 8, ctx); + + return md5_read_ctx (ctx, resbuf); +} + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +int +md5_stream (FILE *stream, void *resblock) +{ + struct md5_ctx ctx; + char buffer[BLOCKSIZE + 72]; + size_t sum; + + /* Initialize the computation context. */ + md5_init_ctx (&ctx); + + /* Iterate over full file contents. */ + while (1) + { + /* We read the file in blocks of BLOCKSIZE bytes. One call of the + computation function processes the whole buffer so that with the + next round of the loop another block can be read. */ + size_t n; + sum = 0; + + /* Read block. Take care for partial reads. */ + while (1) + { + n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); + + sum += n; + + if (sum == BLOCKSIZE) + break; + + if (n == 0) + { + /* Check for the error flag IFF N == 0, so that we don't + exit the loop after a partial read due to e.g., EAGAIN + or EWOULDBLOCK. */ + if (ferror (stream)) + return 1; + goto process_partial_block; + } + + /* We've read at least one byte, so ignore errors. But always + check for EOF, since feof may be true even though N > 0. + Otherwise, we could end up calling fread after EOF. */ + if (feof (stream)) + goto process_partial_block; + } + + /* Process buffer with BLOCKSIZE bytes. Note that + BLOCKSIZE % 64 == 0 + */ + md5_process_block (buffer, BLOCKSIZE, &ctx); + } + + process_partial_block:; + + /* Process any remaining bytes. */ + if (sum > 0) + md5_process_bytes (buffer, sum, &ctx); + + /* Construct result in desired memory. */ + md5_finish_ctx (&ctx, resblock); + return 0; +} + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +void * +md5_buffer (const char *buffer, size_t len, void *resblock) +{ + struct md5_ctx ctx; + + /* Initialize the computation context. */ + md5_init_ctx (&ctx); + + /* Process whole buffer but last len % 64 bytes. */ + md5_process_bytes (buffer, len, &ctx); + + /* Put result in desired memory area. */ + return md5_finish_ctx (&ctx, resblock); +} + + +void +md5_process_bytes (const void *buffer, size_t len, struct md5_ctx *ctx) +{ + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) + { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + memcpy (&ctx->buffer[left_over], buffer, add); + ctx->buflen += add; + + if (ctx->buflen > 64) + { + md5_process_block (ctx->buffer, ctx->buflen & ~63, ctx); + + ctx->buflen &= 63; + /* The regions in the following copy operation cannot overlap. */ + memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], + ctx->buflen); + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len >= 64) + { +#if !_STRING_ARCH_unaligned +# define alignof(type) offsetof (struct { char c; type x; }, x) +# define UNALIGNED_P(p) (((size_t) p) % alignof (md5_uint32) != 0) + if (UNALIGNED_P (buffer)) + while (len > 64) + { + md5_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); + buffer = (const char *) buffer + 64; + len -= 64; + } + else +#endif + { + md5_process_block (buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + } + + /* Move remaining bytes in internal buffer. */ + if (len > 0) + { + size_t left_over = ctx->buflen; + + memcpy (&ctx->buffer[left_over], buffer, len); + left_over += len; + if (left_over >= 64) + { + md5_process_block (ctx->buffer, 64, ctx); + left_over -= 64; + memcpy (ctx->buffer, &ctx->buffer[64], left_over); + } + ctx->buflen = left_over; + } +} + + +/* These are the four functions used in the four steps of the MD5 algorithm + and defined in the RFC 1321. The first function is a little bit optimized + (as found in Colin Plumbs public domain implementation). */ +/* #define FF(b, c, d) ((b & c) | (~b & d)) */ +#define FF(b, c, d) (d ^ (b & (c ^ d))) +#define FG(b, c, d) FF (d, b, c) +#define FH(b, c, d) (b ^ c ^ d) +#define FI(b, c, d) (c ^ (b | ~d)) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. */ + +void +md5_process_block (const void *buffer, size_t len, struct md5_ctx *ctx) +{ + md5_uint32 correct_words[16]; + const md5_uint32 *words = buffer; + size_t nwords = len / sizeof (md5_uint32); + const md5_uint32 *endp = words + nwords; + md5_uint32 A = ctx->A; + md5_uint32 B = ctx->B; + md5_uint32 C = ctx->C; + md5_uint32 D = ctx->D; + + /* First increment the byte count. RFC 1321 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < len) + ++ctx->total[1]; + + /* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + while (words < endp) + { + md5_uint32 *cwp = correct_words; + md5_uint32 A_save = A; + md5_uint32 B_save = B; + md5_uint32 C_save = C; + md5_uint32 D_save = D; + + /* First round: using the given function, the context and a constant + the next context is computed. Because the algorithms processing + unit is a 32-bit word and it is determined to work on words in + little endian byte order we perhaps have to change the byte order + before the computation. To reduce the work for the next steps + we store the swapped words in the array CORRECT_WORDS. */ + +#define OP(a, b, c, d, s, T) \ + do \ + { \ + a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ + ++words; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* It is unfortunate that C does not provide an operator for + cyclic rotation. Hope the C compiler is smart enough. */ +#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + + /* Before we start, one word to the strange constants. + They are defined in RFC 1321 as + + T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 + + Here is an equivalent invocation using Perl: + + perl -e 'foreach(1..64){printf "0x%08x\n", int (4294967296 * abs (sin $_))}' + */ + + /* Round 1. */ + OP (A, B, C, D, 7, 0xd76aa478); + OP (D, A, B, C, 12, 0xe8c7b756); + OP (C, D, A, B, 17, 0x242070db); + OP (B, C, D, A, 22, 0xc1bdceee); + OP (A, B, C, D, 7, 0xf57c0faf); + OP (D, A, B, C, 12, 0x4787c62a); + OP (C, D, A, B, 17, 0xa8304613); + OP (B, C, D, A, 22, 0xfd469501); + OP (A, B, C, D, 7, 0x698098d8); + OP (D, A, B, C, 12, 0x8b44f7af); + OP (C, D, A, B, 17, 0xffff5bb1); + OP (B, C, D, A, 22, 0x895cd7be); + OP (A, B, C, D, 7, 0x6b901122); + OP (D, A, B, C, 12, 0xfd987193); + OP (C, D, A, B, 17, 0xa679438e); + OP (B, C, D, A, 22, 0x49b40821); + + /* For the second to fourth round we have the possibly swapped words + in CORRECT_WORDS. Redefine the macro to take an additional first + argument specifying the function to use. */ +#undef OP +#define OP(f, a, b, c, d, k, s, T) \ + do \ + { \ + a += f (b, c, d) + correct_words[k] + T; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* Round 2. */ + OP (FG, A, B, C, D, 1, 5, 0xf61e2562); + OP (FG, D, A, B, C, 6, 9, 0xc040b340); + OP (FG, C, D, A, B, 11, 14, 0x265e5a51); + OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP (FG, A, B, C, D, 5, 5, 0xd62f105d); + OP (FG, D, A, B, C, 10, 9, 0x02441453); + OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP (FG, D, A, B, C, 14, 9, 0xc33707d6); + OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP (FG, B, C, D, A, 8, 20, 0x455a14ed); + OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP (FG, C, D, A, B, 7, 14, 0x676f02d9); + OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); + + /* Round 3. */ + OP (FH, A, B, C, D, 5, 4, 0xfffa3942); + OP (FH, D, A, B, C, 8, 11, 0x8771f681); + OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP (FH, B, C, D, A, 14, 23, 0xfde5380c); + OP (FH, A, B, C, D, 1, 4, 0xa4beea44); + OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP (FH, B, C, D, A, 6, 23, 0x04881d05); + OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); + + /* Round 4. */ + OP (FI, A, B, C, D, 0, 6, 0xf4292244); + OP (FI, D, A, B, C, 7, 10, 0x432aff97); + OP (FI, C, D, A, B, 14, 15, 0xab9423a7); + OP (FI, B, C, D, A, 5, 21, 0xfc93a039); + OP (FI, A, B, C, D, 12, 6, 0x655b59c3); + OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP (FI, C, D, A, B, 10, 15, 0xffeff47d); + OP (FI, B, C, D, A, 1, 21, 0x85845dd1); + OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP (FI, C, D, A, B, 6, 15, 0xa3014314); + OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP (FI, A, B, C, D, 4, 6, 0xf7537e82); + OP (FI, D, A, B, C, 11, 10, 0xbd3af235); + OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP (FI, B, C, D, A, 9, 21, 0xeb86d391); + + /* Add the starting values of the context. */ + A += A_save; + B += B_save; + C += C_save; + D += D_save; + } + + /* Put checksum in context given as argument. */ + ctx->A = A; + ctx->B = B; + ctx->C = C; + ctx->D = D; +} diff --git a/md5.h b/md5.h new file mode 100644 index 0000000..1c29e67 --- /dev/null +++ b/md5.h @@ -0,0 +1,136 @@ +/* Declaration of functions and data types used for MD5 sum computing + library functions. + Copyright (C) 1995-1997,1999-2005 Free Software Foundation, Inc. + + NOTE: The canonical source of this file is maintained with the GNU C + Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _MD5_H +#define _MD5_H 1 + +#include + +#if HAVE_INTTYPES_H +# include +#endif +#if HAVE_STDINT_H || _LIBC +# include +#endif + +#ifndef __GNUC_PREREQ +# if defined __GNUC__ && defined __GNUC_MINOR__ +# define __GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +# else +# define __GNUC_PREREQ(maj, min) 0 +# endif +#endif + +#ifndef __THROW +# if defined __cplusplus && __GNUC_PREREQ (2,8) +# define __THROW throw () +# else +# define __THROW +# endif +#endif + +#ifndef __attribute__ +# if ! __GNUC_PREREQ (2,8) || __STRICT_ANSI__ +# define __attribute__(x) +# endif +#endif + +#ifndef _LIBC +# define __md5_buffer md5_buffer +# define __md5_finish_ctx md5_finish_ctx +# define __md5_init_ctx md5_init_ctx +# define __md5_process_block md5_process_block +# define __md5_process_bytes md5_process_bytes +# define __md5_read_ctx md5_read_ctx +# define __md5_stream md5_stream +#endif + +typedef uint32_t md5_uint32; + +/* Structure to save state of computation between the single steps. */ +struct md5_ctx +{ + md5_uint32 A; + md5_uint32 B; + md5_uint32 C; + md5_uint32 D; + + md5_uint32 total[2]; + md5_uint32 buflen; + char buffer[128] __attribute__ ((__aligned__ (__alignof__ (md5_uint32)))); +}; + +/* + * The following three functions are build up the low level used in + * the functions `md5_stream' and `md5_buffer'. + */ + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +extern void __md5_init_ctx (struct md5_ctx *ctx) __THROW; + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is necessary that LEN is a multiple of 64!!! */ +extern void __md5_process_block (const void *buffer, size_t len, + struct md5_ctx *ctx) __THROW; + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is NOT required that LEN is a multiple of 64. */ +extern void __md5_process_bytes (const void *buffer, size_t len, + struct md5_ctx *ctx) __THROW; + +/* Process the remaining bytes in the buffer and put result from CTX + in first 16 bytes following RESBUF. The result is always in little + endian byte order, so that a byte-wise output yields to the wanted + ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF be correctly + aligned for a 32 bits value. */ +extern void *__md5_finish_ctx (struct md5_ctx *ctx, void *resbuf) __THROW; + + +/* Put result from CTX in first 16 bytes following RESBUF. The result is + always in little endian byte order, so that a byte-wise output yields + to the wanted ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +extern void *__md5_read_ctx (const struct md5_ctx *ctx, void *resbuf) __THROW; + + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +extern int __md5_stream (FILE *stream, void *resblock) __THROW; + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +extern void *__md5_buffer (const char *buffer, size_t len, + void *resblock) __THROW; + +#endif /* md5.h */ diff --git a/pd.index b/pd.index new file mode 100644 index 0000000..8d0a998 --- /dev/null +++ b/pd.index @@ -0,0 +1,2 @@ +4a3a52f1d881ce91a6e8ba09671a6852 another.pd +b9faccc63257b5ced1f91f21ed8adfa3 unknown.pd diff --git a/test.pd b/test.pd new file mode 100644 index 0000000..68bb65a --- /dev/null +++ b/test.pd @@ -0,0 +1,13 @@ +#N canvas 592 253 450 300 10; +#X text 28 24 Test for URL loader; +#X obj 28 56 baseurl http://localhost/pd; +#X obj 32 140 unknown; +#X floatatom 32 111 5 0 0 0 - - -; +#X floatatom 32 171 5 0 0 0 - - -; +#X obj 135 139 another; +#X floatatom 135 112 5 0 0 0 - - -; +#X floatatom 135 171 5 0 0 0 - - -; +#X connect 2 0 4 0; +#X connect 3 0 2 0; +#X connect 5 0 7 0; +#X connect 6 0 5 0; diff --git a/unknown.pd b/unknown.pd new file mode 100644 index 0000000..b25cfd1 --- /dev/null +++ b/unknown.pd @@ -0,0 +1,5 @@ +#N canvas 1 44 450 300 10; +#X text 46 35 This is a PD patch.; +#X obj 210 36 inlet; +#X obj 210 89 outlet; +#X connect 1 0 2 0; diff --git a/urlloader.c b/urlloader.c new file mode 100644 index 0000000..ecf3ddc --- /dev/null +++ b/urlloader.c @@ -0,0 +1,509 @@ +/* Copyright (c) 2008 Steve Sinclair + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," that comes with Pd. + */ + +/* + * this code adds an external "loader" to Miller S. Puckette's "pure data", + * which allows the loading of libraries/externals from internet URLs + * + * the infrastructure of this file is based on hcsteiner's "libdir" loader + */ + + +#ifdef __WIN32__ +# define MSW +#endif + +#include "m_pd.h" + +#if (PD_MINOR_VERSION >= 40) + +#include "s_stuff.h" +#include "g_canvas.h" +#include +#include +#include +#include +#include + +#include "baseurl.h" +#include + +#include +#include "md5.h" +#include "md5.c" + +typedef void (*t_urlloader_setup)(void); + +/* definitions taken from s_loader.c */ +typedef int (*loader_t)(t_canvas *canvas, char *classname); +void sys_register_loader(loader_t loader); +void class_set_extern_dir(t_symbol *s); + +/* taken from m_class.c */ +int pd_setloadingabstraction(t_symbol *sym); +void canvas_popabstraction(t_canvas *x); + +/* ==================================================== */ + +#define CACHE_TIMEOUT_SECONDS 100 + +typedef struct _urlloader +{ + t_object x_obj; +} t_urlloader; +static t_class *urlloader_class; + +static char *version = "0.0.1"; + +typedef struct _list +{ + struct _list *next; +} t_list; + +typedef struct _url_object +{ + t_list list; + t_symbol *name; + char md5[16]; +} t_url_object; + +typedef struct _url +{ + t_list list; + t_symbol *url; + t_url_object *objects; +} t_url; +static t_url *baseurl_list = NULL; + +char cachedir[MAXPDSTRING]=""; +#define FILEPERM (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) +#define DIRPERM (FILEPERM|S_IXUSR|S_IXGRP|S_IXOTH) +char *get_cache_dir(void) +{ + if (cachedir[0]==0) { + strcpy(cachedir, getenv("HOME")); + strcat(cachedir, "/.pd/cache"); + } + + return cachedir; +} + +FILE* create_and_open(const char *filename, const char *mode) +{ + char str[MAXPDSTRING], *ss=str; + const char *sf=filename; + struct stat st; + while (*sf) { + do { + *ss = *sf; + ss++; + sf++; + } while (*sf && *sf != '/'); + *ss = 0; + + if (stat(str, &st)<0) { + if (*sf) { + if (mkdir(str, DIRPERM)<0) { + post("[urlloader] Error creating %s", str); + return 0; + } + } + else + return fopen(str, mode); + } + else if (!*sf) + return fopen(str, mode); + } + + return 0; +} + +/** Return the full path to a filename in the cache */ +void cache_path(char path[MAXPDSTRING], t_url *u, const char *filename) +{ + char *s; + strncpy(path, get_cache_dir(), MAXPDSTRING); +#ifdef WIN32 + strncat(path, "\\", MAXPDSTRING); +#else + strncat(path, "/", MAXPDSTRING); +#endif + s = path + strlen(path); + strncat(path, u->url->s_name, MAXPDSTRING); + while (*s) { + if (*s == '/') *s = '_'; + s++; + } +#ifdef WIN32 + strncat(path, "\\", MAXPDSTRING); +#else + strncat(path, "/", MAXPDSTRING); +#endif + strncat(path, filename, MAXPDSTRING); +} + +/** Open a file from the cache or download it first if needed. */ +FILE* download_and_open_from_cache(t_url *u, const char *filename, const char *mode) +{ + char str_url[MAXPDSTRING]; + char cache_name[MAXPDSTRING]; + struct stat st; + + cache_path(cache_name, u, filename); + + if (stat(cache_name, &st)<0 + || ((time(NULL)-st.st_mtime) > CACHE_TIMEOUT_SECONDS)) + { + CURL *c = curl_easy_init(); + FILE *f=0; + if (!c) { + post("[urlloader] Error initializing curl."); + return 0; + } + + f = create_and_open(cache_name, "wb"); + if (!f) { + post("[urlloader] Error opening %s for write.", cache_name); + return 0; + } + + /* Download the index file */ + snprintf(str_url, MAXPDSTRING, "%s/%s", u->url->s_name, filename); + curl_easy_setopt(c, CURLOPT_URL, str_url); + curl_easy_setopt(c, CURLOPT_WRITEDATA, f); + if (curl_easy_perform(c)) { + post("[urlloader] Error downloading %s", str_url); + } + curl_easy_cleanup(c); + + post("[urlloader] Downloaded %s to %s", str_url, cache_name); + + fclose(f); + } + + if (mode) + return fopen(cache_name, mode); + else + // indicate success without opening the file + return (FILE*)1; +} + +/** Append an item to a struct beginning with a t_list. + * If allowdup is false (0), second item must be a t_symbol*. + * If duplicate, returns the item without modifying it. */ +void *list_append(void *list, void *item, int size, int allowdup) +{ + t_list** l = list; + t_symbol *s1, *s2; + if (!*l) { + *l = getbytes(size); + } + else { + while (*l) { + if (!allowdup) { + s1 = *(t_symbol**)(((char*)*l)+sizeof(t_list)); + s2 = *(t_symbol**)(((char*)item)+sizeof(t_list)); + if (s1==s2 || strcmp(s1->s_name, s2->s_name)==0) + return *l; + } + if (!(*l)->next) + break; + l = &(*l)->next; + } + (*l)->next = getbytes(size); + l = &(*l)->next; + } + + if (*l) { + memcpy(*l, item, size); + (*l)->next = 0; + } + + return *l; +} + +t_url *url_add(t_atom *url) +{ + t_url u; + u.url = url->a_w.w_symbol; + u.objects = 0; + return list_append(&baseurl_list, &u, sizeof(t_url), 0); +} + +void url_parse_index_line(t_url *u, char* line) +{ + char *s = line, *name, hex[3]; + int i=0; + t_url_object o; + while ((*s==' ' || *s=='\t') && *s) {s++;} /* skip whitespace */ + if (!*s) return; + while (!(*s==' ' || *s=='\t') && *s) { /* remember MD5 sum */ + hex[0] = *s; s++; + hex[1] = *s; s++; + hex[2] = 0; + o.md5[i++] = strtol(hex, 0, 16); + } + if (!*s) return; + while ((*s==' ' || *s=='\t') && *s) {s++;} /* skip whitespace */ + if (!*s) return; + name = s; + while (!(*s==' ' || *s=='\t') && *s) {s++;} /* skip md5sum for now */ + *s = 0; + + o.name = gensym(name); + list_append(&u->objects, &o, sizeof(t_url_object), 0); +} + +/** Download index for a given URL. */ +int url_update_index(t_url *u) +{ + char buffer[MAXPDSTRING], *line=buffer; + int bufpos=0; + + // open and read the downloaded or cached file. + FILE *f = download_and_open_from_cache(u, "pd.index", "rb"); + if (!f) { + post("[urlloader] Error opening index for %s", u->url->s_name); + return -1; + } + + post("[urlloader] Index for %s opened.", u->url->s_name); + while (!feof(f)) { + int sz = fread(buffer+bufpos, 1, 256, f); + int end = bufpos + sz; + while (bufpos < end) { + while (buffer[bufpos]!='\n' && buffer[bufpos]!='\r' + && bufpos < end) + ++bufpos; + if (bufpos < end || feof(f)) { + buffer[bufpos] = 0; + url_parse_index_line(u, line); + ++bufpos; + line = buffer+bufpos; + } + } + } + + fclose(f); + return 0; +} + +/** Download any indexes that are missing. */ +static int cache_update_index(void) +{ + t_url *u; + int rc; + + u = baseurl_list; + while (u) { + rc = 0; + if (u->objects == NULL) + rc = url_update_index(u); + if (rc) { + post("[urlloader] Error updating index for %s", u->url->s_name); + return rc; + } + u = (t_url*)u->list.next; + } + + return 0; +} + +/** Search all objects in the canvas and its owner for baseurl objects. + * Add the found object URLs to the global list of t_urls. + */ +static void canvas_find_baseurls(t_canvas *canvas) +{ + t_gobj *o = canvas->gl_list; + while (o) { + if (strcmp(class_getname(o->g_pd),"baseurl")==0) + url_add(&((t_baseurl*)o)->url); + o = o->g_next; + } + if (canvas->gl_owner) + canvas_find_baseurls(canvas->gl_owner); +} + +/** Find a given class name in the global t_url list. */ +static t_url* url_find_class(char *classname, t_url_object **object) +{ + t_url *u = baseurl_list; + t_url_object *o; + while (u) { + o = u->objects; + while (o) { + char *ext = strstr(o->name->s_name, ".pd"); + if (!ext) continue; + if (strncmp(o->name->s_name, classname, + ext - o->name->s_name)==0) + break; + o = (t_url_object*)o->list.next; + } + if (o) break; + u = (t_url*)u->list.next; + } + if (object) *object = o; + return u; +} + +/** Load an abstraction specified by an absolute path name. */ +int canvas_load_from_absolute_path(t_canvas *canvas, t_symbol *classname, + char *path, char *ext) +{ + char dirbuf[MAXPDSTRING], *nameptr; + t_pd *current = s__X.s_thing; + int fd=-1; + + fd = canvas_open(canvas, path, ext, + dirbuf, &nameptr, MAXPDSTRING, 0); + + if (fd < 0) + return 0; + + close(fd); + if (!pd_setloadingabstraction(classname)) { + canvas_setargs(0, 0); /* TODO argc, argv */ + binbuf_evalfile(gensym(nameptr), gensym(dirbuf)); + if (s__X.s_thing != current) + canvas_popabstraction((t_canvas *)(s__X.s_thing)); + canvas_setargs(0, 0); + } + else { + error("%s: can't load abstraction within itself\n", classname); + return 0; + } + + return 1; +} + +/** + * the loader + * + * @param canvas the context of the object to be created + * @param classname the name of the object (external, library) to be created + * @return 1 on success, 0 on failure + */ +static int urlloader_loader(t_canvas *canvas, char *classname) +{ + int result=0; + t_url *u; + t_url_object *o; + char class_filename[MAXPDSTRING]; + char class_cachepath[MAXPDSTRING]; + char tmpstr[MAXPDSTRING]; + FILE *f; + + static int already_loading=0; + if(already_loading)return 0; + already_loading=1; + + /* loader */ + post("[urlloader] Asked to load %s for canvas %s", classname, canvas->gl_name->s_name); + canvas_find_baseurls(canvas); + cache_update_index(); + + post("[urlloader] Known URLs:"); + u = baseurl_list; + while (u) { + post("[urlloader] %s", u->url->s_name); + o = u->objects; + while (o) { + post("[urlloader] - %s", o->name->s_name); + o = (t_url_object*)o->list.next; + } + u = (t_url*)u->list.next; + } + + u = url_find_class(classname, &o); + if (!(u && o)) { + already_loading=0; + return result; + } + + post("[urlloader] Found: %s.pd at %s", classname, u->url->s_name); + + snprintf(class_filename, MAXPDSTRING, "%s.pd", classname); + if (f=download_and_open_from_cache(u, class_filename, "rb")) + { + char resblock[16]; + int okay=1, i; + if (md5_stream(f, resblock)) { + post("[urlloader] Error generating MD5 sum for %s", class_filename); + result = 0; + already_loading = 0; + fclose(f); + return result; + } + fclose(f); + + for (i=0; i<16; i++) + okay &= (resblock[i]==o->md5[i]); + if (!okay) { + post("[urlloader] MD5 error for %s", class_filename); + result = 0; + already_loading = 0; + return result; + } + + // download GPG signature + snprintf(tmpstr, MAXPDSTRING, "%s.asc", class_filename); + if (!download_and_open_from_cache(u, tmpstr, 0)) { + post("[urlloader] Error downloading GPG signature for %s", + class_filename); + result = 0; + already_loading = 0; + return result; + } + + // check GPG signature + cache_path(class_cachepath, u, classname); + snprintf(tmpstr, MAXPDSTRING, "gpg --verify %s.pd.asc %s.pd", + class_cachepath, class_cachepath); + if (system(tmpstr)) { + post("[urlloader] Error verifying GPG signature for %s", + class_filename); + result = 0; + already_loading = 0; + return result; + } + + + // load pd patch + canvas_load_from_absolute_path(canvas, gensym(classname), + class_cachepath, ".pd"); + result = 1; + } + + already_loading=0; + return result; +} + +#endif /* PD_MINOR_VERSION>=40 */ + +static void*urlloader_new(t_symbol *s, int argc, t_atom *argv) +{ + t_urlloader*x = (t_urlloader*)pd_new(urlloader_class); + return (x); +} + +void urlloader_setup(void) +{ + /* relies on t.grill's loader functionality, fully added in 0.40 */ + post("url loader %s",version); + post("\twritten by Steve Sinclair"); + post("\tcompiled on "__DATE__" at "__TIME__ " "); + post("\tcompiled against Pd version %d.%d.%d.%s", PD_MAJOR_VERSION, PD_MINOR_VERSION, PD_BUGFIX_VERSION, PD_TEST_VERSION); + + curl_global_init(CURL_GLOBAL_ALL); + +#if (PD_MINOR_VERSION >= 40) + sys_register_loader(urlloader_loader); +#else + error("to function, this needs to be compiled against Pd 0.40 or higher,\n"); + error("\tor a version that has sys_register_loader()"); +#endif + + urlloader_class = class_new(gensym("urlloader"), (t_newmethod)urlloader_new, 0, sizeof(t_urlloader), CLASS_NOINLET, A_GIMME, 0); +} -- cgit v1.2.1