From c0bc739ded3a4f9af334ac07a18ea5a85036d896 Mon Sep 17 00:00:00 2001 From: Tim Burks Date: Mon, 28 Jun 2010 22:35:34 -0700 Subject: [PATCH] ObjC rewrite. --- Nukefile | 9 +- Xcode/English.lproj/InfoPlist.strings | 2 + Xcode/Info.plist | 28 ++ Xcode/Nunja.xcodeproj/project.pbxproj | 459 ++++++++++++++++++++ Xcode/Nunja_Prefix.pch | 0 Xcode/nunjad.m | 36 ++ nu/markup.nu | 56 --- nu/nunja.nu | 375 ++++------------ nunjad.nu => nunjad | 14 +- nunjad/nunjad | Bin 45856 -> 0 bytes nunjad/nunjad.1 | 79 ---- nunjad/nunjad.m | 61 --- nunjad/nunjad.xcodeproj/project.pbxproj | 225 ---------- objc/NSFileManager_Nunja.h | 23 + objc/NSFileManager_Nunja.m | 33 ++ objc/Nunja.h | 66 +++ objc/Nunja.m | 363 ++++++++++++++++ objc/NunjaDelegate.h | 23 + objc/NunjaDelegate.m | 170 ++++++++ objc/NunjaRequest.h | 60 +++ objc/NunjaRequest.m | 289 +++++++++++++ objc/NunjaRequestHandler.h | 20 + objc/NunjaRequestHandler.m | 91 ++++ objc/NunjaRequestRouter.h | 18 + objc/NunjaRequestRouter.m | 85 ++++ objc/{util.m => Nunja_daemonize.m} | 20 +- objc/markup.m | 96 ----- objc/nunja.h | 6 - objc/nunja.m | 548 ------------------------ objc/passwd.m | 182 -------- sample/site.nu | 105 +++-- test/test_markup.nu | 19 - test/test_salt.nu | 15 - 33 files changed, 1934 insertions(+), 1642 deletions(-) create mode 100644 Xcode/English.lproj/InfoPlist.strings create mode 100644 Xcode/Info.plist create mode 100644 Xcode/Nunja.xcodeproj/project.pbxproj create mode 100644 Xcode/Nunja_Prefix.pch create mode 100644 Xcode/nunjad.m delete mode 100644 nu/markup.nu rename nunjad.nu => nunjad (90%) delete mode 100755 nunjad/nunjad delete mode 100644 nunjad/nunjad.1 delete mode 100644 nunjad/nunjad.m delete mode 100644 nunjad/nunjad.xcodeproj/project.pbxproj create mode 100644 objc/NSFileManager_Nunja.h create mode 100644 objc/NSFileManager_Nunja.m create mode 100644 objc/Nunja.h create mode 100644 objc/Nunja.m create mode 100644 objc/NunjaDelegate.h create mode 100644 objc/NunjaDelegate.m create mode 100644 objc/NunjaRequest.h create mode 100644 objc/NunjaRequest.m create mode 100644 objc/NunjaRequestHandler.h create mode 100644 objc/NunjaRequestHandler.m create mode 100644 objc/NunjaRequestRouter.h create mode 100644 objc/NunjaRequestRouter.m rename objc/{util.m => Nunja_daemonize.m} (85%) delete mode 100644 objc/markup.m delete mode 100644 objc/nunja.h delete mode 100644 objc/nunja.m delete mode 100644 objc/passwd.m delete mode 100644 test/test_markup.nu delete mode 100644 test/test_salt.nu diff --git a/Nukefile b/Nukefile index 20bddc5..4f1849d 100644 --- a/Nukefile +++ b/Nukefile @@ -5,7 +5,7 @@ (set SYSTEM ((NSString stringWithShellCommand:"uname") chomp)) (case SYSTEM ("Darwin" - (set @arch (list "x86_64")) + (set @arch (list "x86_64" )) (set @cflags "-g -std=gnu99 -fobjc-gc -DDARWIN") (set @ldflags "-framework Foundation -framework Nu -levent -lcrypto")) ("Linux" @@ -20,12 +20,15 @@ (set @framework "Nunja") (set @framework_identifier "nu.programming.nunja") (set @framework_creator_code "????") -(set @framework_extra_install - (do () (SH "sudo cp nunjad /usr/local/bin"))) +;(set @framework_extra_install (do () (SH "sudo cp nunjad /usr/local/bin"))) (compilation-tasks) (framework-tasks) +(task "clean" is + (SH "rm -rf build") + (SH "rm -rf Xcode/build")) + (task "clobber" => "clean" is (SH "rm -rf #{@framework_dir}")) diff --git a/Xcode/English.lproj/InfoPlist.strings b/Xcode/English.lproj/InfoPlist.strings new file mode 100644 index 0000000..88f65cf --- /dev/null +++ b/Xcode/English.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Xcode/Info.plist b/Xcode/Info.plist new file mode 100644 index 0000000..4d7a357 --- /dev/null +++ b/Xcode/Info.plist @@ -0,0 +1,28 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + nu.programming.nunja + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + NSPrincipalClass + + + diff --git a/Xcode/Nunja.xcodeproj/project.pbxproj b/Xcode/Nunja.xcodeproj/project.pbxproj new file mode 100644 index 0000000..41fdc5a --- /dev/null +++ b/Xcode/Nunja.xcodeproj/project.pbxproj @@ -0,0 +1,459 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXBuildFile section */ + 2224951111D9BAA300A4D1F2 /* NSFileManager_Nunja.h in Headers */ = {isa = PBXBuildFile; fileRef = 2224950411D9BAA300A4D1F2 /* NSFileManager_Nunja.h */; }; + 2224951211D9BAA300A4D1F2 /* NSFileManager_Nunja.m in Sources */ = {isa = PBXBuildFile; fileRef = 2224950511D9BAA300A4D1F2 /* NSFileManager_Nunja.m */; }; + 2224951311D9BAA300A4D1F2 /* Nunja_daemonize.m in Sources */ = {isa = PBXBuildFile; fileRef = 2224950611D9BAA300A4D1F2 /* Nunja_daemonize.m */; }; + 2224951411D9BAA300A4D1F2 /* Nunja.h in Headers */ = {isa = PBXBuildFile; fileRef = 2224950711D9BAA300A4D1F2 /* Nunja.h */; }; + 2224951511D9BAA300A4D1F2 /* Nunja.m in Sources */ = {isa = PBXBuildFile; fileRef = 2224950811D9BAA300A4D1F2 /* Nunja.m */; }; + 2224951611D9BAA300A4D1F2 /* NunjaDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 2224950911D9BAA300A4D1F2 /* NunjaDelegate.h */; }; + 2224951711D9BAA300A4D1F2 /* NunjaDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2224950A11D9BAA300A4D1F2 /* NunjaDelegate.m */; }; + 2224951811D9BAA300A4D1F2 /* NunjaRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 2224950B11D9BAA300A4D1F2 /* NunjaRequest.h */; }; + 2224951911D9BAA300A4D1F2 /* NunjaRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 2224950C11D9BAA300A4D1F2 /* NunjaRequest.m */; }; + 2224951A11D9BAA300A4D1F2 /* NunjaRequestHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 2224950D11D9BAA300A4D1F2 /* NunjaRequestHandler.h */; }; + 2224951B11D9BAA300A4D1F2 /* NunjaRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 2224950E11D9BAA300A4D1F2 /* NunjaRequestHandler.m */; }; + 2224951C11D9BAA300A4D1F2 /* NunjaRequestRouter.h in Headers */ = {isa = PBXBuildFile; fileRef = 2224950F11D9BAA300A4D1F2 /* NunjaRequestRouter.h */; }; + 2224951D11D9BAA300A4D1F2 /* NunjaRequestRouter.m in Sources */ = {isa = PBXBuildFile; fileRef = 2224951011D9BAA300A4D1F2 /* NunjaRequestRouter.m */; }; + 22C53C3911D9B17F003626DF /* nunja.nu in Resources */ = {isa = PBXBuildFile; fileRef = 22C53C3811D9B17F003626DF /* nunja.nu */; }; + 22C53C4211D9B1CE003626DF /* libevent.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 22C53C4111D9B1CE003626DF /* libevent.a */; }; + 22C53C4911D9B1FA003626DF /* Nu.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22C53C4811D9B1FA003626DF /* Nu.framework */; }; + 22C53CCE11D9B21B003626DF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22C53CCD11D9B21B003626DF /* Foundation.framework */; }; + 22C53CE511D9B261003626DF /* nunjad.m in Sources */ = {isa = PBXBuildFile; fileRef = 22C53CE211D9B256003626DF /* nunjad.m */; }; + 22C53CEF11D9B2B4003626DF /* Nunja.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Nunja.framework */; }; + 22C53D3E11D9B4A8003626DF /* NuHTTPHelpers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22C53D3D11D9B4A8003626DF /* NuHTTPHelpers.framework */; }; + 8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 22C53CFF11D9B2F8003626DF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8DC2EF4F0486A6940098B216; + remoteInfo = Nunja; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 089C1667FE841158C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; + 2224950411D9BAA300A4D1F2 /* NSFileManager_Nunja.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NSFileManager_Nunja.h; path = ../objc/NSFileManager_Nunja.h; sourceTree = SOURCE_ROOT; }; + 2224950511D9BAA300A4D1F2 /* NSFileManager_Nunja.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NSFileManager_Nunja.m; path = ../objc/NSFileManager_Nunja.m; sourceTree = SOURCE_ROOT; }; + 2224950611D9BAA300A4D1F2 /* Nunja_daemonize.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Nunja_daemonize.m; path = ../objc/Nunja_daemonize.m; sourceTree = SOURCE_ROOT; }; + 2224950711D9BAA300A4D1F2 /* Nunja.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nunja.h; path = ../objc/Nunja.h; sourceTree = SOURCE_ROOT; }; + 2224950811D9BAA300A4D1F2 /* Nunja.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Nunja.m; path = ../objc/Nunja.m; sourceTree = SOURCE_ROOT; }; + 2224950911D9BAA300A4D1F2 /* NunjaDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NunjaDelegate.h; path = ../objc/NunjaDelegate.h; sourceTree = SOURCE_ROOT; }; + 2224950A11D9BAA300A4D1F2 /* NunjaDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NunjaDelegate.m; path = ../objc/NunjaDelegate.m; sourceTree = SOURCE_ROOT; }; + 2224950B11D9BAA300A4D1F2 /* NunjaRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NunjaRequest.h; path = ../objc/NunjaRequest.h; sourceTree = SOURCE_ROOT; }; + 2224950C11D9BAA300A4D1F2 /* NunjaRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NunjaRequest.m; path = ../objc/NunjaRequest.m; sourceTree = SOURCE_ROOT; }; + 2224950D11D9BAA300A4D1F2 /* NunjaRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NunjaRequestHandler.h; path = ../objc/NunjaRequestHandler.h; sourceTree = SOURCE_ROOT; }; + 2224950E11D9BAA300A4D1F2 /* NunjaRequestHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NunjaRequestHandler.m; path = ../objc/NunjaRequestHandler.m; sourceTree = SOURCE_ROOT; }; + 2224950F11D9BAA300A4D1F2 /* NunjaRequestRouter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NunjaRequestRouter.h; path = ../objc/NunjaRequestRouter.h; sourceTree = SOURCE_ROOT; }; + 2224951011D9BAA300A4D1F2 /* NunjaRequestRouter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NunjaRequestRouter.m; path = ../objc/NunjaRequestRouter.m; sourceTree = SOURCE_ROOT; }; + 22C53C3811D9B17F003626DF /* nunja.nu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = nunja.nu; path = ../nu/nunja.nu; sourceTree = SOURCE_ROOT; }; + 22C53C4111D9B1CE003626DF /* libevent.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libevent.a; path = /usr/local/lib/libevent.a; sourceTree = ""; }; + 22C53C4811D9B1FA003626DF /* Nu.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nu.framework; path = /Library/Frameworks/Nu.framework; sourceTree = ""; }; + 22C53CCD11D9B21B003626DF /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 22C53CDE11D9B242003626DF /* nunjad */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = nunjad; sourceTree = BUILT_PRODUCTS_DIR; }; + 22C53CE211D9B256003626DF /* nunjad.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = nunjad.m; sourceTree = ""; }; + 22C53D3D11D9B4A8003626DF /* NuHTTPHelpers.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NuHTTPHelpers.framework; path = /Library/Frameworks/NuHTTPHelpers.framework; sourceTree = ""; }; + 32DBCF5E0370ADEE00C91783 /* Nunja_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Nunja_Prefix.pch; sourceTree = ""; }; + 8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8DC2EF5B0486A6940098B216 /* Nunja.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Nunja.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 22C53CDC11D9B242003626DF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 22C53CEF11D9B2B4003626DF /* Nunja.framework in Frameworks */, + 22C53D3E11D9B4A8003626DF /* NuHTTPHelpers.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8DC2EF560486A6940098B216 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 22C53C4211D9B1CE003626DF /* libevent.a in Frameworks */, + 22C53C4911D9B1FA003626DF /* Nu.framework in Frameworks */, + 22C53CCE11D9B21B003626DF /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 034768DFFF38A50411DB9C8B /* Products */ = { + isa = PBXGroup; + children = ( + 8DC2EF5B0486A6940098B216 /* Nunja.framework */, + 22C53CDE11D9B242003626DF /* nunjad */, + ); + name = Products; + sourceTree = ""; + }; + 0867D691FE84028FC02AAC07 /* Nunja */ = { + isa = PBXGroup; + children = ( + 08FB77AEFE84172EC02AAC07 /* Classes */, + 32C88DFF0371C24200C91783 /* Other Sources */, + 089C1665FE841158C02AAC07 /* Resources */, + 0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */, + 034768DFFF38A50411DB9C8B /* Products */, + ); + name = Nunja; + sourceTree = ""; + }; + 0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */ = { + isa = PBXGroup; + children = ( + 22C53CCD11D9B21B003626DF /* Foundation.framework */, + 22C53C4811D9B1FA003626DF /* Nu.framework */, + 22C53C4111D9B1CE003626DF /* libevent.a */, + 1058C7B0FEA5585E11CA2CBB /* Linked Frameworks */, + 1058C7B2FEA5585E11CA2CBB /* Other Frameworks */, + 22C53D3D11D9B4A8003626DF /* NuHTTPHelpers.framework */, + ); + name = "External Frameworks and Libraries"; + sourceTree = ""; + }; + 089C1665FE841158C02AAC07 /* Resources */ = { + isa = PBXGroup; + children = ( + 8DC2EF5A0486A6940098B216 /* Info.plist */, + 089C1666FE841158C02AAC07 /* InfoPlist.strings */, + ); + name = Resources; + sourceTree = ""; + }; + 08FB77AEFE84172EC02AAC07 /* Classes */ = { + isa = PBXGroup; + children = ( + 2224950411D9BAA300A4D1F2 /* NSFileManager_Nunja.h */, + 2224950511D9BAA300A4D1F2 /* NSFileManager_Nunja.m */, + 2224950611D9BAA300A4D1F2 /* Nunja_daemonize.m */, + 2224950711D9BAA300A4D1F2 /* Nunja.h */, + 2224950811D9BAA300A4D1F2 /* Nunja.m */, + 2224950911D9BAA300A4D1F2 /* NunjaDelegate.h */, + 2224950A11D9BAA300A4D1F2 /* NunjaDelegate.m */, + 2224950B11D9BAA300A4D1F2 /* NunjaRequest.h */, + 2224950C11D9BAA300A4D1F2 /* NunjaRequest.m */, + 2224950D11D9BAA300A4D1F2 /* NunjaRequestHandler.h */, + 2224950E11D9BAA300A4D1F2 /* NunjaRequestHandler.m */, + 2224950F11D9BAA300A4D1F2 /* NunjaRequestRouter.h */, + 2224951011D9BAA300A4D1F2 /* NunjaRequestRouter.m */, + ); + name = Classes; + sourceTree = ""; + }; + 1058C7B0FEA5585E11CA2CBB /* Linked Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = "Linked Frameworks"; + sourceTree = ""; + }; + 1058C7B2FEA5585E11CA2CBB /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 32C88DFF0371C24200C91783 /* Other Sources */ = { + isa = PBXGroup; + children = ( + 22C53CE211D9B256003626DF /* nunjad.m */, + 22C53C3811D9B17F003626DF /* nunja.nu */, + 32DBCF5E0370ADEE00C91783 /* Nunja_Prefix.pch */, + ); + name = "Other Sources"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 8DC2EF500486A6940098B216 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 2224951111D9BAA300A4D1F2 /* NSFileManager_Nunja.h in Headers */, + 2224951411D9BAA300A4D1F2 /* Nunja.h in Headers */, + 2224951611D9BAA300A4D1F2 /* NunjaDelegate.h in Headers */, + 2224951811D9BAA300A4D1F2 /* NunjaRequest.h in Headers */, + 2224951A11D9BAA300A4D1F2 /* NunjaRequestHandler.h in Headers */, + 2224951C11D9BAA300A4D1F2 /* NunjaRequestRouter.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 22C53CDD11D9B242003626DF /* nunjad */ = { + isa = PBXNativeTarget; + buildConfigurationList = 22C53CE411D9B256003626DF /* Build configuration list for PBXNativeTarget "nunjad" */; + buildPhases = ( + 22C53CDB11D9B242003626DF /* Sources */, + 22C53CDC11D9B242003626DF /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 22C53D0011D9B2F8003626DF /* PBXTargetDependency */, + ); + name = nunjad; + productName = nunjad; + productReference = 22C53CDE11D9B242003626DF /* nunjad */; + productType = "com.apple.product-type.tool"; + }; + 8DC2EF4F0486A6940098B216 /* Nunja */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Nunja" */; + buildPhases = ( + 8DC2EF500486A6940098B216 /* Headers */, + 8DC2EF520486A6940098B216 /* Resources */, + 8DC2EF540486A6940098B216 /* Sources */, + 8DC2EF560486A6940098B216 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Nunja; + productInstallPath = "$(HOME)/Library/Frameworks"; + productName = Nunja; + productReference = 8DC2EF5B0486A6940098B216 /* Nunja.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0867D690FE84028FC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Nunja" */; + compatibilityVersion = "Xcode 3.1"; + hasScannedForEncodings = 1; + mainGroup = 0867D691FE84028FC02AAC07 /* Nunja */; + productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8DC2EF4F0486A6940098B216 /* Nunja */, + 22C53CDD11D9B242003626DF /* nunjad */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8DC2EF520486A6940098B216 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */, + 22C53C3911D9B17F003626DF /* nunja.nu in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 22C53CDB11D9B242003626DF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 22C53CE511D9B261003626DF /* nunjad.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8DC2EF540486A6940098B216 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2224951211D9BAA300A4D1F2 /* NSFileManager_Nunja.m in Sources */, + 2224951311D9BAA300A4D1F2 /* Nunja_daemonize.m in Sources */, + 2224951511D9BAA300A4D1F2 /* Nunja.m in Sources */, + 2224951711D9BAA300A4D1F2 /* NunjaDelegate.m in Sources */, + 2224951911D9BAA300A4D1F2 /* NunjaRequest.m in Sources */, + 2224951B11D9BAA300A4D1F2 /* NunjaRequestHandler.m in Sources */, + 2224951D11D9BAA300A4D1F2 /* NunjaRequestRouter.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 22C53D0011D9B2F8003626DF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8DC2EF4F0486A6940098B216 /* Nunja */; + targetProxy = 22C53CFF11D9B2F8003626DF /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 089C1666FE841158C02AAC07 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 089C1667FE841158C02AAC07 /* English */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 1DEB91AE08733DA50010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = Nunja_Prefix.pch; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(HOME)/Library/Frameworks"; + PRODUCT_NAME = Nunja; + WRAPPER_EXTENSION = framework; + }; + name = Debug; + }; + 1DEB91AF08733DA50010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = Nunja_Prefix.pch; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(HOME)/Library/Frameworks"; + PRODUCT_NAME = Nunja; + WRAPPER_EXTENSION = framework; + }; + name = Release; + }; + 1DEB91B208733DA50010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = DARWIN; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = /usr/local/include; + ONLY_ACTIVE_ARCH = YES; + PREBINDING = NO; + SDKROOT = macosx10.6; + }; + name = Debug; + }; + 1DEB91B308733DA50010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PREPROCESSOR_DEFINITIONS = DARWIN; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = /usr/local/include; + PREBINDING = NO; + SDKROOT = macosx10.6; + }; + name = Release; + }; + 22C53CE011D9B242003626DF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = nunjad; + }; + name = Debug; + }; + 22C53CE111D9B242003626DF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = nunjad; + ZERO_LINK = NO; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Nunja" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB91AE08733DA50010E9CD /* Debug */, + 1DEB91AF08733DA50010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Nunja" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB91B208733DA50010E9CD /* Debug */, + 1DEB91B308733DA50010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 22C53CE411D9B256003626DF /* Build configuration list for PBXNativeTarget "nunjad" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 22C53CE011D9B242003626DF /* Debug */, + 22C53CE111D9B242003626DF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0867D690FE84028FC02AAC07 /* Project object */; +} diff --git a/Xcode/Nunja_Prefix.pch b/Xcode/Nunja_Prefix.pch new file mode 100644 index 0000000..e69de29 diff --git a/Xcode/nunjad.m b/Xcode/nunjad.m new file mode 100644 index 0000000..3183c90 --- /dev/null +++ b/Xcode/nunjad.m @@ -0,0 +1,36 @@ +#import +#import "Nunja.h" +#import "NunjaDelegate.h" +#import "NunjaRequest.h" +#import "NunjaRequestHandler.h" + +@interface MyNunjaDelegate : NunjaDelegate +{ +} +@end + +@implementation MyNunjaDelegate + +- (void) nunjaDidFinishLaunching { +#ifdef DARWIN + [self addHandlerWithHTTPMethod:@"GET" + path:@"/block/me:" + block:^(NunjaRequest *REQUEST) { + NSMutableString *result = [NSMutableString string]; + [result appendString:@"Handling 'block'\n"]; + [result appendString:@"Bindings\n"]; + [result appendString:[[REQUEST bindings] description]]; + [result appendString:@"\n"]; + [result appendString:@"Query\n"]; + [result appendString:[[REQUEST query] description]]; + [REQUEST setContentType:@"text/plain"]; + return result; + }]; +#endif +} +@end + +int main (int argc, const char * argv[]) +{ + return NunjaMain(argc, argv, @"MyNunjaDelegate"); +} diff --git a/nu/markup.nu b/nu/markup.nu deleted file mode 100644 index 323cf46..0000000 --- a/nu/markup.nu +++ /dev/null @@ -1,56 +0,0 @@ -;; requires Nunja -(load "Nunja") - -(set &html (NunjaMarkupOperator operatorWithTag:"html" prefix:<<-END - -END)) - -(global XMLNS "http://www.w3.org/1999/xhtml") - -(macro markup (*names) - `(progn - (',*names each: - (do (name) - (set stringName (name stringValue)) - (set expression - (list 'global ((+ "&" stringName) symbolValue) '(NunjaMarkupOperator operatorWithTag:stringName))) - (eval expression))))) - -# add tags as needed -(markup a - body - br - button - div - fieldset - form - head - h1 - h2 - h3 - h4 - h5 - h6 - img - input - label - li - link - meta - ol - option - p - pre - script - select - span - strong - style - table - td - textarea - th - title - tr - tbody - ul) diff --git a/nu/nunja.nu b/nu/nunja.nu index f5728c5..23cd2af 100644 --- a/nu/nunja.nu +++ b/nu/nunja.nu @@ -14,19 +14,12 @@ ;; See the License for the specific language governing permissions and ;; limitations under the License. -;; import some useful C functions -(global random (NuBridgedFunction functionWithName:"random" signature:"l")) -(global srandom (NuBridgedFunction functionWithName:"srandom" signature:"vI")) - (case (set SYSTEM ((NSString stringWithShellCommand:"uname") chomp)) ("Darwin" (import Foundation)) ("Linux" (global NSLog (NuBridgedFunction functionWithName:"NSLog" signature:"v@")) (global NSUTF8StringEncoding 4)) (else nil)) -(load "Nu:template") -(load "Nunja:mime") - ;; @class NSDate ;; @discussion Extensions for Nunja. (class NSDate @@ -39,13 +32,13 @@ timeZone:(NSTimeZone localTimeZone) locale:nil)) (result appendString:((NSTimeZone localTimeZone) abbreviation)) result) - + ;; Get an RFC822-compliant representation of a date, expressed in GMT. (- (id) rfc822-GMT is (set result ((NSMutableString alloc) init)) (result appendString: (self descriptionWithCalendarFormat:"%a, %d %b %Y %H:%M:%S GMT" - timeZone:(NSTimeZone timeZoneWithName:"GMT") locale:nil)) + timeZone:(NSTimeZone timeZoneWithName:"GMT") locale:nil)) result) ;; Get an RFC1123-compliant representation of a date. @@ -66,301 +59,83 @@ (result insertString:":" atIndex:(- (result length) 2)) result)) -;; use this pattern to extract a cookie from a header -(set cookie-pattern (regex -"[ ]*([^=]*)=(.*)")) -;; @class NunjaCookie -;; @discussion A class for managing user-identifying cookies. -(class NunjaCookie is NSObject - (ivars) - (ivar-accessors) - - ;; Generate a random identifier for use in a cookie. - (+ (id) randomIdentifier is - "#{((random) stringValue)}#{((random) stringValue)}#{((random) stringValue)}#{((random) stringValue)}") - - ;; Construct a cookie for a specified user. - (+ (id) cookieForUser:(id) user is - ((self alloc) initWithUser:user - value:(self randomIdentifier) - expiration:(NSDate dateWithTimeIntervalSinceNow:3600))) - - ;; Initialize a cookie for a specified user. - (- (id) initWithUser:(id) user value:(id) value expiration:(id) expiration is - (super init) - (set @name "session") - (set @user user) - (set @value value) - (set @expiration expiration) - (set @stringValue nil) - self) - - ;; Get a string description of a cookie. - (- (id) description is - "cookie=#{@name} value=#{@value} user=#{@user} expiration=#{(@expiration rfc822)}") - - ;; Get a string value for a cookie suitable for inclusion in a response header. - (- (id) stringValue is "#{@name}=#{@value}; Expires:#{(@expiration rfc1123)}; Path=/")) - -;; @class NunjaRequest -;; @discussion A class for managing requests received by the server. -(class NunjaRequest - (ivar-accessors) - - (- (id) cookies is - (unless @_cookies - (set @_cookies - (if (set cookies ((self requestHeaders) objectForKey:"Cookie")) - (then (set cookieDictionary (dict)) - ((cookies componentsSeparatedByString:";") each: - (do (cookieDescription) - (if (set match (cookie-pattern findInString:cookieDescription)) - (cookieDictionary setObject:(match groupAtIndex:2) - forKey:(match groupAtIndex:1))))) - cookieDictionary) - (else (dict))))) - @_cookies) - - (- (id) post is - (if (Nunja verbose) - (puts "body is") - (puts ((NSString alloc) initWithData:(self body) encoding:NSUTF8StringEncoding))) - (set d (((NSString alloc) initWithData:(self body) encoding:NSUTF8StringEncoding) urlQueryDictionary)) - (if (Nunja verbose) - (puts (d description))) - d) - - (- (void) setContentType:(id)t is (self setValue:t forResponseHeader:"Content-Type")) - - (- (void) redirectToLocation:(id) location is - (self setValue:location forResponseHeader:"Location") - (self respondWithCode:303 message:"redirecting" string:"redirecting"))) - -;; An HTTP request handler. Handlers consist of an action, a pattern, and a block. -;; The action is an HTTP verb such as "get" or "post", the pattern is either an NSString -;; or a NuRegex, and the block is a NuBlock to be evaluated in the request handling. -;; Request handers are typically created using the "get" or "post" macros and are responsible -;; for setting the response headers and returning the appropriate response data, which can be -;; either raw data (in an NSData object) or a string containing HTML text. -(class NunjaRequestHandler is NSObject - (ivar (id) action (id) pattern (id) block (id) keys) - (ivars) - (ivar-accessors) - - (- (void) setValue:(id) value forKey:(id) key is - (puts "this should not get called") - (puts "#{key}: #{value}")) - - ;; Create a handler with a specified action, pattern, and block. Used internally. - (+ (id) handlerWithAction:(id)action pattern:(id)pattern block:(id)block is - ;; if the pattern is a string that has dynamic parts, turn it into a regex - (set keys nil) - (if (pattern isKindOfClass:NSString) - (set dynamic-part-regex (regex -"/:([^/]*)")) - (set tokens (dynamic-part-regex findAllInString:pattern)) - (if (tokens count) - (set keys (tokens map:(do (token) (token groupAtIndex:1)))) - (set newpattern (+ "^" (dynamic-part-regex replaceWithString:-"/([^/]*)" inString:pattern) "$")) - (set pattern (regex newpattern)))) - - (set handler ((self alloc) init)) - (handler setAction:action) - (handler setPattern:pattern) - (handler setKeys:keys) - (handler setBlock:block) - handler) - - ;; Try to match the handler against a specified action and path. Used internally. - (- (id)matchRequest:(id)request is - (if (or (eq (request command) @action) - (and (eq (request command) "HEAD") (eq @action "GET"))) - (then - (set path (request path)) - (cond ;; match against a string - ((@pattern isKindOfClass:NSString) - (eq @pattern path)) - ;; match against a regular expression - ((@pattern isKindOfClass:NuRegex) - (set match (@pattern findInString:path)) - (if match - (then - (request setMatch:match) - (if (and @keys (eq (+ (@keys count) 1) (match count))) - (set bindings (dict)) - ((@keys count) times: - (do (i) - (bindings setValue:(match groupAtIndex:(+ i 1)) - forKey:(@keys objectAtIndex:i)))) - (request setBindings:bindings)) - YES) - (else nil))) - ;; unsupported pattern type, no match - (else nil))) - ;; unsupported action, no match - (else nil))) - - (set text-html-pattern (regex "^text/html.*$")) - - ;; Handle a request. Used internally. - (- (id)handleRequest:(id)request is - (if (Nunja verbose) - (puts "handling request #{(request uri)}") - (puts "request from host #{(request remoteHost)} port #{(request remotePort)}")) - (let (body (@block request)) - (cond - ;; return without responding, this means the handler has rejected the URL - ((not body) nil) - ;; return data objects as-is - ((body isKindOfClass:NSData) ;; we should set the content type if it isn't set - (request respondWithData:body) - t) - ;; return other non-strings as their stringValues - ((not (body isKindOfClass:NSString)) - (request respondWithString:(body stringValue)) - t) - ;; if a content type is set and it isn't text/html, return string as-is - ((and (set content-type (request valueForResponseHeader:"Content-Type")) - (not (text-html-pattern findInString:content-type))) - (request respondWithString:body) - t) - ;; return string as html - (else (request setContentType:"text/html; charset=UTF-8") - (request respondWithString:body) - t)))) - - ;; Return a response redirecting the client to a new location. This method may be called from action handlers. - (- (id)redirectResponse:(id)request toLocation:(id)location is - (request setValue:location forResponseHeader:"Location") - (request respondWithCode:303 message:"redirecting" string:"redirecting"))) - -(class Nunja - ;; Return a response redirecting the client to a new location. This method may be called from action handlers. - (+ (id)redirectResponse:(id)request toLocation:(id)location is - (request setValue:location forResponseHeader:"Location") - (request respondWithCode:303 message:"redirecting" string:"redirecting"))) - -(class NunjaCache is NSObject - (ivar (id) cache) - - (set sharedCache nil) ;; closure makes this a class variable - - (+ (id) sharedCache is - (unless sharedCache (set sharedCache ((self alloc) init))) - sharedCache) - - (- (id) init is (super init) (set @cache (dict)) self) - - (- (void) setResponse:(id) response forPath:(id)path is - (puts "caching response for #{path}") - (@cache setObject:response forKey:path)) - - (- (id) handleRequest:(id) request is - (set path (request path)) - (if (set item (@cache objectForKey:path)) - (then (request respondWithString:item) - t) - (else nil)))) - -;; @class NunjaController -;; @discussion The Nunja Controller. Responsible for handling requests. -(class NunjaController is NSObject - (ivar (id) handlers (id) root (id) defaultHandler) - (ivar-accessors) - - (set privateSharedController nil) ;; private shared variable - - (+ (id) sharedController is - privateSharedController) - - (- (id) initWithSite:(id) site is - (self init) - (set @handlers (array)) - (set privateSharedController self) - (set @root site) - (load (+ site "/site.nu")) - self) - - (- (void) handleRequest:(id) request is - (set path (request path)) - (if (Nunja verbose) - (puts (+ "REQUEST " (request command) " " path "-----")) - (puts ((request requestHeaders) description))) - (request setValue:"Nunja" forResponseHeader:"Server") - - (set handled nil) - - (set handled ((NunjaCache sharedCache) handleRequest:request)) - - (unless handled ;; try using the programmed handlers - (@handlers each: - (do (handler) - (if (and (not handled) (handler matchRequest:request)) - (set handled (handler handleRequest:request)))))) - - (unless handled ;; does the path end in a '/'? If so, append index.html - (set lastCharacter (path characterAtIndex:(- (path length) 1))) - (if (eq lastCharacter '/') - (set filename (+ @root "/public" path "index.html")) - (if ((NSFileManager defaultManager) fileExistsAtPath:filename) - (set data (NSData dataWithContentsOfFile:filename)) - (request setValue:(mime-type filename) forResponseHeader:"Content-Type") - (request setValue:"max-age=3600" forResponseHeader:"Cache-Control") - (request respondWithData:data) - (set handled YES)))) - - (unless handled ;; look for a file or directory that matches the path - (set filename (+ @root "/public" path)) - (if ((NSFileManager defaultManager) fileExistsAtPath:filename) - (then - (if ((NSFileManager defaultManager) directoryExistsAtPath:filename) - (then ;; for a directory, redirect to the same path with '/' appended - (request setValue:(+ path "/") forResponseHeader:"Location") - (request respondWithCode:301 message:"moved permanently" string:"Moved Permanently") - (set handled YES)) - (else ;; for a file, send its contents - (set data (NSData dataWithContentsOfFile:filename)) - (request setValue:(mime-type filename) forResponseHeader:"Content-Type") - (request setValue:"max-age=3600" forResponseHeader:"Cache-Control") - (request respondWithData:data) - (set handled YES)))))) - - (unless handled ;; try appending .html to the path - (set filename (+ @root "/public" path ".html")) - (if ((NSFileManager defaultManager) fileExistsAtPath:filename) - (set data (NSData dataWithContentsOfFile:filename)) - (request setValue:"text/html" forResponseHeader:"Content-Type") - (request setValue:"max-age=3600" forResponseHeader:"Cache-Control") - (request respondWithData:data) - (set handled YES))) - - (unless handled - (if @defaultHandler - (then (@defaultHandler handleRequest:request)) - (else (request respondWithCode:404 message:"Not Found" string:"Not Found. You said: #{(request command)} #{(request path)}")))))) ;; Declare a get action. -(macro-1 get (pattern *body) - `(((NunjaController sharedController) handlers) - << (NunjaRequestHandler handlerWithAction:"GET" - pattern:,pattern - block:(do (REQUEST) ,@*body)))) +(global get (macro get (path *body) + `((NunjaDelegate sharedDelegate) + addHandlerWithHTTPMethod:"GET" + path:,path + block:(do (REQUEST) ,@*body)))) ;; Declare a post action. -(macro-1 post (pattern *body) - `(((NunjaController sharedController) handlers) - << (NunjaRequestHandler handlerWithAction:"POST" - pattern:,pattern - block:(do (REQUEST) ,@*body)))) +(global post (macro post (path *body) + `((NunjaDelegate sharedDelegate) + addHandlerWithHTTPMethod:"POST" + path:,path + block:(do (REQUEST) ,@*body)))) ;; Declare a 404 handler. -(macro-1 get-404 (*body) - `((NunjaController sharedController) setDefaultHandler: - (NunjaRequestHandler handlerWithAction:"GET" - pattern:nil - block:(do (REQUEST) ,@*body)))) - -;; Set the top-level directory for a site -(macro-1 root (top-level-directory) - `((NunjaController sharedController) setRoot:,top-level-directory)) - - +(global get-404 (macro get-404 (*body) + `((NunjaDelegate sharedDelegate) + setDefaultHandlerWithBlock:(do (REQUEST) ,@*body)))) + +(Nunja setMimeTypes: + (dict "ai" "application/postscript" + "asc" "text/plain" + "avi" "video/x-msvideo" + "bin" "application/octet-stream" + "bmp" "image/bmp" + "class" "application/octet-stream" + "cer" "application/pkix-cert" + "crl" "application/pkix-crl" + "crt" "application/x-x509-ca-cert" + "css" "text/css" + "dms" "application/octet-stream" + "doc" "application/msword" + "dvi" "application/x-dvi" + "eps" "application/postscript" + "etx" "text/x-setext" + "exe" "application/octet-stream" + "gif" "image/gif" + "htm" "text/html" + "html" "text/html" + "ico" "application/icon" + "ics" "text/calendar" + "jpe" "image/jpeg" + "jpeg" "image/jpeg" + "jpg" "image/jpeg" + "js" "text/javascript" + "lha" "application/octet-stream" + "lzh" "application/octet-stream" + "mobileconfig" "application/x-apple-aspen-config" + "mov" "video/quicktime" + "mpe" "video/mpeg" + "mpeg" "video/mpeg" + "mpg" "video/mpeg" + "m3u8" "application/x-mpegURL" + "pbm" "image/x-portable-bitmap" + "pdf" "application/pdf" + "pgm" "image/x-portable-graymap" + "png" "image/png" + "pnm" "image/x-portable-anymap" + "ppm" "image/x-portable-pixmap" + "ppt" "application/vnd.ms-powerpoint" + "ps" "application/postscript" + "qt" "video/quicktime" + "ras" "image/x-cmu-raster" + "rb" "text/plain" + "rd" "text/plain" + "rtf" "application/rtf" + "sgm" "text/sgml" + "sgml" "text/sgml" + "tif" "image/tiff" + "tiff" "image/tiff" + "ts" "video/MP2T" + "txt" "text/plain" + "xbm" "image/x-xbitmap" + "xls" "application/vnd.ms-excel" + "xml" "text/xml" + "xpm" "image/x-xpixmap" + "xwd" "image/x-xwindowdump" + "zip" "application/zip")) diff --git a/nunjad.nu b/nunjad similarity index 90% rename from nunjad.nu rename to nunjad index 102d5da..5cacfbd 100755 --- a/nunjad.nu +++ b/nunjad @@ -17,8 +17,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -(load "NuHTTPHelpers") (load "Nunja") +(load "NuHTTPHelpers") (set exit (NuBridgedFunction functionWithName:"exit" signature:"vi")) @@ -41,6 +41,7 @@ ;; the option(s) we need to set (set site nil) (set port 3000) +(set localOnly NO) ;; process the remaining arguments (while (< argi (argv count)) @@ -49,22 +50,23 @@ ("--site" (set argi (+ argi 1)) (set site (argv argi))) ("-p" (set argi (+ argi 1)) (set port ((argv argi) intValue))) ("--port" (set argi (+ argi 1)) (set port ((argv argi) intValue))) - ("-l" (Nunja setLocalOnly:YES)) - ("--local" (Nunja setLocalOnly:YES)) + ("-l" (set localOnly YES)) + ("--local" (set localOnly YES)) ("-v" (Nunja setVerbose:YES)) ("--verbose" (Nunja setVerbose:YES)) (else (puts (+ "unknown option: " (argv argi))) (exit -1))) (set argi (+ argi 1))) -(set n ((Nunja alloc) init)) -(if (Nunja localOnly) +(set n (Nunja nunja)) +(if localOnly (then (n bindToAddress:"127.0.0.1" port:port)) (else (n bindToAddress:"0.0.0.0" port:port))) (if site - (n setController:((NunjaController alloc) initWithSite:site))) + (n setDelegate:((NunjaDelegate alloc) initWithSite:site))) (puts (+ "Nunja is running on port " port)) (set $site site) ;; make the path to the site directory available to handlers +((n delegate) dump) (n run) diff --git a/nunjad/nunjad b/nunjad/nunjad deleted file mode 100755 index 1df5bdef2bd878fcf7527b10ef2e956a1a69eb88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45856 zcmeHQ50F&FneW*hbWc}V;$l`=@qvSdxn=(t&;gfZ2Us#B3#`Bj7s0px26kk3XPrN| z=mIMXS56J+>Ltd?sey`8SSD4Z&dM1zh_DA7rV=!q#d%&Nx>~ab8qTvkV?5@5zkdB@ z-^~6qrN)?aZ}sc`zV80|>tFYKJKghr^NyW+dcP1N*CoXH-AIKf06DD)mwd$Y5y7iM zGBXbs^1g(4fe_tDzG~n?o(>uTjetf#BcKt`2xtT}0vZ90fJQ(gpb^jr{23$gs}J8k zhfTfyOI3j*!P&u>fpVs!1Jhhly=p~f zb=jUBJAg17g_Q=s)bG40`io+LH(K{(QvSRovM(n-zg=}P@s_p_UVwJE4a`PikU5e z){a0}(pmV8xAd#AC4kNdL&h6xzdhLAYzEuchEOqEzgi{Fo+8rj41t-1X$D%%wc&=g zfW^<2w^zxlRn15{$9tg=^-hUgCq<}WnhSg@e2x;Vhm3V%uNTKUpUS2)`rpKd6m3! zr4VVSJl+>Pkh6nnHm!9oylnhhl)QS?fV6WSY&mvnnxV#9o6NSzx^Q6aF!&u+@(k60 z^v94F3A7F`FRA3!DtV-x^0Mbf>DScS5Q$J?_ISM$tn~shGLUx4Q|xr2#qNsd&zptGrAMA$B?FU7+hpPZ6R83CGC8;1rK{K##>d|{~&&`Sa)|X%O?yZ`4 z?7tg1%%>3fjQR@V=N;_-KV#{^an>VpOx8O39g-4P+47B%XrQgECfFEm2yZM~$Vcn# zq44@h*}_n)9g8X$YA=-!Tyo293WOuzM9Stn${~~Ty&3XBAS{Uu*VtFKBxaSfT@~V> zUKW2zto_!8k(#2&sx@4{ccREOIH#CbYlx-uN-@8|Myuseecu?;j!&dgpR<+KAJ^%i zzE=as2gE2i&JTqHqhskS%dBImsIPwk2;3_C@i+ZF{b&7&O?m#Ev4Z%AJqKOSzn<%N zz3Y#^c-rmnc|OnIGh4*oXD-R-ef&uBhgeka)uE|xrSB$Roo~f*f6p1OFaCdv;_v$t zbKk*Z$iH*e?MPA+ZzB;u;!lkGmCRM|^~Vn<*Fl0mUcJx1bM9F@tdd^`y$H2-&UK+y zvfL_u>PU6p-It)|k?NyFySk!(pahTGMNNH+62*tnhfh{~EqMjf?FXWlfK{Pb7f4oY zsV~)6J^Rh^_nd_Q6#o|8LC0lMxf3MfN2I*M!~VpqtJwQ7bU~GO&gw&XYT|xbtNQ4` zk1Y`|%ktNfdy%S64=dJpB`bEB)#>IfQgY?tt`HRmk}sl%iWm85Dl$pD;i=^{0oo+5;Z%oxQ5GSd1~5aNPGFe zL=3!@N~K!(k!#>p=B{S$CFZVW?kIC#V{Sil%b0r-Ii&KlRX1}zXuE)*{AHD6pcPBIE57xM2|CS08bu7dpY^LkM-6MXj9{0At2 z&}(%>s8vK(sX5&DNy^m z%T?-8X{k!RDxIfNyZupqAVskE!Fc*)1iSn#s7t-jJR{iU)A50k<(vj*ct`PV-g}DP zsM0Me?NRCdD&3{hCsn#%rN>lyQl$ebwdLznBcKt`2xtT}0vZ90fJQ(gpb^jrXaqC@ zUjzguRGCZXeRIA!zs9$Gx#=;NEcXqbj?{)it>VV;Q#rjuYFWoBqPMV$U(#4#QuC|^ zhfCqix_!pJ1+BZux{osMWsyOtJKMgWhCB<(>LU1pvMd^GD_anVtdE8|%9aH>LO47Q zg@b_zzIMcSht}2C(NWe;vbnTP{E_V%W5L#DyR}6uvkrJOnM)gQ4Kzg~GulJpwuaWS zPVaT*bu;ZMAvJZJV;wXC8Uc-fMnEH=5zq)|1T+GFkqG?Kvj4|k&azS9zUDjlj($5* zZq{g`mHOw*A{K7vj56blWI4{jh_^-hO2J z`(kWtz0j=~-{W&nYzJpP!zTb0vd6LFO6;A%UxEEF$7%M*z_Mz@nMh79UaoF#+fVPqJ~Hon{tq8#qnK*7=bB$I%L~~cR&X1U>Tg4I@cKUemS2!wv$z@3kQI{g<+-keA?>*rwlNlf^oZP z+usjuPZ4bI?Ax9P76HfBh-z=qnKH^DfaglYb7UE9q2^d?V9@_0Q-&8ZI1F!=46xF{ zG^LroEdqLF%J3^a{6?l8X#vO(N9);^Wlvv)0xCcm0FOAbt{tXk+s~uk0=p3D0?;dv zK8TcOT*RRWQB+vxN2_sg_9F-!&3@kYVPUiEP=faM8gZ84aVG7+Se9)+Z`;oQ+mVO* z2j2bQ%a@j1Zane!s;QjYT11Yo0P*7nK8J@3?7JS3f=Y;9{!U`E|cU5kX0n-OY-EA>Q`xlf28^p`a#mq zWZ(@vCKHSM6V>^N;=7-_79RkA#t%#%#eefZd!A;E=mq0{dQ)orPnV{yIkx?!=vB|n zVjcbi8KFw7K2_bHsOz6ympA^NyD0u5IRuP7am7q(UF=C7K_~2XO@5*VR_u1EE+1INB zec(Q~@MH9~h;{fGVI)W{KeE7+2hB04Vl}A5niBxx^exZb_%S+I!8&wC8>xe0yMx_D zl#k_-s5=oa^dxV=zbp=Pao+63{o{8`fCE9I`V`jO1T2qQ1Iyzps4;Mux?LgXX*Jqb zpCArXQIfgP3c!gU+FfY(vG_zh-;?|hBZD`=ghchL=sBON`~V7{9-pXt6>}>YkAtzh zK(*rv_?OI;QGJpvVZ7 z!L_AGX1V$4wY&)a3wHaKXTd4vt`=BkFEb}S5PrcNSYqHHa+o9eIgbq5{WCDda+86d zbr@~m0_Jg(;xnTQpYhl_D-_@}n}P455nB*j5$&)C`^$$A??=2FaWf(VZSc8|w)Y04 zr-HV(vZU)lf6c0{uNnc3fJQ(gpb^jrXaqC@8Uc-fMnEH=5zq*H&ItSj`}BWCdojV8360Sd2IW@mj>$h~Glwu_lfVgctb^<8vN_?p`CH5zq)|1T+E~0gZr0 zKqH_L&jFG*g`80dx;5zUAUzelw56)3R% z@mYty_y=M4`WRCB;WJOZ2N5p+GEe_|J8%2m(=LO4_pDF5C;Hse_Fwwj!%nH*uzU%| zBTpL)8h4x%|r+J7t zVbziOSFAiVh0&AD11*FvA;K;$!zUhO72|Ma#%JLRUn-YM6Mrzmy9{j5JI_>k0MXQ5GKqH_L&Ga ze-Bl$&Ak9=+T}rXJG|dBeD~pVJ5-0st!P_|T{%o)|jV}BOon-3XC z2W4dCiNWz~A8#QHcHxN9l!D_w4-xuuH_4n8hHp=o4~bt|f4e@@vED5Q9q2Mr2pLSB zGVJ+)j3)j>l4R9UH?;d(oq8i#4Fm>wTkTLpu)F6ZN$gyP1 zXIcOd84FhMZ3~BKjV~@1Db#LJX$P zzIk8BFqYpvno|hYFZ}>Vk} zIuNOYMnEI**Ns5eoC`a>Q&JzTm?CEKpB>+$^_?lH1KoGvAJg}R`-UdrT6t%|oUTV- zDeT(wx1HViZ{j^H+;~Kas;#N_@Xzh<{35rxD=%lyydQ{ho9@op)0LOIXH$1m=iD7a z+_l5~?523`p04~{MUV3?>2@3G`Yl%dx$9APy}Nf)Fn5n#URfo?&v&Pu?3r3#(zR() zCv?k$ZuOP*_~DtR^75{SuIk+4d7*Po{Uvwb0DW#a0DIhXsa&?(no~Y~v%9C0bk}W@ zI@!kCWK{Y^PQ2@mG+hIFBkRvKgy;%S>g=9ZUUb(r<;A@|QFGrE;kV1SZY`g=_3?5~ znm$-=+z_06!lviEKKUf`vj--hqO92mC-={OeDWE;aFwq-P%vjp^M7=1`AC#*dE*bA z=(D=42QONCL==(U-2V)D735VtA}VkAjc{#w_^haUSe)hfHuwBcj)QVGKl(&x*R!IO z z!%9vu$3}klRs$bwT-%kbg6_M7f6M$|v;Rs*otdc9Ficx7_={BALbR=6TkkI6;&`N9 zk0gcfp>xN}9+X%Vs(D)t`M*Vclz6a>9KV|1%5KcvmPwM|y{){EWvcHYmR0V;df8T9 z;C<{$KV%eU@m6eJ`;VO*8{0{|=l_Guv)-0*hdR+VpJlS20Qw;g+sgB7o}%40z4@vH z%jH`1P7%Aj_2?gK_h(qkyf>C~3u7*L+Y^StdxPs;(Ir{*^ennKi}u*G_4@?scM1ky zA;`BI;{Ap4^@e=EA@cGChkU~!^66I`B0u`;bvT2SEBt$RF`)!3gmBCbV@A>u^t%I_iW zM*K12Gl)kJPa^&&Vm~5ZpT_U!uz{a^5N8G5pS6}+zD7VJpb^jrXaqC@8Uc-fMnEH= z5zq)|1T+Gl2mx1fy16Uu{|<{#@7fE5tHyIZZE0Qfe}^pqtead<(Er`a3zvskk#hv^*#mw=v5U^~yD^(tL2x5WC!xll*=F0O1X*1BQ z_^?wDZS;j#Hn$Pm7aoiU9C^GhBmM6ALyr70*P!X|8JEbBlF;^DNB)DsJnhvd) - -@protocol NunjaProtocol -- (int) bindToAddress:(const char *) address port:(int) port; -- (BOOL) localOnly; -- (void) setLocalOnly:(BOOL) l; -- (void) setVerbose:(BOOL) v; -- (void) setController:(id) controller; -- (void) run; -@end - -int main (int argc, const char * argv[]) { - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; - - Class Nunja = NSClassFromString(@"Nunja"); - Class NunjaController = NSClassFromString(@"NunjaController"); - - int port = 5000; - NSString *site = nil; - - int i = 0; - while (i < argc) { - if (!strcmp(argv[i], "-s") || - !strcmp(argv[i], "--site")) { - if (++i < argc) { - site = [[[NSString alloc] initWithCString:argv[i]] autorelease]; - } - } - else if (!strcmp(argv[i], "-p") || - !strcmp(argv[i], "--port")) { - if (++i < argc) { - port = atoi(argv[i]); - } - } - else if (!strcmp(argv[i], "-l") || - !strcmp(argv[i], "--local")) { - [Nunja setLocalOnly:YES]; - } - else if (!strcmp(argv[i], "-v") || - !strcmp(argv[i], "--verbose")) { - [Nunja setVerbose:YES]; - } - i++; - } - - if (!site) { - NSLog(@"Please specify a site description with the -s or --site option"); - } else { - id nunja = [[Nunja alloc] init]; - if ([Nunja localOnly]) { - [nunja bindToAddress:"127.0.0.1" port:port]; - } else { - [nunja bindToAddress:"0.0.0.0" port:port]; - } - [nunja setController:[[NunjaController alloc] initWithSite:site]]; - [nunja run]; - } - - [pool drain]; - return 0; -} diff --git a/nunjad/nunjad.xcodeproj/project.pbxproj b/nunjad/nunjad.xcodeproj/project.pbxproj deleted file mode 100644 index ac94c49..0000000 --- a/nunjad/nunjad.xcodeproj/project.pbxproj +++ /dev/null @@ -1,225 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 45; - objects = { - -/* Begin PBXBuildFile section */ - 221A336B11D286E900535B31 /* Nu.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 221A336A11D286E900535B31 /* Nu.framework */; }; - 221A336F11D2870400535B31 /* Nunja.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 221A336E11D2870400535B31 /* Nunja.framework */; }; - 8DD76F9A0486AA7600D96B5E /* nunjad.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* nunjad.m */; settings = {ATTRIBUTES = (); }; }; - 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; }; - 8DD76F9F0486AA7600D96B5E /* nunjad.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = C6859EA3029092ED04C91782 /* nunjad.1 */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 8DD76F9E0486AA7600D96B5E /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 8; - dstPath = /usr/share/man/man1/; - dstSubfolderSpec = 0; - files = ( - 8DD76F9F0486AA7600D96B5E /* nunjad.1 in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 1; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 08FB7796FE84155DC02AAC07 /* nunjad.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = nunjad.m; sourceTree = ""; }; - 08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; - 221A336A11D286E900535B31 /* Nu.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nu.framework; path = /Library/Frameworks/Nu.framework; sourceTree = ""; }; - 221A336E11D2870400535B31 /* Nunja.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nunja.framework; path = Library/Frameworks/Nunja.framework; sourceTree = SDKROOT; }; - 8DD76FA10486AA7600D96B5E /* nunjad */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = nunjad; sourceTree = BUILT_PRODUCTS_DIR; }; - C6859EA3029092ED04C91782 /* nunjad.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = nunjad.1; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 8DD76F9B0486AA7600D96B5E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */, - 221A336B11D286E900535B31 /* Nu.framework in Frameworks */, - 221A336F11D2870400535B31 /* Nunja.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 08FB7794FE84155DC02AAC07 /* nunjad */ = { - isa = PBXGroup; - children = ( - 08FB7795FE84155DC02AAC07 /* Source */, - C6859EA2029092E104C91782 /* Documentation */, - 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */, - 1AB674ADFE9D54B511CA2CBB /* Products */, - ); - name = nunjad; - sourceTree = ""; - }; - 08FB7795FE84155DC02AAC07 /* Source */ = { - isa = PBXGroup; - children = ( - 08FB7796FE84155DC02AAC07 /* nunjad.m */, - ); - name = Source; - sourceTree = ""; - }; - 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = { - isa = PBXGroup; - children = ( - 08FB779EFE84155DC02AAC07 /* Foundation.framework */, - 221A336A11D286E900535B31 /* Nu.framework */, - 221A336E11D2870400535B31 /* Nunja.framework */, - ); - name = "External Frameworks and Libraries"; - sourceTree = ""; - }; - 1AB674ADFE9D54B511CA2CBB /* Products */ = { - isa = PBXGroup; - children = ( - 8DD76FA10486AA7600D96B5E /* nunjad */, - ); - name = Products; - sourceTree = ""; - }; - C6859EA2029092E104C91782 /* Documentation */ = { - isa = PBXGroup; - children = ( - C6859EA3029092ED04C91782 /* nunjad.1 */, - ); - name = Documentation; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 8DD76F960486AA7600D96B5E /* nunjad */ = { - isa = PBXNativeTarget; - buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "nunjad" */; - buildPhases = ( - 8DD76F990486AA7600D96B5E /* Sources */, - 8DD76F9B0486AA7600D96B5E /* Frameworks */, - 8DD76F9E0486AA7600D96B5E /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = nunjad; - productInstallPath = "$(HOME)/bin"; - productName = nunjad; - productReference = 8DD76FA10486AA7600D96B5E /* nunjad */; - productType = "com.apple.product-type.tool"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 08FB7793FE84155DC02AAC07 /* Project object */ = { - isa = PBXProject; - buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "nunjad" */; - compatibilityVersion = "Xcode 3.1"; - hasScannedForEncodings = 1; - mainGroup = 08FB7794FE84155DC02AAC07 /* nunjad */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 8DD76F960486AA7600D96B5E /* nunjad */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXSourcesBuildPhase section */ - 8DD76F990486AA7600D96B5E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 8DD76F9A0486AA7600D96B5E /* nunjad.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 1DEB927508733DD40010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - INSTALL_PATH = /usr/local/bin; - PRODUCT_NAME = nunjad; - }; - name = Debug; - }; - 1DEB927608733DD40010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_MODEL_TUNING = G5; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - INSTALL_PATH = /usr/local/bin; - PRODUCT_NAME = nunjad; - }; - name = Release; - }; - 1DEB927908733DD40010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - ONLY_ACTIVE_ARCH = YES; - PREBINDING = NO; - SDKROOT = macosx10.6; - }; - name = Debug; - }; - 1DEB927A08733DD40010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - PREBINDING = NO; - SDKROOT = macosx10.6; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "nunjad" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB927508733DD40010E9CD /* Debug */, - 1DEB927608733DD40010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "nunjad" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB927908733DD40010E9CD /* Debug */, - 1DEB927A08733DD40010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; -} diff --git a/objc/NSFileManager_Nunja.h b/objc/NSFileManager_Nunja.h new file mode 100644 index 0000000..8924419 --- /dev/null +++ b/objc/NSFileManager_Nunja.h @@ -0,0 +1,23 @@ +/*! +@file NSFileManager_Nunja.h +@discussion General utilities for the Nunja web server. +@copyright Copyright (c) 2010 Neon Design Technology, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#import + +@interface NSFileManager (Nunja) +- (BOOL) directoryExistsAtPath:(NSString *) path; +@end diff --git a/objc/NSFileManager_Nunja.m b/objc/NSFileManager_Nunja.m new file mode 100644 index 0000000..849165a --- /dev/null +++ b/objc/NSFileManager_Nunja.m @@ -0,0 +1,33 @@ +/*! +@file NSFileManager_Nunja.m +@discussion General utilities for the Nunja web server. +@copyright Copyright (c) 2010 Neon Design Technology, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#import "NSFileManager_Nunja.h" + +@implementation NSFileManager (Nunja) + +- (BOOL) directoryExistsAtPath:(NSString *) path +{ + BOOL isDirectory = NO; + BOOL fileExists = [self fileExistsAtPath:path isDirectory:&isDirectory]; + if (!fileExists) + return NO; + else + return isDirectory; +} + +@end diff --git a/objc/Nunja.h b/objc/Nunja.h new file mode 100644 index 0000000..37c84ef --- /dev/null +++ b/objc/Nunja.h @@ -0,0 +1,66 @@ +/*! + @file Nunja.h + @discussion Core of the Nunja web server. + @copyright Copyright (c) 2010 Neon Design Technology, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import +#import + +@class NunjaRequest; + +@protocol NunjaDelegateProtocol +// Override this to perform Objective-C setup of your Nunja. +- (void) nunjaDidFinishLaunching; + +// Call this within nunjaDidFinishLaunching to add a handler. +- (void) addHandlerWithHTTPMethod:(NSString *)httpMethod path:(NSString *)path block:(id)block; + +// Call this within nunjaDidFinishLaunching to set the 404 handler. +- (void) setDefaultHandlerWithBlock:(id) block; + +// Override this to add your own custom request processing. You probably won't need this. +- (void) handleRequest:(NunjaRequest *)request; +@end + + +@interface Nunja : NSObject {} +// Get a Nunja instance. We only support one per process. ++ (Nunja *) nunja; + +// Control logging ++ (void) setVerbose:(BOOL) v; ++ (BOOL) verbose; + +// Known MIME types ++ (NSMutableDictionary *) mimeTypes; ++ (void) setMimeTypes:(NSMutableDictionary *) dictionary; ++ (NSString *) mimeTypeForFileWithName:(NSString *) filename; + +// The delegate performs all request handling. +- (void) setDelegate:(id) d; +- (id) delegate; + +// Bind the server to a specified address and port. +- (int) bindToAddress:(NSString *) address port:(int) port; + +// Run the server. +- (void) run; + +@end + + +// Run Nunja. Pass nil for NunjaDelegateClassName to set up your site with Nu (site.nu). +int NunjaMain(int argc, const char *argv[], NSString *NunjaDelegateClassName); diff --git a/objc/Nunja.m b/objc/Nunja.m new file mode 100644 index 0000000..24f99a5 --- /dev/null +++ b/objc/Nunja.m @@ -0,0 +1,363 @@ +/*! + @file Nunja.m + @discussion Core of the Nunja web server. + @copyright Copyright (c) 2008 Neon Design Technology, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#include +#include +#include +#include +#include +#define HTTP_SEEOTHER 303 +#define HTTP_DENIED 403 + +#include +#include +#include // inet_ntoa + +#import +#import + +#import "Nunja.h" +#import "NunjaRequest.h" +#import "NunjaDelegate.h" + +void NunjaInit() +{ + static int initialized = 0; + if (!initialized) { + initialized = 1; + [Nu loadNuFile:@"nunja" fromBundleWithIdentifier:@"nu.programming.nunja" withContext:nil]; + } +} + +BOOL verbose_nunja = NO; + +@interface ConcreteNunja : Nunja +{ + struct event_base *event_base; + struct evhttp *httpd; + id delegate; +} + +- (id) delegate; +@end + +@implementation ConcreteNunja + + ++ (void) load +{ + NunjaInit(); +} + +static void nunja_request_handler(struct evhttp_request *req, void *nunja_pointer) +{ + Nunja *nunja = (Nunja *) nunja_pointer; + id delegate = [nunja delegate]; + if (delegate) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NunjaRequest *request = [[NunjaRequest alloc] initWithNunja:nunja request:req]; + [delegate handleRequest:request]; + [request release]; + [pool release]; + } + else { + nunja_response_helper(req, HTTP_OK, @"OK", + [[NSString stringWithFormat:@"Please set the Nunja delegate.
If you are running nunjad, use the '-s' option to specify a site.
\nRequest: %s\n", + evhttp_request_uri(req)] + dataUsingEncoding:NSUTF8StringEncoding]); + } +} + +- (id) init +{ + [super init]; + event_base = event_init(); + evdns_init(); + httpd = evhttp_new(event_base); + evhttp_set_gencb(httpd, nunja_request_handler, self); + delegate = nil; + return self; +} + +- (int) bindToAddress:(NSString *) address port:(int) port +{ + return evhttp_bind_socket(httpd, [address cStringUsingEncoding:NSUTF8StringEncoding], port); +} + +- (void) run +{ + event_base_dispatch(event_base); +} + +- (void) dealloc +{ + evhttp_free(httpd); + [super dealloc]; +} + +- (id) delegate +{ + return delegate; +} + +- (void) setDelegate:(id) d +{ + [d retain]; + [delegate release]; + delegate = d; +} + +@class NuBlock; +@class NuCell; + +static void nunja_dns_gethostbyname_cb(int result, char type, int count, int ttl, void *addresses, void *arg) +{ + id address = nil; + if (result == DNS_ERR_TIMEOUT) { + fprintf(stdout, "[Timed out] "); + } + else if (result != DNS_ERR_NONE) { + fprintf(stdout, "[Error code %d] ", result); + } + else { + fprintf(stdout, "type: %d, count: %d, ttl: %d\n", type, count, ttl); + switch (type) { + case DNS_IPv4_A: + { + struct in_addr *in_addrs = addresses; + if (ttl < 0) { + // invalid resolution + } + else if (count == 0) { + // no addresses + } + else { + address = [NSString stringWithFormat:@"%s", inet_ntoa(in_addrs[0])]; + } + break; + } + case DNS_PTR: + /* may get at most one PTR */ + // this needs review. TB. + if (count == 1) + fprintf(stdout, "addresses: %s ", *(char **)addresses); + break; + default: + break; + } + } + NuBlock *block = (NuBlock *) arg; + id args = [[NSClassFromString(@"NuCell") alloc] init]; + [args setCar:address]; + [block evalWithArguments:args context:nil]; + [block release]; + [args release]; +} + +- (void) resolveDomainName:(NSString *) name andDo:(NuBlock *) block +{ + [block retain]; + evdns_resolve_ipv4([name cStringUsingEncoding:NSUTF8StringEncoding], 0, nunja_dns_gethostbyname_cb, block); +} + +void nunja_http_request_done(struct evhttp_request *req, void *arg) +{ + NSData *data = nil; + if (req->response_code != HTTP_OK) { + if (req->response_code == HTTP_SEEOTHER) { + fprintf(stdout, "REDIRECTING\n"); + //NSDictionary *headers = nunja_request_headers_helper(req); + return; // this is not handled yet. + } + fprintf(stdout, "FAILED to get OK (response = %d)\n", req->response_code); + } + else if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { + fprintf(stdout, "FAILED to find Content-Type\n"); + } + else { + data = [NSData dataWithBytes:EVBUFFER_DATA(req->input_buffer) length:EVBUFFER_LENGTH(req->input_buffer)]; + } + NuBlock *block = (NuBlock *) arg; + id args = [[NSClassFromString(@"NuCell") alloc] init]; + [args setCar:data]; + [block evalWithArguments:args context:nil]; + [block release]; + [args release]; + fprintf(stdout, "end of callback\n"); + // leaking... + //evhttp_connection_free(req->evcon); +} + +- (void) getResourceFromHost:(NSString *) host address:(NSString *) address port:(int)port path:(NSString *)path andDo:(NuBlock *) block +{ + [block retain]; + // make the connection + struct evhttp_connection *evcon = evhttp_connection_new([address cStringUsingEncoding:NSUTF8StringEncoding], port); + if (evcon == NULL) { + fprintf(stdout, "FAILED to connect\n"); + id args = [[NSClassFromString(@"NuCell") alloc] init]; + [block evalWithArguments:args context:nil]; + [block release]; + [args release]; + return; + } + // make the request + struct evhttp_request *req = evhttp_request_new(nunja_http_request_done, block); + evhttp_add_header(req->output_headers, "Host", [host cStringUsingEncoding:NSUTF8StringEncoding]); + // give ownership of the request to the connection + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, [path cStringUsingEncoding:NSUTF8StringEncoding]) == -1) { + fprintf(stdout, "FAILED to make the request \n"); + } +} + +- (void) postDataToHost:(NSString *) host address:(NSString *) address port:(int)port path:(NSString *)path data:(NSData *) data andDo:(NuBlock *) block +{ + [block retain]; + // make the connection + struct evhttp_connection *evcon = evhttp_connection_new([address cStringUsingEncoding:NSUTF8StringEncoding], port); + if (evcon == NULL) { + fprintf(stdout, "FAILED to connect\n"); + id args = [[NSClassFromString(@"NuCell") alloc] init]; + [block evalWithArguments:args context:nil]; + [block release]; + [args release]; + return; + } + // make the request + struct evhttp_request *req = evhttp_request_new(nunja_http_request_done, block); + evhttp_add_header(req->output_headers, "Host", [host cStringUsingEncoding:NSUTF8StringEncoding]); + evhttp_add_header(req->output_headers, "Content-Length", [[NSString stringWithFormat:@"%d", [data length]] cStringUsingEncoding:NSUTF8StringEncoding]); + evhttp_add_header(req->output_headers, "Content-Type", "application/x-www-form-urlencoded"); + evbuffer_add(req->output_buffer, [data bytes], [data length]); + + // give ownership of the request to the connection + if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, [path cStringUsingEncoding:NSUTF8StringEncoding]) == -1) { + fprintf(stdout, "FAILED to make the request \n"); + } +} + +@end + +@implementation Nunja + ++ (Nunja *) nunja { + return [[[ConcreteNunja alloc] init] autorelease]; +} + ++ (void) setVerbose:(BOOL) v +{ + verbose_nunja = v; +} + ++ (BOOL) verbose {return verbose_nunja;} + +- (void) run { +} + +- (int) bindToAddress:(NSString *) address port:(int) port { + return 0; +} + +- (void) setDelegate:(id) d +{ +} + +- (id) delegate { + return nil; +} + +static NSMutableDictionary *mimeTypes = nil; + ++ (NSMutableDictionary *) mimeTypes { + return mimeTypes; +} + ++ (void) setMimeTypes:(NSMutableDictionary *) dictionary { + [dictionary retain]; + [mimeTypes release]; + mimeTypes = dictionary; +} + ++ (NSString *) mimeTypeForFileWithName:(NSString *) pathName { + if (mimeTypes) { + NSString *suffix = [[pathName componentsSeparatedByString:@"."] lastObject]; + NSString *mimeType = [mimeTypes objectForKey:suffix]; + if (mimeType) + return mimeType; + } + // default + return @"text/html; charset=utf-8"; +} + +@end + +int NunjaMain(int argc, const char *argv[], NSString *NunjaDelegateClassName) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + int port = 5000; + NSString *site = @"."; + + BOOL localOnly = NO; + int i = 0; + while (i < argc) { + if (!strcmp(argv[i], "-s") || + !strcmp(argv[i], "--site")) { + if (++i < argc) { + site = [[[NSString alloc] initWithCString:argv[i]] autorelease]; + } + } + else if (!strcmp(argv[i], "-p") || + !strcmp(argv[i], "--port")) { + if (++i < argc) { + port = atoi(argv[i]); + } + } + else if (!strcmp(argv[i], "-l") || + !strcmp(argv[i], "--local")) { + localOnly = YES; + } + else if (!strcmp(argv[i], "-v") || + !strcmp(argv[i], "--verbose")) { + [Nunja setVerbose:YES]; + } + i++; + } + + Nunja *nunja = [Nunja nunja]; + if (localOnly) { + [nunja bindToAddress:@"127.0.0.1" port:port]; + } + else { + [nunja bindToAddress:@"0.0.0.0" port:port]; + } + Class NunjaDelegateClass = NunjaDelegateClassName ? NSClassFromString(NunjaDelegateClassName) : [NunjaDelegate class]; + id delegate = [[[NunjaDelegateClass alloc] initWithSite:site] autorelease]; + if ([delegate respondsToSelector:@selector(nunjaDidFinishLaunching)]) { + [delegate nunjaDidFinishLaunching]; + } + if ([Nunja verbose]) { + [delegate dump]; + } + [nunja setDelegate:delegate]; + [nunja run]; + + [pool drain]; + return 0; +} + diff --git a/objc/NunjaDelegate.h b/objc/NunjaDelegate.h new file mode 100644 index 0000000..24f5d7a --- /dev/null +++ b/objc/NunjaDelegate.h @@ -0,0 +1,23 @@ +#import +#import "Nunja.h" + +@class NunjaRequestHandler; +@class NunjaRequestRouter; + +@interface NunjaDelegate : NSObject +{ + NunjaRequestHandler *defaultHandler; + NunjaRequestRouter *router; +} + +- (id) initWithSite:(NSString *) site; + +- (void) addHandler:(NunjaRequestHandler *) handler; +- (void) addHandlerWithHTTPMethod:(NSString *)httpMethod path:(NSString *)path block:(id)block; +- (void) setDefaultHandlerWithBlock:(id) block; + +- (void) dump; + +@end + + diff --git a/objc/NunjaDelegate.m b/objc/NunjaDelegate.m new file mode 100644 index 0000000..43e0bcf --- /dev/null +++ b/objc/NunjaDelegate.m @@ -0,0 +1,170 @@ + +#import "NunjaDelegate.h" +#import "NunjaRequestRouter.h" +#import "NunjaRequestHandler.h" +#import "Nunja.h" + +@implementation NunjaDelegate + +static NunjaDelegate *_sharedDelegate; + ++ (NunjaDelegate *) sharedDelegate { + return _sharedDelegate; +} + +- (id) init +{ + if (self = [super init]) { + self->router = [[NunjaRequestRouter routerWithToken:@""] retain]; + } + _sharedDelegate = self; + return self; +} + +- (id) initWithSite:(id) site +{ + self = [self init]; + + id parser = [NSClassFromString(@"Nu") parser]; + + // set working directory to site path + chdir([site cStringUsingEncoding:NSUTF8StringEncoding]); + + // load site description + NSString *filename = [NSString stringWithFormat:@"site.nu", site]; + NSString *sourcecode = [NSString stringWithContentsOfFile:filename]; + if (sourcecode) { + [parser parseEval:sourcecode]; + } + return self; +} + +- (void) setDefaultHandlerWithBlock:(id) block { + id handler = [NunjaRequestHandler handlerWithHTTPMethod:@"GET" path:@"" block:block]; + [handler retain]; + [self->defaultHandler release]; + self->defaultHandler = handler; +} + +- (void) addHandler:(id) handler +{ + [self->router insertHandler:handler level:0]; +} + +- (void) addHandlerWithHTTPMethod:(NSString *)httpMethod path:(NSString *)path block:(id)block +{ + [self addHandler:[NunjaRequestHandler handlerWithHTTPMethod:httpMethod path:path block:block]]; +} + +- (void) dump +{ + [self->router dump:0]; +} + +- (void) handleRequest:(NunjaRequest *) request +{ + id path = [request path]; + if ([Nunja verbose]) { + NSLog(@"REQUEST %@ %@ ----", [request HTTPMethod], path); + NSLog(@"%@", [request requestHeaders]); + } + [request setValue:@"Nunja" forResponseHeader:@"Server"]; + [request setBindings:[NSMutableDictionary dictionary]]; + + id httpMethod = [request HTTPMethod]; + if ([httpMethod isEqualToString:@"HEAD"]) + httpMethod = @"GET"; + + id parts = [[NSString stringWithFormat:@"%@%@", httpMethod, [request path]] componentsSeparatedByString:@"/"]; + if (([parts count] > 2) && [[parts lastObject] isEqualToString:@""]) { + parts = [parts subarrayWithRange:NSMakeRange(0, [parts count]-1)]; + } + + BOOL handled = NO; // [[NunjaCache sharedCache] handleRequest:request]; + + if (!handled) { + NunjaRequestHandler *handler = [router routeRequest:request parts:parts level:0]; + if (handler) { + handled = [handler handleRequest:request]; + } + } + + if (!handled) { // does the path end in a '/'? If so, append index.html + unichar lastCharacter = [path characterAtIndex:[path length] - 1]; + if (lastCharacter == '/') { + if (!handled) { + NSString *filename = [NSString stringWithFormat:@"public%@index.html", path]; + if ([[NSFileManager defaultManager] fileExistsAtPath:filename]) { + NSData *data = [NSData dataWithContentsOfFile:filename]; + [request setValue:[Nunja mimeTypeForFileWithName:filename] forResponseHeader:@"Content-Type"]; + [request setValue:@"max-age=3600" forResponseHeader:@"Cache-Control"]; + [request respondWithData:data]; + handled = YES; + } + } + if (!handled) { + NSString *filename = [NSString stringWithFormat:@"public%@prog_index.m3u8", path]; + if ([[NSFileManager defaultManager] fileExistsAtPath:filename]) { + NSData *data = [NSData dataWithContentsOfFile:filename]; + [request setValue:[Nunja mimeTypeForFileWithName:filename] forResponseHeader:@"Content-Type"]; + [request setValue:@"max-age=3600" forResponseHeader:@"Cache-Control"]; + [request respondWithData:data]; + handled = YES; + } + } + } + } + + if (!handled) { + // look for a file or directory that matches the path + NSString *filename = [NSString stringWithFormat:@"public%@", path]; + BOOL isDirectory = NO; + BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:filename isDirectory:&isDirectory]; + if (fileExists) { + if (isDirectory) { + unichar lastCharacter = [path characterAtIndex:[path length] - 1]; + if (lastCharacter != '/') { + // for a directory, redirect to the same path with '/' appended + [request setValue:[path stringByAppendingString:@"/"] forResponseHeader:@"Location"]; + [request respondWithCode:301 message:@"moved permanently" string:@"Moved Permanently"]; + handled = YES; + } + } else { + // for a file, send its contents + NSData *data = [NSData dataWithContentsOfFile:filename]; + [request setValue:[Nunja mimeTypeForFileWithName:filename] forResponseHeader:@"Content-Type"]; + [request setValue:@"max-age=3600" forResponseHeader:@"Cache-Control"]; + [request respondWithData:data]; + handled = YES; + } + } + } + + if (!handled) { // try appending .html to the path + NSString *filename = [NSString stringWithFormat:@"public%@.html", path]; + if ([[NSFileManager defaultManager] fileExistsAtPath:filename]) { + NSData *data = [NSData dataWithContentsOfFile:filename]; + [request setValue:@"text/html" forResponseHeader:@"Content-Type"]; + [request setValue:@"max-age=3600" forResponseHeader:@"Cache-Control"]; + [request respondWithData:data]; + handled = YES; + } + } + + if (!handled) { + if (defaultHandler) { + [defaultHandler handleRequest:request]; + } + else { + [request respondWithCode:404 + message:@"Not Found" + string:[NSString stringWithFormat:@"Not Found. You said: %@ %@", [request HTTPMethod], [request path]]]; + } + } +} + +- (void) nunjaDidFinishLaunching { + +} + +@end diff --git a/objc/NunjaRequest.h b/objc/NunjaRequest.h new file mode 100644 index 0000000..37cf684 --- /dev/null +++ b/objc/NunjaRequest.h @@ -0,0 +1,60 @@ + +#import + +#include +#include +#include +#include +#include + +void nunja_response_helper(struct evhttp_request *req, int code, NSString *message, NSData *data); +NSDictionary *nunja_request_headers_helper(struct evhttp_request *req); + +@class Nunja; + +@interface NunjaRequest : NSObject +{ + Nunja *nunja; + struct evhttp_request *req; + NSString *_uri; + NSString *_path; + NSDictionary *_parameters; + NSDictionary *_query; + NSDictionary *_bindings; + id _cookies; + int _responded; + int _responseCode; + NSString *_responseMessage; +} + +- (id) initWithNunja:(Nunja *)n request:(struct evhttp_request *)r; +- (Nunja *) nunja; +- (NSString *) uri; +- (NSString *) path; +- (NSDictionary *) parameters; +- (NSDictionary *) query; +- (id) bindings; +- (void) setBindings:(id) bindings; +- (NSData *) body; +- (NSString *) HTTPMethod; +- (NSString *) remoteHost; +- (int) remotePort; +- (NSDictionary *) requestHeaders; +- (NSDictionary *) responseHeaders; +- (int) setValue:(NSString *) value forResponseHeader:(NSString *) key; +- (NSString *) valueForResponseHeader:(NSString *) key; +- (int) removeResponseHeader:(NSString *) key; +- (void) clearResponseHeaders; +- (int) responseCode; +- (void) setResponseCode:(int) code message:(NSString *) message; +- (int) setValue:(NSString *) value forResponseHeader:(NSString *) key; +- (BOOL) respondWithString:(NSString *) string; +- (BOOL) respondWithData:(NSData *) data; +- (BOOL) respondWithCode:(int) code message:(NSString *) message string:(NSString *) string; +- (BOOL) respondWithCode:(int) code message:(NSString *) message data:(NSData *) data; +- (NSDictionary *) cookies; +- (void) setContentType:(NSString *)content_type; +- (int) redirectResponseToLocation:(NSString *) location; + + +@end diff --git a/objc/NunjaRequest.m b/objc/NunjaRequest.m new file mode 100644 index 0000000..c8fe662 --- /dev/null +++ b/objc/NunjaRequest.m @@ -0,0 +1,289 @@ +#import "NunjaRequest.h" +#import "Nunja.h" +#import + +#include +#include + +@interface NSString (Helpers) +- (NSDictionary *) urlQueryDictionary; +@end + +@implementation NunjaRequest + +- (id) initWithNunja:(Nunja *)n request:(struct evhttp_request *)r +{ + [super init]; + nunja = n; + req = r; + // get the URI + _uri = [[NSString alloc] initWithCString:evhttp_request_uri(req) encoding:NSUTF8StringEncoding]; + // scan for the path + int max = [_uri length]; + int base = 0; + int i = 0; + unichar c = 0; + while ((i < max) && ((c = [_uri characterAtIndex:i])) && (c != ';') && (c != '?')) + i++; + _path = [[_uri substringToIndex:i] retain]; + // if necessary, scan the object parameters + _parameters = nil; + if (c == ';') { + i = i + 1; + base = i; + while ((i < max) && ((c = [_uri characterAtIndex:i])) && (c != '?')) + i++; + NSString *parameterString = [_uri substringWithRange:NSMakeRange(base, i-base)]; + _parameters = [[parameterString urlQueryDictionary] retain]; + } + // if necessary, scan the query string + _query = nil; + if (c == '?') { + i = i + 1; + base = i; + while ((i < max) && ((c = [_uri characterAtIndex:i]))) + i++; + NSString *queryString = [_uri substringWithRange:NSMakeRange(base, i-base)]; + _query = [[queryString urlQueryDictionary] retain]; + } + // we haven't responded yet + _responded = NO; + // default response code is that everything is ok + _responseCode = HTTP_OK; + _responseMessage = @"OK"; + return self; +} + +- (void) dealloc +{ + [_uri release]; + [_path release]; + [_parameters release]; + [_query release]; + [_bindings release]; + [_cookies release]; + [super dealloc]; +} + +- (Nunja *) nunja {return nunja;} + +- (NSString *) uri +{ + return _uri; +} + +- (NSString *) path +{ + return _path; +} + +- (NSDictionary *) parameters +{ + return _parameters ? _parameters : [NSDictionary dictionary]; +} + +- (NSDictionary *) query +{ + return _query ? _query : [NSDictionary dictionary]; +} + +- (id) bindings +{ + return _bindings ? _bindings : [NSDictionary dictionary]; +} + +- (void) setBindings:(id) bindings +{ + [bindings retain]; + [_bindings release]; + _bindings = bindings; +} + +- (NSData *) body +{ + if (!req->input_buffer->buffer) + return nil; + else { + NSData *data = [NSData dataWithBytes:req->input_buffer->buffer length:req->input_buffer->off]; + return data; + } +} + +- (NSString *) HTTPMethod +{ + switch (req->type) { + case EVHTTP_REQ_GET: + return @"GET"; + case EVHTTP_REQ_POST: + return @"POST"; + case EVHTTP_REQ_HEAD: + return @"HEAD"; + default: + return @"UNKNOWN"; + } +} + +- (NSString *) remoteHost +{ + return [NSString stringWithCString:req->remote_host encoding:NSUTF8StringEncoding]; +} + +- (int) remotePort +{ + return req->remote_port; +} + +NSDictionary *nunja_request_headers_helper(struct evhttp_request *req) +{ + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + struct evkeyval *header; + TAILQ_FOREACH(header, req->input_headers, next) { + [dict setObject:[NSString stringWithCString:header->value encoding:NSUTF8StringEncoding] + forKey:[NSString stringWithCString:header->key encoding:NSUTF8StringEncoding]]; + } + return dict; +} + +- (NSDictionary *) requestHeaders +{ + return nunja_request_headers_helper(req); +} + +static NSDictionary *nunja_response_headers_helper(struct evhttp_request *req) +{ + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + struct evkeyval *header; + TAILQ_FOREACH(header, req->output_headers, next) { + NSString *value = [NSString stringWithCString:header->value encoding:NSUTF8StringEncoding]; + NSString *key = [NSString stringWithCString:header->key encoding:NSUTF8StringEncoding]; + if (value && key) { + [dict setObject:value forKey:key]; + } + } + return dict; +} + +- (NSDictionary *) responseHeaders +{ + return nunja_response_headers_helper(req); +} + +- (int) setValue:(NSString *) value forResponseHeader:(NSString *) key +{ + return evhttp_add_header(req->output_headers, [key cStringUsingEncoding:NSUTF8StringEncoding], [value cStringUsingEncoding:NSUTF8StringEncoding]); +} + +- (NSString *) valueForResponseHeader:(NSString *) key +{ + const char *value = evhttp_find_header(req->output_headers, [key cStringUsingEncoding:NSUTF8StringEncoding]); + return value ? [NSString stringWithCString:value encoding:NSUTF8StringEncoding] : nil; +} + +- (int) removeResponseHeader:(NSString *) key +{ + return evhttp_remove_header(req->output_headers, [key cStringUsingEncoding:NSUTF8StringEncoding]); +} + +- (void) clearResponseHeaders +{ + evhttp_clear_headers(req->output_headers); +} + +- (int) responseCode +{ + return _responseCode; +} + +- (void) setResponseCode:(int) code message:(NSString *) message +{ + _responseCode = code; + [message retain]; + [_responseMessage release]; + _responseMessage = message; +} + +void nunja_response_helper(struct evhttp_request *req, int code, NSString *message, NSData *data) +{ + if ([Nunja verbose]) { + NSLog(@"RESPONSE %d %@ %@", code, message, [nunja_response_headers_helper(req) description]); + } + struct evbuffer *buf = evbuffer_new(); + if (buf == NULL) { + NSLog(@"FATAL: failed to create response buffer"); + assert(0); + } + evbuffer_add(buf, [data bytes], [data length]); + evhttp_send_reply(req, code, [message cStringUsingEncoding:NSUTF8StringEncoding], buf); + evbuffer_free(buf); +} + +- (BOOL) respondWithString:(NSString *) string +{ + if (!_responded) { + nunja_response_helper(req, _responseCode, _responseMessage, [string dataUsingEncoding:NSUTF8StringEncoding]); + _responded = YES; + } + return YES; +} + +- (BOOL) respondWithData:(NSData *) data +{ + if (!_responded) { + nunja_response_helper(req, _responseCode, _responseMessage, data); + _responded = YES; + } + return YES; +} + +- (BOOL) respondWithCode:(int) code message:(NSString *) message string:(NSString *) string +{ + if (!_responded) { + nunja_response_helper(req, code, message, [string dataUsingEncoding:NSUTF8StringEncoding]); + _responded = YES; + } + return YES; +} + +- (BOOL) respondWithCode:(int) code message:(NSString *) message data:(NSData *) data +{ + if (!_responded) { + nunja_response_helper(req, code, message, data); + _responded = YES; + } + return YES; +} + +- (NSDictionary *) cookies +{ + static id cookie_pattern = nil; + if (!cookie_pattern) { + cookie_pattern = [[NSClassFromString(@"NuRegex") regexWithPattern:@"[ ]*([^=]*)=(.*)"] retain]; + } + if (!_cookies) { + _cookies = [[NSMutableDictionary alloc] init]; + NSString *cookieText = [[self requestHeaders] objectForKey:@"Cookie"]; + NSArray *parts = [cookieText componentsSeparatedByString:@";"]; + for (int i = 0; i < [parts count]; i++) { + NSString *cookieDescription = [parts objectAtIndex:i]; + id match = [cookie_pattern findInString:cookieDescription]; + if (match) { + [_cookies setObject:[match groupAtIndex:2] forKey:[match groupAtIndex:1]]; + } + } + } + return _cookies; +} + +- (void) setContentType:(NSString *)content_type +{ + [self setValue:content_type forResponseHeader:@"Content-Type"]; +} + +- (int) redirectResponseToLocation:(NSString *) location +{ + [self setValue:location forResponseHeader:@"Location"]; + [self respondWithCode:303 message:@"redirecting" string:@"redirecting"]; + return YES; +} + +@end diff --git a/objc/NunjaRequestHandler.h b/objc/NunjaRequestHandler.h new file mode 100644 index 0000000..fd95ec7 --- /dev/null +++ b/objc/NunjaRequestHandler.h @@ -0,0 +1,20 @@ + +#import "Nunja.h" +#import "NunjaRequest.h" + +@interface NunjaRequestHandler : NSObject { + NSString *httpMethod; + NSString *path; + id block; // A Nu or C block to be invoked to handle the request. + + NSMutableArray *parts; // internal, used to expand pattern for request routing +} + ++ (NunjaRequestHandler *) handlerWithHTTPMethod:(id)httpMethod path:(id)path block:(id)block; + +- (NSString *) httpMethod; +- (NSString *) path; +- (NSMutableArray *) parts; + +- (BOOL) handleRequest:(NunjaRequest *)request; +@end \ No newline at end of file diff --git a/objc/NunjaRequestHandler.m b/objc/NunjaRequestHandler.m new file mode 100644 index 0000000..0c44681 --- /dev/null +++ b/objc/NunjaRequestHandler.m @@ -0,0 +1,91 @@ +#import "NunjaRequestHandler.h" +#import "NunjaRequest.h" +#import + +@implementation NunjaRequestHandler + +- (NSString *) path { + return path; +} + +- (NSString *) httpMethod { + return httpMethod; +} + +- (NSMutableArray *) parts { + return self->parts; +} + ++ (NunjaRequestHandler *) handlerWithHTTPMethod:(id)httpMethod path:(id)path block:(id)block +{ + NunjaRequestHandler *handler = [[[NunjaRequestHandler alloc] init] autorelease]; + handler->httpMethod = [httpMethod retain]; + handler->path = [path retain]; + handler->parts = [[[NSString stringWithFormat:@"%@%@", httpMethod, path] componentsSeparatedByString:@"/"] retain]; + handler->block = [block retain]; + return handler; +} + +// Handle a request. Used internally. +- (BOOL) handleRequest:(NunjaRequest *)request +{ + if ([Nunja verbose]) { + NSLog(@"handling request %@", [request uri]); + NSLog(@"request from host %@ port %d", [request remoteHost], [request remotePort]); + } + + id args = [[[NSClassFromString(@"NuCell") alloc] init] autorelease]; + [args setCar:request]; + + id body = nil; + if ([block isKindOfClass:NSClassFromString(@"NuBlock")]) { + // evaluate block with request as the single argument + body = [block evalWithArguments:args context:[NSMutableDictionary dictionary]]; + } +#ifdef DARWIN + else { + // evaluate block as a C block + body = ((id(^)(id)) block)(request); + } +#endif + + if ([Nunja verbose]) { + NSLog(@"evaluated with status %d", [request responseCode]); + } + + static id text_html_pattern = nil; + if (!text_html_pattern) { + text_html_pattern = [[NSClassFromString(@"NuRegex") regexWithPattern:@"^text/html.*$"] retain]; + } + + id content_type; + + if (!body || (body == [NSNull null])) { + // return without responding, this means the handler has rejected the URL + return NO; + } + else if ([body isKindOfClass:[NSData class]]) { + // return data objects as-is + // we should set the content type if it isn't set + [request respondWithData:body]; + return YES; // just non-nil + } + else if (![body isKindOfClass:[NSString class]]) { + // return other non-strings as their stringValues + [request respondWithString:[body stringValue]]; + return YES; + } + else if ((content_type = [request valueForResponseHeader:@"Content-Type"]) && ![text_html_pattern findInString:content_type]) { + // if a content type is set and it isn't text/html, return string as-is + [request respondWithString:body]; + return YES; + } + else { + // return string as html + [request setContentType:@"text/html; charset=UTF-8"]; + [request respondWithString:body]; + return YES; + } +} + +@end diff --git a/objc/NunjaRequestRouter.h b/objc/NunjaRequestRouter.h new file mode 100644 index 0000000..768edff --- /dev/null +++ b/objc/NunjaRequestRouter.h @@ -0,0 +1,18 @@ +#import + +@class NunjaRequest; +@class NunjaRequestHandler; + +@interface NunjaRequestRouter : NSObject { + NSMutableDictionary *contents; + NSString *token; + NunjaRequestHandler *handler; +} + ++ (NunjaRequestRouter *) routerWithToken:(id) token; +- (NSString *) token; +- (void) dump:(int) level; +- (id) routeRequest:(NunjaRequest *) request parts:(NSArray *) parts level:(int) level; +- (void) insertHandler:(NunjaRequestHandler *) handler level:(int) level; +@end + diff --git a/objc/NunjaRequestRouter.m b/objc/NunjaRequestRouter.m new file mode 100644 index 0000000..dbb375a --- /dev/null +++ b/objc/NunjaRequestRouter.m @@ -0,0 +1,85 @@ + +#import "NunjaRequestHandler.h" +#import "NunjaRequestRouter.h" +#import "NunjaRequest.h" + +NSString *spaces(int n) { + NSMutableString *result = [NSMutableString string]; + for (int i = 0; i < n; i++) { + [result appendString:@" "]; + } + return result; +} + + +@implementation NunjaRequestRouter + ++ (NunjaRequestRouter *) routerWithToken:(NSString *) token +{ + NunjaRequestRouter *router = [[[self alloc] init] autorelease]; + router->contents = [[NSMutableDictionary alloc] init]; + router->token = [token retain]; + return router; +} + +- (id) token { + return token; +} + +- (void) dump:(int) level +{ + NSLog(@"%@%@\t%@", spaces(level), self->token, self->handler); + id keys = [self->contents allKeys]; + for (int i = 0; i < [keys count]; i++) { + id key = [keys objectAtIndex:i]; + id value = [self->contents objectForKey:key]; + [value dump:(level+1)]; + } +} + +- (id) routeRequest:(NunjaRequest *) request parts:(NSArray *) parts level:(int) level +{ + if (level == [parts count]) { + return self->handler; + } + else { + id key = [parts objectAtIndex:level]; + id response; + id child = [self->contents objectForKey:key]; + if (child && (response = [child routeRequest:request parts:parts level:(level+1)])) { + // if the response is non-null, we have a match + return response; + } + else if (child = [self->contents objectForKey:@":"]) { + [[request bindings] setObject:key forKey:[[child token] substringToIndex:([[child token] length]-1)]]; + return [child routeRequest:request parts:parts level:(level + 1)]; + } + else { + return nil; + } + } +} + +- (void) insertHandler:(NunjaRequestHandler *) h level:(int) level +{ + if (level == [[h parts] count]) { + self->handler = [h retain]; + } + else { + id key = [[h parts] objectAtIndex:level]; + BOOL key_is_wildcard = ([key length] > 0) && ([key characterAtIndex:([key length] - 1)] == ':'); + id child = [self->contents objectForKey:(key_is_wildcard ? @":" : key)]; + if (!child) { + child = [NunjaRequestRouter routerWithToken:key]; + } + if (([key length] > 0) && (([key characterAtIndex:0] == ':') || ([key characterAtIndex:([key length] -1)] == ':'))) { + [self->contents setObject:child forKey:@":"]; + } + else { + [self->contents setObject:child forKey:key]; + } + [child insertHandler:h level:level+1]; + } +} + +@end diff --git a/objc/util.m b/objc/Nunja_daemonize.m similarity index 85% rename from objc/util.m rename to objc/Nunja_daemonize.m index 2d8df5d..2b29e96 100644 --- a/objc/util.m +++ b/objc/Nunja_daemonize.m @@ -1,5 +1,5 @@ /*! -@file util.m +@file Nunja_daemonize.m @discussion General utilities for the Nunja web server. @copyright Copyright (c) 2008 Neon Design Technology, Inc. @@ -27,9 +27,13 @@ #include #import +#import "Nunja.h" // http://www.netzmafia.de/skripten/unix/linux-daemon-howto.html -void daemonize() + +@implementation Nunja (daemonize) + ++ (void) daemonize { pid_t pid, sid; @@ -68,16 +72,4 @@ void daemonize() close(STDERR_FILENO); } -@implementation NSFileManager (Nunja) - -- (BOOL) directoryExistsAtPath:(NSString *) path -{ - BOOL isDirectory = NO; - BOOL fileExists = [self fileExistsAtPath:path isDirectory:&isDirectory]; - if (!fileExists) - return NO; - else - return isDirectory; -} - @end diff --git a/objc/markup.m b/objc/markup.m deleted file mode 100644 index e1e0d14..0000000 --- a/objc/markup.m +++ /dev/null @@ -1,96 +0,0 @@ -#import - -@interface NuOperator : NSObject -{ -} - -- (id) evalWithArguments:(id) cdr context:(NSMutableDictionary *) context; -- (id) callWithArguments:(id) cdr context:(NSMutableDictionary *) context; -@end - -@interface NunjaMarkupOperator : NuOperator -{ - NSString *tag; - NSString *prefix; -} - -- (id) initWithTag:(NSString *) tag; -- (id) initWithTag:(NSString *) tag prefix:(NSString *) prefix; -@end - -@implementation NunjaMarkupOperator - -+ (id) operatorWithTag:(NSString *) _tag -{ - return [[[self alloc] initWithTag:_tag] autorelease]; -} - -+ (id) operatorWithTag:(NSString *) _tag prefix:(NSString *) _prefix -{ - return [[[self alloc] initWithTag:_tag prefix:_prefix] autorelease]; -} - -- (id) initWithTag:(NSString *) _tag -{ - return [self initWithTag:_tag prefix:nil]; -} - -- (id) initWithTag:(NSString *) _tag prefix:(NSString *) _prefix -{ - self = [super init]; - tag = _tag ? [_tag retain] : @"undefined-element"; - prefix = _prefix ? [_prefix retain] : @""; - return self; -} - -- (id) callWithArguments:(id)cdr context:(NSMutableDictionary *)context -{ - NSMutableString *body = [NSMutableString string]; - NSMutableString *attributes = [NSMutableString string]; - - static id NuSymbol = nil; - if (!NuSymbol) { - NuSymbol = NSClassFromString(@"NuSymbol"); - } - - id cursor = cdr; - while (cursor && (cursor != [NSNull null])) { - id item = [cursor car]; - if ([item isKindOfClass:[NuSymbol class]] && [item isLabel]) { - cursor = [cursor cdr]; - if (cursor && (cursor != [NSNull null])) { - id value = [[cursor car] evalWithContext:context]; - [attributes appendFormat:@" %@=\"%@\"", [item labelName], [value stringValue]]; - } - } - else { - id evaluatedItem = [item evalWithContext:context]; - if ([evaluatedItem isKindOfClass:[NSString class]]) { - [body appendString:evaluatedItem]; - } - else if ([evaluatedItem isKindOfClass:[NSArray class]]) { - int max = [evaluatedItem count]; - for (int i = 0; i < max; i++) { - [body appendString:[evaluatedItem objectAtIndex:i]]; - } - } - else if (evaluatedItem == [NSNull null]) { - // do nothing - } - else { - [body appendString:[evaluatedItem stringValue]]; - } - } - if (cursor && (cursor != [NSNull null])) - cursor = [cursor cdr]; - } - - if ([body length]) { - return [NSString stringWithFormat:@"%@<%@%@>%@", prefix, tag, attributes, body, tag]; - } - else { - return [NSString stringWithFormat:@"%@<%@%@/>", prefix, tag, attributes]; - } -} - -@end diff --git a/objc/nunja.h b/objc/nunja.h deleted file mode 100644 index 941f453..0000000 --- a/objc/nunja.h +++ /dev/null @@ -1,6 +0,0 @@ - - -#import - -@interface SuperNunja : NSObject {} -@end \ No newline at end of file diff --git a/objc/nunja.m b/objc/nunja.m deleted file mode 100644 index 9dc8c09..0000000 --- a/objc/nunja.m +++ /dev/null @@ -1,548 +0,0 @@ -/*! -@file nunja.m -@discussion Objective-C components of the Nunja web server. -@copyright Copyright (c) 2008 Neon Design Technology, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -#include -#include -#include -#include -#include -#define HTTP_SEEOTHER 303 -#define HTTP_DENIED 403 - -#include -#include - -#import -#import -#import "nunja.h" - -void NunjaInit() -{ - static initialized = 0; - if (!initialized) { - initialized = 1; - [Nu loadNuFile:@"nunja" fromBundleWithIdentifier:@"nu.programming.nunja" withContext:nil]; - } -} - -@class Nunja; - -@implementation SuperNunja -@end - -static BOOL verbose_nunja = NO; -static BOOL local_nunja = NO; -static BOOL autotags = YES; - -@interface NunjaRequest : NSObject -{ - Nunja *nunja; - struct evhttp_request *req; - NSString *_uri; - NSString *_path; - NSDictionary *_parameters; - NSDictionary *_query; - id _match; - NSDictionary *_bindings; - id _cookies; - int _responded; - int _responseCode; - NSString *_responseMessage; -} - -@end - -@implementation NunjaRequest - -- (id) initWithNunja:(Nunja *)n request:(struct evhttp_request *)r -{ - [super init]; - nunja = n; - req = r; - // get the URI - _uri = [[NSString alloc] initWithCString:evhttp_request_uri(req) encoding:NSUTF8StringEncoding]; - // scan for the path - int max = [_uri length]; - int base = 0; - int i = 0; - unichar c; - while ((i < max) && ((c = [_uri characterAtIndex:i])) && (c != ';') && (c != '?')) - i++; - _path = [[_uri substringToIndex:i] retain]; - // if necessary, scan the object parameters - _parameters = nil; - if (c == ';') { - i = i + 1; - base = i; - while ((i < max) && ((c = [_uri characterAtIndex:i])) && (c != '?')) - i++; - NSString *parameterString = [_uri substringWithRange:NSMakeRange(base, i-base)]; - _parameters = [[parameterString urlQueryDictionary] retain]; - } - // if necessary, scan the query string - _query = nil; - if (c == '?') { - i = i + 1; - base = i; - while ((i < max) && ((c = [_uri characterAtIndex:i]))) - i++; - NSString *queryString = [_uri substringWithRange:NSMakeRange(base, i-base)]; - _query = [[queryString urlQueryDictionary] retain]; - } - // we haven't responded yet - _responded = NO; - // default response code is that everything is ok - _responseCode = HTTP_OK; - _responseMessage = @"OK"; - return self; -} - -- (void) dealloc -{ - [_uri release]; - [_path release]; - [_parameters release]; - [_query release]; - [_match release]; - [_bindings release]; - [_cookies release]; - [super dealloc]; -} - -- (Nunja *) nunja {return nunja;} - -- (NSString *) uri -{ - return _uri; -} - -- (NSString *) path -{ - return _path; -} - -- (NSDictionary *) parameters -{ - return _parameters; -} - -- (NSDictionary *) query -{ - return _query; -} - -- (id) match -{ - return _match; -} - -- (void) setMatch:(id) match -{ - [match retain]; - [_match release]; - _match = match; -} - -- (id) bindings -{ - return _bindings; -} - -- (void) setBindings:(id) bindings -{ - [bindings retain]; - [_bindings release]; - _bindings = bindings; -} - -- (NSData *) body -{ - if (!req->input_buffer->buffer) - return nil; - else { - NSData *data = [NSData dataWithBytes:req->input_buffer->buffer length:req->input_buffer->off]; - } -} - -- (NSString *) command -{ - switch (req->type) { - case EVHTTP_REQ_GET: - return @"GET"; - case EVHTTP_REQ_POST: - return @"POST"; - case EVHTTP_REQ_HEAD: - return @"HEAD"; - default: - return @"UNKNOWN"; - } -} - -- (NSString *) method -{ - return [self command]; -} - -- (NSString *) remoteHost -{ - return [NSString stringWithCString:req->remote_host encoding:NSUTF8StringEncoding]; -} - -- (int) remotePort -{ - return req->remote_port; -} - -static NSDictionary *nunja_request_headers_helper(struct evhttp_request *req) -{ - NSMutableDictionary *dict = [NSMutableDictionary dictionary]; - struct evkeyval *header; - TAILQ_FOREACH(header, req->input_headers, next) { - [dict setObject:[NSString stringWithCString:header->value encoding:NSUTF8StringEncoding] - forKey:[NSString stringWithCString:header->key encoding:NSUTF8StringEncoding]]; - } - return dict; -} - -- (NSDictionary *) requestHeaders -{ - return nunja_request_headers_helper(req); -} - -static NSDictionary *nunja_response_headers_helper(struct evhttp_request *req) -{ - NSMutableDictionary *dict = [NSMutableDictionary dictionary]; - struct evkeyval *header; - TAILQ_FOREACH(header, req->output_headers, next) { - [dict setObject:[NSString stringWithCString:header->value encoding:NSUTF8StringEncoding] - forKey:[NSString stringWithCString:header->key encoding:NSUTF8StringEncoding]]; - } - return dict; -} - -- (NSDictionary *) responseHeaders -{ - return nunja_response_headers_helper(req); -} - -- (int) setValue:(const char *) value forResponseHeader:(const char *) key -{ - return evhttp_add_header(req->output_headers, key, value); -} - -- (NSString *) valueForResponseHeader:(const char *) key -{ - const char *value = evhttp_find_header(req->output_headers, key); - return value ? [NSString stringWithCString:value encoding:NSUTF8StringEncoding] : nil; -} - -- (int) removeResponseHeader:(const char *) key -{ - return evhttp_remove_header(req->output_headers, key); -} - -- (void) clearResponseHeaders -{ - evhttp_clear_headers(req->output_headers); -} - -- (int) responseCode { - return _responseCode; -} - -- (void) setResponseCode:(int) code message:(NSString *) message { - _responseCode = code; - [message retain]; - [_responseMessage release]; - _responseMessage = message; -} - -static void nunja_response_helper(struct evhttp_request *req, int code, NSString *message, NSData *data) -{ - if (verbose_nunja) { - NSLog(@"RESPONSE %d %@ %@", code, message, [nunja_response_headers_helper(req) description]); - } - struct evbuffer *buf = evbuffer_new(); - if (buf == NULL) err(1, "failed to create response buffer"); - evbuffer_add(buf, [data bytes], [data length]); - evhttp_send_reply(req, code, [message cStringUsingEncoding:NSUTF8StringEncoding], buf); - evbuffer_free(buf); -} - -- (BOOL) respondWithString:(NSString *) string -{ - if (!_responded) { - nunja_response_helper(req, _responseCode, _responseMessage, [string dataUsingEncoding:NSUTF8StringEncoding]); - _responded = YES; - } - return YES; -} - -- (BOOL) respondWithData:(NSData *) data -{ - if (!_responded) { - nunja_response_helper(req, _responseCode, _responseMessage, data); - _responded = YES; - } - return YES; -} - -- (BOOL) respondWithCode:(int) code message:(NSString *) message string:(NSString *) string -{ - if (!_responded) { - nunja_response_helper(req, code, message, [string dataUsingEncoding:NSUTF8StringEncoding]); - _responded = YES; - } - return YES; -} - -- (BOOL) respondWithCode:(int) code message:(NSString *) message data:(NSData *) data -{ - if (!_responded) { - nunja_response_helper(req, code, message, data); - _responded = YES; - } - return YES; -} - -@end - -@protocol NunjaController -- (void) handleRequest:(NunjaRequest *)request; -@end - -@interface Nunja : SuperNunja -{ - struct event_base *event_base; - struct evhttp *httpd; - id controller; -} - -- (id) controller; -@end - -@implementation Nunja - -+ (void) setVerbose:(BOOL) v -{ - verbose_nunja = v; -} - -+ (BOOL) verbose {return verbose_nunja;} - -+ (void) setLocalOnly:(BOOL) l -{ - local_nunja = l; -} - -+ (BOOL) localOnly {return local_nunja;} - -+ (void) load -{ - NunjaInit(); -} - -static void nunja_request_handler(struct evhttp_request *req, void *nunja_pointer) -{ - Nunja *nunja = (Nunja *) nunja_pointer; - id controller = [nunja controller]; - if (controller) { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NunjaRequest *request = [[NunjaRequest alloc] initWithNunja:nunja request:req]; - [controller handleRequest:request]; - [request release]; - [pool release]; - } - else { - nunja_response_helper(req, HTTP_OK, @"OK", - [[NSString stringWithFormat:@"Please set the Nunja server controller.
If you are running nunjad, use the '-s' option to specify a site.
\nRequest: %s\n", - evhttp_request_uri(req)] - dataUsingEncoding:NSUTF8StringEncoding]); - } -} - -- (id) init -{ - [super init]; - event_base = event_init(); - evdns_init(); - httpd = evhttp_new(event_base); - evhttp_set_gencb(httpd, nunja_request_handler, self); - controller = nil; - return self; -} - -- (int) bindToAddress:(const char *) address port:(int) port -{ - return evhttp_bind_socket(httpd, address, port); -} - -- (void) run -{ - event_base_dispatch(event_base); -} - -- (void) dealloc -{ - evhttp_free(httpd); - [super dealloc]; -} - -- (id) controller -{ - return controller; -} - -- (void) setController:(id) d -{ - [d retain]; - [controller release]; - controller = d; -} - -@class NuBlock; -@class NuCell; - -static void nunja_dns_gethostbyname_cb(int result, char type, int count, int ttl, void *addresses, void *arg) -{ - id address = nil; - if (result == DNS_ERR_TIMEOUT) { - fprintf(stdout, "[Timed out] "); - } - else if (result != DNS_ERR_NONE) { - fprintf(stdout, "[Error code %d] ", result); - } - else { - fprintf(stdout, "type: %d, count: %d, ttl: %d\n", type, count, ttl); - switch (type) { - case DNS_IPv4_A: - { - struct in_addr *in_addrs = addresses; - if (ttl < 0) { - // invalid resolution - } - else if (count == 0) { - // no addresses - } - else { - address = [NSString stringWithFormat:@"%s", inet_ntoa(in_addrs[0])]; - } - break; - } - case DNS_PTR: - /* may get at most one PTR */ - // this needs review. TB. - if (count == 1) - fprintf(stdout, "addresses: %s ", *(char **)addresses); - break; - default: - break; - } - } - NuBlock *block = (NuBlock *) arg; - NuCell *args = [[NuCell alloc] init]; - [args setCar:address]; - [block evalWithArguments:args context:nil]; - [block release]; - [args release]; -} - -- (void) resolveDomainName:(NSString *) name andDo:(NuBlock *) block -{ - [block retain]; - evdns_resolve_ipv4([name cStringUsingEncoding:NSUTF8StringEncoding], 0, nunja_dns_gethostbyname_cb, block); -} - -void nunja_http_request_done(struct evhttp_request *req, void *arg) -{ - fprintf(stdout, "received %d bytes\n", (int) arg); - NSData *data = nil; - if (req->response_code != HTTP_OK) { - if (req->response_code == HTTP_SEEOTHER) { - fprintf(stdout, "REDIRECTING\n"); - NSDictionary *headers = nunja_request_headers_helper(req); - return; // this is not handled yet. - } - fprintf(stdout, "FAILED to get OK (response = %d)\n", req->response_code); - } - else if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { - fprintf(stdout, "FAILED to find Content-Type\n"); - } - else { - data = [NSData dataWithBytes:EVBUFFER_DATA(req->input_buffer) length:EVBUFFER_LENGTH(req->input_buffer)]; - } - NuBlock *block = (NuBlock *) arg; - NuCell *args = [[NuCell alloc] init]; - [args setCar:data]; - [block evalWithArguments:args context:nil]; - [block release]; - [args release]; - fprintf(stdout, "end of callback\n"); - // leaking... - //evhttp_connection_free(req->evcon); -} - -- (void) getResourceFromHost:(NSString *) host address:(NSString *) address port:(int)port path:(NSString *)path andDo:(NuBlock *) block -{ - [block retain]; - // make the connection - struct evhttp_connection *evcon = evhttp_connection_new([address cStringUsingEncoding:NSUTF8StringEncoding], port); - if (evcon == NULL) { - fprintf(stdout, "FAILED to connect\n"); - NuCell *args = [[NuCell alloc] init]; - [block evalWithArguments:args context:nil]; - [block release]; - [args release]; - return; - } - // make the request - struct evhttp_request *req = evhttp_request_new(nunja_http_request_done, block); - evhttp_add_header(req->output_headers, "Host", [host cStringUsingEncoding:NSUTF8StringEncoding]); - // give ownership of the request to the connection - if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, [path cStringUsingEncoding:NSUTF8StringEncoding]) == -1) { - fprintf(stdout, "FAILED to make the request \n"); - } -} - -- (void) postDataToHost:(NSString *) host address:(NSString *) address port:(int)port path:(NSString *)path data:(NSData *) data andDo:(NuBlock *) block -{ - [block retain]; - // make the connection - struct evhttp_connection *evcon = evhttp_connection_new([address cStringUsingEncoding:NSUTF8StringEncoding], port); - if (evcon == NULL) { - fprintf(stdout, "FAILED to connect\n"); - NuCell *args = [[NuCell alloc] init]; - [block evalWithArguments:args context:nil]; - [block release]; - [args release]; - return; - } - // make the request - struct evhttp_request *req = evhttp_request_new(nunja_http_request_done, block); - evhttp_add_header(req->output_headers, "Host", [host cStringUsingEncoding:NSUTF8StringEncoding]); - evhttp_add_header(req->output_headers, "Content-Length", [[NSString stringWithFormat:@"%d", [data length]] cStringUsingEncoding:NSUTF8StringEncoding]); - evhttp_add_header(req->output_headers, "Content-Type", "application/x-www-form-urlencoded"); - evbuffer_add(req->output_buffer, [data bytes], [data length]); - - // give ownership of the request to the connection - if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, [path cStringUsingEncoding:NSUTF8StringEncoding]) == -1) { - fprintf(stdout, "FAILED to make the request \n"); - } -} - -@end diff --git a/objc/passwd.m b/objc/passwd.m deleted file mode 100644 index 744da6a..0000000 --- a/objc/passwd.m +++ /dev/null @@ -1,182 +0,0 @@ -#import "nunja.h" - -char *md5_crypt(const char *pw, const char *salt); - -@implementation SuperNunja (SaltedPasswords) - -+ (NSString *) saltedPassword:(NSString *) password withSalt:(NSString *) salt -{ - char *passwordString = [password cStringUsingEncoding:NSUTF8StringEncoding]; - char *saltString = [salt cStringUsingEncoding:NSUTF8StringEncoding]; - - size_t pw_maxlen = 256; - - /* truncate password if necessary */ - if ((strlen(passwordString) > pw_maxlen)) { - passwordString[pw_maxlen] = 0; - } - - /* now compute password hash */ - char *hash = md5_crypt(passwordString, saltString); - - return [[NSString alloc] initWithCString:hash encoding:NSUTF8StringEncoding]; -} - -@end - -////// The remainder of this file is covered by the following license: - -/* - * ---------------------------------------------------------------------------- - * "THE BEER-WARE LICENSE" (Revision 42): - * wrote this file. As long as you retain this notice you - * can do whatever you want with this stuff. If we meet some day, and you think - * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp - * ---------------------------------------------------------------------------- - */ - -/* - * Ported from FreeBSD to Linux, only minimal changes. --marekm - */ - -/* - * Adapted from shadow-19990607 by Tudor Bosman, tudorb@jm.nu - */ - -#pragma ident "%Z%%M% %I% %E% SMI" - -#include - -static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ - "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - -static char *magic = "$1$"; /* - * This string is magic for - * this algorithm. Having - * it this way, we can get - * get better later on - */ - -static void -to64(char *s, unsigned long v, int n) -{ - while (--n >= 0) { - *s++ = itoa64[v&0x3f]; - v >>= 6; - } -} - -int -is_md5_salt(const char *salt) -{ - return (!strncmp(salt, magic, strlen(magic))); -} - -/* - * UNIX password - * - * Use MD5 for what it is best at... - */ - -char * -md5_crypt(const char *pw, const char *salt) -{ - static char passwd[120], *p; - static const char *sp,*ep; - unsigned char final[16]; - int sl,pl,i,j; - MD5_CTX ctx,ctx1; - unsigned long l; - - /* Refine the Salt first */ - sp = salt; - - /* If it starts with the magic string, then skip that */ - if(!strncmp(sp,magic,strlen(magic))) - sp += strlen(magic); - - /* It stops at the first '$', max 8 chars */ - for(ep=sp;*ep && *ep != '$' && ep < (sp+8);ep++) - continue; - - /* get the length of the true salt */ - sl = ep - sp; - - MD5_Init(&ctx); - - /* The password first, since that is what is most unknown */ - MD5_Update(&ctx,pw,strlen(pw)); - - /* Then our magic string */ - MD5_Update(&ctx,magic,strlen(magic)); - - /* Then the raw salt */ - MD5_Update(&ctx,sp,sl); - - /* Then just as many characters of the MD5(pw,salt,pw) */ - MD5_Init(&ctx1); - MD5_Update(&ctx1,pw,strlen(pw)); - MD5_Update(&ctx1,sp,sl); - MD5_Update(&ctx1,pw,strlen(pw)); - MD5_Final(final,&ctx1); - for(pl = strlen(pw); pl > 0; pl -= 16) - MD5_Update(&ctx,final,pl>16 ? 16 : pl); - - /* Don't leave anything around in vm they could use. */ - memset(final,0,sizeof final); - - /* Then something really weird... */ - for (j=0,i = strlen(pw); i ; i >>= 1) - if(i&1) - MD5_Update(&ctx, final+j, 1); - else - MD5_Update(&ctx, pw+j, 1); - - /* Now make the output string */ - strcpy(passwd,magic); - strncat(passwd,sp,sl); - strcat(passwd,"$"); - - MD5_Final(final,&ctx); - - /* - * and now, just to make sure things don't run too fast - * On a 60 Mhz Pentium this takes 34 msec, so you would - * need 30 seconds to build a 1000 entry dictionary... - */ - for(i=0;i<1000;i++) { - MD5_Init(&ctx1); - if(i & 1) - MD5_Update(&ctx1,pw,strlen(pw)); - else - MD5_Update(&ctx1,final,16); - - if(i % 3) - MD5_Update(&ctx1,sp,sl); - - if(i % 7) - MD5_Update(&ctx1,pw,strlen(pw)); - - if(i & 1) - MD5_Update(&ctx1,final,16); - else - MD5_Update(&ctx1,pw,strlen(pw)); - MD5_Final(final,&ctx1); - } - - p = passwd + strlen(passwd); - - l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4; - l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4; - l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4; - l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4; - l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4; - l = final[11] ; to64(p,l,2); p += 2; - *p = '\0'; - - /* Don't leave anything around in vm they could use. */ - memset(final,0,sizeof final); - - return passwd; -} - diff --git a/sample/site.nu b/sample/site.nu index f2caf7b..6cddc30 100644 --- a/sample/site.nu +++ b/sample/site.nu @@ -15,6 +15,47 @@ ;; limitations under the License. (load "NuHTTPHelpers") +(load "Nu:template") + +;; import some useful C functions +(global random (NuBridgedFunction functionWithName:"random" signature:"l")) +(global srandom (NuBridgedFunction functionWithName:"srandom" signature:"vI")) + +(class NunjaRequest + (- post is ((self body) urlQueryDictionary))) + +;; @class NunjaCookie +;; @discussion A class for managing user-identifying cookies. +(class NunjaCookie is NSObject + (ivars) + (ivar-accessors) + + ;; Generate a random identifier for use in a cookie. + (+ (id) randomIdentifier is + "#{((random) stringValue)}#{((random) stringValue)}#{((random) stringValue)}#{((random) stringValue)}") + + ;; Construct a cookie for a specified user. + (+ (id) cookieForUser:(id) user is + ((self alloc) initWithUser:user + value:(self randomIdentifier) + expiration:(NSDate dateWithTimeIntervalSinceNow:3600))) + + ;; Initialize a cookie for a specified user. + (- (id) initWithUser:(id) user value:(id) value expiration:(id) expiration is + (super init) + (set @name "session") + (set @user user) + (set @value value) + (set @expiration expiration) + (set @stringValue nil) + self) + + ;; Get a string description of a cookie. + (- (id) description is + "cookie=#{@name} value=#{@value} user=#{@user} expiration=#{(@expiration rfc822)}") + + ;; Get a string value for a cookie suitable for inclusion in a response header. + (- (id) stringValue is "#{@name}=#{@value}; Expires:#{(@expiration rfc1123)}; Path=/")) ;; global variables (set sessionCookies (dict)) @@ -88,7 +129,7 @@ HTML)) forKey:"BODY") (set post (REQUEST post)) (if (eq (post "response") "Cancel") (then - (Nunja redirectResponse:REQUEST toLocation:"/")) + (REQUEST redirectResponseToLocation:"/")) (else (set username (post "username")) (set password (post "password")) @@ -97,7 +138,7 @@ HTML)) forKey:"BODY") (set sessionCookie (NunjaCookie cookieForUser:username)) (sessionCookies setObject:sessionCookie forKey:(sessionCookie value)) (REQUEST setValue:(sessionCookie stringValue) forResponseHeader:"Set-Cookie") - (Nunja redirectResponse:REQUEST toLocation:"/")) + (REQUEST redirectResponseToLocation:"/")) (else (RESPONSE setValue:"Please try again" forKey:"TITLE") (RESPONSE setValue:(eval (NuTemplate codeForString:<<-HTML @@ -115,7 +156,7 @@ HTML)) forKey:"BODY") (get "/logout" (set sessionCookieName ((REQUEST cookies) "session")) (if sessionCookieName (sessionCookies removeObjectForKey:sessionCookieName)) - (Nunja redirectResponse:REQUEST toLocation:"/")) + (REQUEST redirectResponseToLocation:"/")) ;; add-a-friend page. (get "/addfriend" @@ -139,13 +180,13 @@ HTML)) forKey:"BODY") (set post (REQUEST post)) (if (eq (post "response") "Submit") (friends << (dict name:(post "name") email:(post "email")))) - (Nunja redirectResponse:REQUEST toLocation:"/")) + (REQUEST redirectResponseToLocation:"/")) ;; delete-a-friend with a GET. Strictly, this should be a post, but we use a get to show how it would be done. (get "/delete" (set post (REQUEST query)) (set friends (friends select:(do (friend) (!= (friend "name") (post "name"))))) - (Nunja redirectResponse:REQUEST toLocation:"/")) + (REQUEST redirectResponseToLocation:"/")) (get "/about" (set RESPONSE (dict)) @@ -186,9 +227,9 @@ END forKey:"BODY") (eval page-layout)) ;; large file download -(get (regex -"/data(.*)") +(get "/data/size:" (REQUEST setValue:"application/octet-stream" forResponseHeader:"Content-Type") - (set size ((REQUEST match) groupAtIndex:1)) + (set size ((REQUEST bindings) size:)) (set megabytes (if (eq size "") then 1 else (size doubleValue))) @@ -196,8 +237,8 @@ END forKey:"BODY") ;; perform a dns lookup ;; ex: /dns/programming.nu -(get (regex -"/dns/(.*)") - (set hostname ((REQUEST match) groupAtIndex:1)) +(get "/dns/hostname:" + (set hostname ((REQUEST bindings) hostname:)) ((REQUEST nunja) resolveDomainName:hostname andDo: (do (address) (if address @@ -206,23 +247,25 @@ END forKey:"BODY") (REQUEST respondWithString:"unable to resolve #{hostname}")))) nil) ;; return nil to leave the connection open -;; request and return a resource from a specified host -;; ex: /proxy/programming.nu/about -(get (regex -"/proxy/([^\/]+)/(.*)") - (set host ((REQUEST match) groupAtIndex:1)) - (set path (+ "/" ((REQUEST match) groupAtIndex:2))) - ((REQUEST nunja) resolveDomainName:host andDo: - (do (address) - (if address - (then ((REQUEST nunja) getResourceFromHost:host address:address port:80 path:path andDo: - (do (data) - (if data - (then (REQUEST respondWithData:data)) - (else (REQUEST respondWithString:"unable to load #{path}")))))) - (else (REQUEST respondWithString:"unable to resolve host #{host}"))))) - nil) ;; return nil to leave the connection open +(if NO + ;; request and return a resource from a specified host + ;; ex: /proxy/programming.nu/about + (get (regex -"/proxy/([^\/]+)/(.*)") + (set host ((REQUEST match) groupAtIndex:1)) + (set path (+ "/" ((REQUEST match) groupAtIndex:2))) + ((REQUEST nunja) resolveDomainName:host andDo: + (do (address) + (if address + (then ((REQUEST nunja) getResourceFromHost:host address:address port:80 path:path andDo: + (do (data) + (if data + (then (REQUEST respondWithData:data)) + (else (REQUEST respondWithString:"unable to load #{path}")))))) + (else (REQUEST respondWithString:"unable to resolve host #{host}"))))) + nil) ;; return nil to leave the connection open + ) -(get (regex -"/posttest") +(get "/posttest" (set host "localhost") (set path "/login") ((REQUEST nunja) resolveDomainName:host andDo: @@ -252,19 +295,16 @@ END forKey:"BODY") (get "/recycle.ico" (REQUEST setValue:"application/icon" forResponseHeader:"Content-Type") - (NSData dataWithContentsOfFile:"sample/public/favicon.ico")) + (NSData dataWithContentsOfFile:"public/favicon.ico")) -(get "/follow/:me" +(get "/follow/me:" (REQUEST setValue:"text/plain" forResponseHeader:"Content-Type") (+ "/follow/" ((REQUEST bindings) "me"))) -(get "/:a/before/:b" +(get "/a:/before/b:" (REQUEST setValue:"text/plain" forResponseHeader:"Content-Type") (+ "/" ((REQUEST bindings) "b") "/after/" ((REQUEST bindings) "a"))) -(get (regex -"^/foo/([^/]+)$") - ((REQUEST match) groupAtIndex:1)) - (get "/get" (set q (REQUEST query)) (set a (((q allKeys) sort) map: @@ -279,6 +319,7 @@ END forKey:"BODY") (+ key ":" (q key))))) (a componentsJoinedByString:",")) - +(get-404 + "Resource Not Found: #{(REQUEST path)}") diff --git a/test/test_markup.nu b/test/test_markup.nu deleted file mode 100644 index 0fa44fd..0000000 --- a/test/test_markup.nu +++ /dev/null @@ -1,19 +0,0 @@ -;; test_markup.nu -;; tests for Nunja markup operator -;; -;; Copyright (c) 2010 Tim Burks, Neon Design Technology, Inc. - -(load "Nunja") - -(class TestMarkup is NuTestCase - - (- testMarkup is - (set &html (NunjaMarkupOperator operatorWithTag:"html" prefix:"\n")) - (set &body (NunjaMarkupOperator operatorWithTag:"body")) - - (assert_equal "" (&body)) - (assert_equal "" (&body incomplete:)) - (assert_equal "" (&body attr:"val")) - (assert_equal "\nhello, world" (&html (&body this:"is" a:"test" "hello," " world"))) - (assert_equal "abc123" (&body (array "a" "b" "c") 123)) - (assert_equal "12." (&body "1" (if (eq 1 1) 2) (if (eq 1 2) 3) ".")))) diff --git a/test/test_salt.nu b/test/test_salt.nu deleted file mode 100644 index 2e2dd56..0000000 --- a/test/test_salt.nu +++ /dev/null @@ -1,15 +0,0 @@ -;; test_salt.nu -;; tests for Nunja password salting helper. -;; -;; Copyright (c) 2007 Tim Burks, Neon Design Technology, Inc. - -(load "Nunja") - -(class TestSalt is NuTestCase - - (- testSalt is - (set salted (Nunja saltedPassword:"secret" withSalt:"sauce")) - ;; golden result obtained with "openssl passwd -1 -salt sauce" - (assert_equal "$1$sauce$ToKwxvX1ZyeiswSSzdPRi0" salted))) - -