diff --git a/web/root/gallery_ex/README b/web/root/gallery_ex/README
new file mode 100644
index 0000000000000000000000000000000000000000..3ef9d6e1c336dfdb5660b7c410a7813db766bcec
--- /dev/null
+++ b/web/root/gallery_ex/README
@@ -0,0 +1,10 @@
+This is a sample set of simple photo gallery scripts.  This also serves as and
+example of usage of webctrl.ini for custom error pages.
+
+The pics directory contains subdirectories with .jpg files.
+
+When the gallery is initially viewed, the custom 404 error handler in the thumbs
+directory will generate a thumbnail for each picture assuming that ImageMagicks
+convert utility is installed and is in your path.
+
+Remove the access.ars file to allow public access to this dir.
diff --git a/web/root/gallery_ex/access.ars b/web/root/gallery_ex/access.ars
new file mode 100644
index 0000000000000000000000000000000000000000..7b4d8393bf65417b0dcfe91eb57ee4b3d1f85563
--- /dev/null
+++ b/web/root/gallery_ex/access.ars
@@ -0,0 +1 @@
+LEVEL 90
diff --git a/web/root/gallery_ex/blank.html b/web/root/gallery_ex/blank.html
new file mode 100644
index 0000000000000000000000000000000000000000..18ecdcb795c33d6ab7bbb43f647947defca5634d
--- /dev/null
+++ b/web/root/gallery_ex/blank.html
@@ -0,0 +1 @@
+<html></html>
diff --git a/web/root/gallery_ex/gallery_bottom.ssjs b/web/root/gallery_ex/gallery_bottom.ssjs
new file mode 100644
index 0000000000000000000000000000000000000000..bfea4139b3b26937b57aa2d78cfe505cd9d3ebac
--- /dev/null
+++ b/web/root/gallery_ex/gallery_bottom.ssjs
@@ -0,0 +1,11 @@
+var picspath=web_root_dir+'/gallery_ex/pics';
+
+writeln('<html><head><title>'+http_request.query['gallery']+' gallery</title></head>');
+writeln('<body>');
+var pics=directory(picspath+'/'+http_request.query['gallery']+'/*.jpg');
+for(index in pics) {
+	var filename=pics[index];
+	filename=filename.replace(/.*\//,'');
+	writeln('<a target="image" href="pics/'+http_request.query['gallery']+'/'+filename+'"><img border="0" src="thumbs/'+http_request.query['gallery']+'/'+filename+'"></a> ');
+}
+writeln('</body></html>');
diff --git a/web/root/gallery_ex/gallery_index.ssjs b/web/root/gallery_ex/gallery_index.ssjs
new file mode 100644
index 0000000000000000000000000000000000000000..f3b139a27a56f2440862c8b1977cca4defc5f311
--- /dev/null
+++ b/web/root/gallery_ex/gallery_index.ssjs
@@ -0,0 +1,14 @@
+var picspath=web_root_dir+'/gallery_ex/pics';
+
+writeln("<html><head><title>Image Gallery List</title></head>");
+writeln("<body>");
+var galleries=new Array();
+galleries=directory(picspath+'/*');
+for(gallery in galleries) {
+	var name=galleries[gallery];
+	name=name.replace(/.*\/(.*)\//,'$1');
+	var pics=new Array();
+	pics=directory(picspath+'/'+name+'/*.jpg');
+	writeln('<p><a href="gallery_bottom.ssjs?gallery='+name+'" target="bottom">'+name+'</a><br>'+strftime("%m/%d/%Y %H:%M:%S",file_date(picspath+'/'+name))+'<br>'+(pics.length)+' pictures</p>');
+}
+write('</table></body></html>');
diff --git a/web/root/gallery_ex/index.ssjs b/web/root/gallery_ex/index.ssjs
new file mode 100644
index 0000000000000000000000000000000000000000..3326ed43e37bb5311707ae30e531549e43a7c285
--- /dev/null
+++ b/web/root/gallery_ex/index.ssjs
@@ -0,0 +1,23 @@
+var picspath=web_root_dir+'/gallery_ex/pics';
+var gallery=http_request.query['gallery'];
+
+if(gallery==undefined) {
+	writeln("<html><head><title>Synchronet Galleries"+picspath+"</title></head>");
+}
+else {
+	writeln('<html><head><title>'+gallery+'</title></head>');
+}
+writeln('<frameset cols="*,200" style="margin: 0;" marginheight="0" marginwidth="0" border="0" framespacing="0">');
+writeln('  <frameset rows="*,110" style="margin: 0;" marginheight="0" marginwidth="0" border="0" framespacing="0">');
+writeln('  <frame name="image" src="blank.html" marginheight="0" marginwidth="0" border="0" framespacing="0">');
+if(gallery==undefined) {
+	writeln('  <frame name="bottom" src="blank.html" marginheight="0" marginwidth="0" border="0" framespacing="0">');
+}
+else {
+	writeln('  <frame name="bottom" src="gallery_bottom.ssjs?gallery='+gallery+'" marginheight="0" marginwidth="0" border="0" framespacing="0">');
+}
+writeln(' </frameset>');
+writeln(' <frame name="gallery_index" src="gallery_index.ssjs" marginheight="0" marginwidth="0" border="0" framespacing="0">');
+writeln('</frameset>');
+writeln('<body>Frames required</body>');
+writeln('</html>');
diff --git a/web/root/gallery_ex/thumbs/404.ssjs b/web/root/gallery_ex/thumbs/404.ssjs
new file mode 100644
index 0000000000000000000000000000000000000000..995bd74fa38ec620eaaf8f78c78581ecb6770ed5
--- /dev/null
+++ b/web/root/gallery_ex/thumbs/404.ssjs
@@ -0,0 +1,31 @@
+var picspath=web_root_dir+'/gallery_ex/pics';
+var thumbpath=web_root_dir+'/gallery_ex/thumbs';
+
+var gallery=http_request.real_path;
+gallery=gallery.replace(/^.*\/([^\/]*?)\/[^\/]*$/,"$1");
+var filename=http_request.real_path;
+filename=filename.replace(/.*\//,'');
+
+if(!file_isdir(thumbpath+'/'+gallery)) {
+	mkdir(thumbpath+'/'+gallery);
+}
+var fullthumb=thumbpath+'/'+gallery+'/'+filename
+if(!file_exists(fullthumb)) {
+	var fullpic=picspath+'/'+gallery+'/'+filename
+	if(file_exists(fullpic)) {
+		system.exec('convert '+fullpic+' -thumbnail 120 '+fullthumb);
+	}
+}
+
+if(file_exists(fullthumb)) {
+	http_reply.status="200 Ok";
+	http_reply.header['Content-Type']='image/jpeg';
+	var f=new File(fullthumb);
+	f.open('r',true);
+	write(f.read());
+	f.close();
+}
+else {
+	http_reply.status="404 Not Found";
+	write("<html><head><title>404 Error</title></head><body>404 Not Found</body></html>");
+}
diff --git a/web/root/gallery_ex/thumbs/webctrl.ini b/web/root/gallery_ex/thumbs/webctrl.ini
new file mode 100644
index 0000000000000000000000000000000000000000..f7e802d395a75c2b6057cb80774f0f5b320174b1
--- /dev/null
+++ b/web/root/gallery_ex/thumbs/webctrl.ini
@@ -0,0 +1,2 @@
+[*.jpg]
+ErrorDirectory=gallery_ex/thumbs