aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIOhannes m zmölnig <zmoelnig@users.sourceforge.net>2008-09-03 09:34:50 +0000
committerIOhannes m zmölnig <zmoelnig@users.sourceforge.net>2008-09-03 09:34:50 +0000
commit22c887bcf77c0b40aaeffbe18a8a5afcc77b03e3 (patch)
tree4af1fb55de511b6e371ac93561ca8cc573218ae4
added stephen sinclairs "urloader"svn2git-root
svn path=/trunk/externals/loaders/urloader/; revision=10275
-rw-r--r--Makefile167
-rw-r--r--another.pd7
-rw-r--r--baseurl.c58
-rw-r--r--baseurl.h15
-rw-r--r--md5.c452
-rw-r--r--md5.h136
-rw-r--r--pd.index2
-rw-r--r--test.pd13
-rw-r--r--unknown.pd5
-rw-r--r--urlloader.c509
10 files changed, 1364 insertions, 0 deletions
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 <radarsat1@gmail.com>
+ * 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 <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#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 <radarsat1@gmail.com>
+ * 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 <drepper@gnu.ai.mit.edu>, 1995. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "md5.h"
+
+#include <stddef.h>
+#include <string.h>
+
+#if USE_UNLOCKED_IO
+# include "unlocked-io.h"
+#endif
+
+#ifdef _LIBC
+# include <endian.h>
+# 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 <stdio.h>
+
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#if HAVE_STDINT_H || _LIBC
+# include <stdint.h>
+#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 <radarsat1@gmail.com>
+ * 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 <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include "baseurl.h"
+#include <curl/curl.h>
+
+#include <stdint.h>
+#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);
+}