diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e0c6a14 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# OS X +.DS_Store + +# Xcode +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +profile +*.moved-aside +DerivedData +*.hmap +*.xccheckout + +# CocoaPods +Pods + +# appledoc +docset-installed.txt + diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..440c1b2 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "mongoose"] + path = mongoose + url = git://github.com/CIM/mongoose.git diff --git a/MongooseDaemon.h b/MongooseDaemon.h deleted file mode 100644 index 8d4d905..0000000 --- a/MongooseDaemon.h +++ /dev/null @@ -1,51 +0,0 @@ -// -// MongooseDaemon.h -// -// Created by Rama McIntosh on 3/4/09. -// Copyright Rama McIntosh 2009. All rights reserved. -// - - -// -// Copyright (c) 2009, Rama McIntosh All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of Rama McIntosh nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// - -#import "mongoose.h" - -@interface MongooseDaemon : NSObject { - struct mg_context *ctx; -} - -@property (readwrite) struct mg_context *ctx; - -- (void)startMongooseDaemon:(NSString *)ports; -- (void)stopMongooseDaemon; - -@end diff --git a/MongooseDaemon.m b/MongooseDaemon.m deleted file mode 100644 index 4e5639d..0000000 --- a/MongooseDaemon.m +++ /dev/null @@ -1,100 +0,0 @@ -// -// MongooseDaemon.m -// -// Created by Rama McIntosh on 3/4/09. -// Copyright Rama McIntosh 2009. All rights reserved. -// - -// -// Copyright (c) 2009, Rama McIntosh All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of Rama McIntosh nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// - -// MongooseDaemon is a small wrapper to make ingetrating mongoose -// with iPhone apps super easy - -#import "MongooseDaemon.h" -#include -#include -#include - -//#define DOCUMENTS_FOLDER [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] -#define DOCUMENTS_FOLDER NSHomeDirectory() - -@implementation MongooseDaemon - -@synthesize ctx; - - -// Return the localized IP address - From Erica Sadun's cookbook -- (NSString *) localIPAddress -{ - char baseHostName[255]; - gethostname(baseHostName, 255); - - // Adjust for iPhone -- add .local to the host name - char hn[255]; - sprintf(hn, "%s.local", baseHostName); - - struct hostent *host = gethostbyname(hn); - if (host == NULL) - { - herror("resolv"); - return NULL; - } - else { - struct in_addr **list = (struct in_addr **)host->h_addr_list; - return [NSString stringWithCString:inet_ntoa(*list[0])]; - } - - return NULL; -} - -- (void)startHTTP:(NSString *)ports -{ - self.ctx = mg_start(); // Start Mongoose serving thread - mg_set_option(ctx, "root", [DOCUMENTS_FOLDER UTF8String]); // Set document root - mg_set_option(ctx, "ports", [ports UTF8String]); // Listen on port XXXX - //mg_bind_to_uri(ctx, "/foo", &bar, NULL); // Setup URI handler - - // Now Mongoose is up, running and configured. - // Serve until somebody terminates us - NSLog(@"Mongoose Server is running on http://%@:8080", [self localIPAddress]); -} - -- (void)startMongooseDaemon:(NSString *)ports; -{ - [self startHTTP:ports]; -} - -- (void)stopMongooseDaemon -{ - mg_stop(ctx); -} - -@end diff --git a/MongooseDaemon/MongooseDaemon.xcodeproj/project.pbxproj b/MongooseDaemon/MongooseDaemon.xcodeproj/project.pbxproj new file mode 100644 index 0000000..d2278c5 --- /dev/null +++ b/MongooseDaemon/MongooseDaemon.xcodeproj/project.pbxproj @@ -0,0 +1,1272 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 19FB6B201842832800C71880 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 19FB6B1F1842832800C71880 /* XCTest.framework */; }; + 19FB6B241842832800C71880 /* libMongooseDaemon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19FB6B0B1842832800C71880 /* libMongooseDaemon.a */; }; + 19FB6B2A1842832800C71880 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 19FB6B281842832800C71880 /* InfoPlist.strings */; }; + 19FB6B2C1842832800C71880 /* MongooseDaemonTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 19FB6B2B1842832800C71880 /* MongooseDaemonTests.m */; }; + 19FB6B6B1842843900C71880 /* MongooseDaemon.m in Sources */ = {isa = PBXBuildFile; fileRef = 19FB6B691842843900C71880 /* MongooseDaemon.m */; }; + 19FB6B6F184286C600C71880 /* mongoose.c in Sources */ = {isa = PBXBuildFile; fileRef = 19FB6B6D184286C600C71880 /* mongoose.c */; }; + 19FB6B9A18428AD000C71880 /* mongoose.c in Sources */ = {isa = PBXBuildFile; fileRef = 19FB6B6D184286C600C71880 /* mongoose.c */; }; + 19FB6B9C18428ADC00C71880 /* MongooseDaemon.m in Sources */ = {isa = PBXBuildFile; fileRef = 19FB6B691842843900C71880 /* MongooseDaemon.m */; }; + 19FB6BA318428FA600C71880 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 19FB6B111842832800C71880 /* Foundation.framework */; }; + 19FB6BA518428FA600C71880 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 19FB6BA418428FA600C71880 /* CoreGraphics.framework */; }; + 19FB6BA718428FA600C71880 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 19FB6BA618428FA600C71880 /* UIKit.framework */; }; + 19FB6BAD18428FA600C71880 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 19FB6BAB18428FA600C71880 /* InfoPlist.strings */; }; + 19FB6BAF18428FA600C71880 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 19FB6BAE18428FA600C71880 /* main.m */; }; + 19FB6BB318428FA600C71880 /* CIMAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 19FB6BB218428FA600C71880 /* CIMAppDelegate.m */; }; + 19FB6BB518428FA600C71880 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 19FB6BB418428FA600C71880 /* Images.xcassets */; }; + 19FB6BBB18428FA600C71880 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 19FB6B1F1842832800C71880 /* XCTest.framework */; }; + 19FB6BBC18428FA600C71880 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 19FB6B111842832800C71880 /* Foundation.framework */; }; + 19FB6BBD18428FA600C71880 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 19FB6BA618428FA600C71880 /* UIKit.framework */; }; + 19FB6BC518428FA600C71880 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 19FB6BC318428FA600C71880 /* InfoPlist.strings */; }; + 19FB6BC718428FA600C71880 /* iOSMongooseExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 19FB6BC618428FA600C71880 /* iOSMongooseExampleTests.m */; }; + 19FB6BD018428FF600C71880 /* CIMMongooseExampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 19FB6BCF18428FF600C71880 /* CIMMongooseExampleViewController.m */; }; + 19FB6BD11842903600C71880 /* libMongooseDaemon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19FB6B0B1842832800C71880 /* libMongooseDaemon.a */; }; + 19FB6BD41842914E00C71880 /* HelloWorld.html in Resources */ = {isa = PBXBuildFile; fileRef = 19FB6BD31842914E00C71880 /* HelloWorld.html */; }; + 19FB6BDA18429C7C00C71880 /* CIMSwitchCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 19FB6BD918429C7C00C71880 /* CIMSwitchCell.xib */; }; + 19FB6BDD18429D5800C71880 /* CIMSwitchCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 19FB6BDC18429D5800C71880 /* CIMSwitchCell.m */; }; + 19FB6BE01842A02B00C71880 /* CIMTextFieldCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 19FB6BDF1842A02B00C71880 /* CIMTextFieldCell.m */; }; + 19FB6BE21842A03A00C71880 /* CIMTextFieldCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 19FB6BE11842A03A00C71880 /* CIMTextFieldCell.xib */; }; + E2D85CA2184690B700044FD9 /* MongooseDaemon.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 19FB6B681842843900C71880 /* MongooseDaemon.h */; }; + E2D85CA4184690DE00044FD9 /* MongooseDaemon.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 19FB6B681842843900C71880 /* MongooseDaemon.h */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 19FB6B221842832800C71880 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 19FB6B031842832800C71880 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 19FB6B0A1842832800C71880; + remoteInfo = MongooseDaemon; + }; + 19FB6BBE18428FA600C71880 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 19FB6B031842832800C71880 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 19FB6BA118428FA600C71880; + remoteInfo = iOSMongooseExample; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + E2D85CA11846908400044FD9 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/${PRODUCT_NAME}"; + dstSubfolderSpec = 16; + files = ( + E2D85CA2184690B700044FD9 /* MongooseDaemon.h in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E2D85CA3184690D600044FD9 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/${PRODUCT_NAME}"; + dstSubfolderSpec = 16; + files = ( + E2D85CA4184690DE00044FD9 /* MongooseDaemon.h in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 190D72B41843CD5400D0A555 /* MongooseDaemon_MongooseCallbacks.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MongooseDaemon_MongooseCallbacks.h; sourceTree = ""; }; + 193861DC185698A200EFEDED /* MongooseDaemonVersion.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MongooseDaemonVersion.h; path = MongooseDaemon/MongooseDaemonVersion.h; sourceTree = SOURCE_ROOT; }; + 193861DD18569E6C00EFEDED /* MongooseDaemon-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "MongooseDaemon-Info.plist"; sourceTree = ""; }; + 1947CAAE185759B000C7CDE3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 19FB6B0B1842832800C71880 /* libMongooseDaemon.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libMongooseDaemon.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 19FB6B111842832800C71880 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 19FB6B161842832800C71880 /* MongooseDaemon-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MongooseDaemon-Prefix.pch"; sourceTree = ""; }; + 19FB6B1E1842832800C71880 /* MongooseDaemonTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MongooseDaemonTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 19FB6B1F1842832800C71880 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + 19FB6B271842832800C71880 /* MongooseDaemonTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "MongooseDaemonTests-Info.plist"; sourceTree = ""; }; + 19FB6B291842832800C71880 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 19FB6B2B1842832800C71880 /* MongooseDaemonTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MongooseDaemonTests.m; sourceTree = ""; }; + 19FB6B681842843900C71880 /* MongooseDaemon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MongooseDaemon.h; sourceTree = ""; }; + 19FB6B691842843900C71880 /* MongooseDaemon.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MongooseDaemon.m; sourceTree = ""; }; + 19FB6B6D184286C600C71880 /* mongoose.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mongoose.c; sourceTree = ""; }; + 19FB6B6E184286C600C71880 /* mongoose.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mongoose.h; sourceTree = ""; }; + 19FB6B7618428A7200C71880 /* MongooseDaemon.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MongooseDaemon.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 19FB6BA218428FA600C71880 /* iOSMongooseExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iOSMongooseExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 19FB6BA418428FA600C71880 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 19FB6BA618428FA600C71880 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; + 19FB6BAA18428FA600C71880 /* iOSMongooseExample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "iOSMongooseExample-Info.plist"; sourceTree = ""; }; + 19FB6BAC18428FA600C71880 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 19FB6BAE18428FA600C71880 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 19FB6BB018428FA600C71880 /* iOSMongooseExample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "iOSMongooseExample-Prefix.pch"; sourceTree = ""; }; + 19FB6BB118428FA600C71880 /* CIMAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CIMAppDelegate.h; sourceTree = ""; }; + 19FB6BB218428FA600C71880 /* CIMAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CIMAppDelegate.m; sourceTree = ""; }; + 19FB6BB418428FA600C71880 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 19FB6BBA18428FA600C71880 /* iOSMongooseExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iOSMongooseExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 19FB6BC218428FA600C71880 /* iOSMongooseExampleTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "iOSMongooseExampleTests-Info.plist"; sourceTree = ""; }; + 19FB6BC418428FA600C71880 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 19FB6BC618428FA600C71880 /* iOSMongooseExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = iOSMongooseExampleTests.m; sourceTree = ""; }; + 19FB6BCE18428FF600C71880 /* CIMMongooseExampleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CIMMongooseExampleViewController.h; sourceTree = ""; }; + 19FB6BCF18428FF600C71880 /* CIMMongooseExampleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CIMMongooseExampleViewController.m; sourceTree = ""; }; + 19FB6BD31842914E00C71880 /* HelloWorld.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = HelloWorld.html; sourceTree = ""; }; + 19FB6BD918429C7C00C71880 /* CIMSwitchCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CIMSwitchCell.xib; sourceTree = ""; }; + 19FB6BDB18429D5800C71880 /* CIMSwitchCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CIMSwitchCell.h; sourceTree = ""; }; + 19FB6BDC18429D5800C71880 /* CIMSwitchCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CIMSwitchCell.m; sourceTree = ""; }; + 19FB6BDE1842A02B00C71880 /* CIMTextFieldCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CIMTextFieldCell.h; sourceTree = ""; }; + 19FB6BDF1842A02B00C71880 /* CIMTextFieldCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CIMTextFieldCell.m; sourceTree = ""; }; + 19FB6BE11842A03A00C71880 /* CIMTextFieldCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CIMTextFieldCell.xib; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 19FB6B081842832800C71880 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 19FB6B1B1842832800C71880 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 19FB6B241842832800C71880 /* libMongooseDaemon.a in Frameworks */, + 19FB6B201842832800C71880 /* XCTest.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 19FB6B7218428A7200C71880 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 19FB6B9F18428FA600C71880 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 19FB6BD11842903600C71880 /* libMongooseDaemon.a in Frameworks */, + 19FB6BA518428FA600C71880 /* CoreGraphics.framework in Frameworks */, + 19FB6BA718428FA600C71880 /* UIKit.framework in Frameworks */, + 19FB6BA318428FA600C71880 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 19FB6BB718428FA600C71880 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 19FB6BBB18428FA600C71880 /* XCTest.framework in Frameworks */, + 19FB6BBD18428FA600C71880 /* UIKit.framework in Frameworks */, + 19FB6BBC18428FA600C71880 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 19FB6B021842832800C71880 = { + isa = PBXGroup; + children = ( + 19FB6B141842832800C71880 /* MongooseDaemon */, + 19FB6B251842832800C71880 /* MongooseDaemonTests */, + 19FB6BA818428FA600C71880 /* iOSMongooseExample */, + 19FB6BC018428FA600C71880 /* iOSMongooseExampleTests */, + 19FB6B0D1842832800C71880 /* Frameworks */, + 19FB6B0C1842832800C71880 /* Products */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + }; + 19FB6B0C1842832800C71880 /* Products */ = { + isa = PBXGroup; + children = ( + 19FB6B0B1842832800C71880 /* libMongooseDaemon.a */, + 19FB6B1E1842832800C71880 /* MongooseDaemonTests.xctest */, + 19FB6B7618428A7200C71880 /* MongooseDaemon.framework */, + 19FB6BA218428FA600C71880 /* iOSMongooseExample.app */, + 19FB6BBA18428FA600C71880 /* iOSMongooseExampleTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 19FB6B0D1842832800C71880 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 19FB6B1F1842832800C71880 /* XCTest.framework */, + 19FB6B111842832800C71880 /* Foundation.framework */, + 19FB6BA418428FA600C71880 /* CoreGraphics.framework */, + 19FB6BA618428FA600C71880 /* UIKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 19FB6B141842832800C71880 /* MongooseDaemon */ = { + isa = PBXGroup; + children = ( + 19FB6B6C1842846100C71880 /* mongoose */, + 193861DC185698A200EFEDED /* MongooseDaemonVersion.h */, + 19FB6B681842843900C71880 /* MongooseDaemon.h */, + 190D72B41843CD5400D0A555 /* MongooseDaemon_MongooseCallbacks.h */, + 19FB6B691842843900C71880 /* MongooseDaemon.m */, + 19FB6B151842832800C71880 /* Supporting Files */, + ); + path = MongooseDaemon; + sourceTree = ""; + }; + 19FB6B151842832800C71880 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 193861DD18569E6C00EFEDED /* MongooseDaemon-Info.plist */, + 1947CAAD185759B000C7CDE3 /* InfoPlist.strings */, + 19FB6B161842832800C71880 /* MongooseDaemon-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 19FB6B251842832800C71880 /* MongooseDaemonTests */ = { + isa = PBXGroup; + children = ( + 19FB6B2B1842832800C71880 /* MongooseDaemonTests.m */, + 19FB6B261842832800C71880 /* Supporting Files */, + ); + path = MongooseDaemonTests; + sourceTree = ""; + }; + 19FB6B261842832800C71880 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 19FB6B271842832800C71880 /* MongooseDaemonTests-Info.plist */, + 19FB6B281842832800C71880 /* InfoPlist.strings */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 19FB6B6C1842846100C71880 /* mongoose */ = { + isa = PBXGroup; + children = ( + 19FB6B6D184286C600C71880 /* mongoose.c */, + 19FB6B6E184286C600C71880 /* mongoose.h */, + ); + name = mongoose; + path = ../../mongoose; + sourceTree = ""; + }; + 19FB6BA818428FA600C71880 /* iOSMongooseExample */ = { + isa = PBXGroup; + children = ( + 19FB6BB118428FA600C71880 /* CIMAppDelegate.h */, + 19FB6BB218428FA600C71880 /* CIMAppDelegate.m */, + 19FB6BCE18428FF600C71880 /* CIMMongooseExampleViewController.h */, + 19FB6BCF18428FF600C71880 /* CIMMongooseExampleViewController.m */, + 19FB6BE31842A8BD00C71880 /* Custom Cells */, + 19FB6BB418428FA600C71880 /* Images.xcassets */, + 19FB6BA918428FA600C71880 /* Supporting Files */, + 19FB6BD21842912F00C71880 /* Resources */, + ); + path = iOSMongooseExample; + sourceTree = ""; + }; + 19FB6BA918428FA600C71880 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 19FB6BAA18428FA600C71880 /* iOSMongooseExample-Info.plist */, + 19FB6BAB18428FA600C71880 /* InfoPlist.strings */, + 19FB6BAE18428FA600C71880 /* main.m */, + 19FB6BB018428FA600C71880 /* iOSMongooseExample-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 19FB6BC018428FA600C71880 /* iOSMongooseExampleTests */ = { + isa = PBXGroup; + children = ( + 19FB6BC618428FA600C71880 /* iOSMongooseExampleTests.m */, + 19FB6BC118428FA600C71880 /* Supporting Files */, + ); + path = iOSMongooseExampleTests; + sourceTree = ""; + }; + 19FB6BC118428FA600C71880 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 19FB6BC218428FA600C71880 /* iOSMongooseExampleTests-Info.plist */, + 19FB6BC318428FA600C71880 /* InfoPlist.strings */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 19FB6BD21842912F00C71880 /* Resources */ = { + isa = PBXGroup; + children = ( + 19FB6BD31842914E00C71880 /* HelloWorld.html */, + ); + name = Resources; + sourceTree = ""; + }; + 19FB6BE31842A8BD00C71880 /* Custom Cells */ = { + isa = PBXGroup; + children = ( + 19FB6BD918429C7C00C71880 /* CIMSwitchCell.xib */, + 19FB6BDB18429D5800C71880 /* CIMSwitchCell.h */, + 19FB6BDC18429D5800C71880 /* CIMSwitchCell.m */, + 19FB6BE11842A03A00C71880 /* CIMTextFieldCell.xib */, + 19FB6BDE1842A02B00C71880 /* CIMTextFieldCell.h */, + 19FB6BDF1842A02B00C71880 /* CIMTextFieldCell.m */, + ); + name = "Custom Cells"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 19FB6B0A1842832800C71880 /* libMongooseDaemon.a */ = { + isa = PBXNativeTarget; + buildConfigurationList = 19FB6B2F1842832800C71880 /* Build configuration list for PBXNativeTarget "libMongooseDaemon.a" */; + buildPhases = ( + 19FB6B071842832800C71880 /* Sources */, + 19FB6B081842832800C71880 /* Frameworks */, + E2D85CA11846908400044FD9 /* CopyFiles */, + 19BBA1941858B1F30001A27E /* appledoc */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libMongooseDaemon.a; + productName = MongooseDaemon; + productReference = 19FB6B0B1842832800C71880 /* libMongooseDaemon.a */; + productType = "com.apple.product-type.library.static"; + }; + 19FB6B1D1842832800C71880 /* MongooseDaemonTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 19FB6B321842832800C71880 /* Build configuration list for PBXNativeTarget "MongooseDaemonTests" */; + buildPhases = ( + 19FB6B1A1842832800C71880 /* Sources */, + 19FB6B1B1842832800C71880 /* Frameworks */, + 19FB6B1C1842832800C71880 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 19FB6B231842832800C71880 /* PBXTargetDependency */, + ); + name = MongooseDaemonTests; + productName = MongooseDaemonTests; + productReference = 19FB6B1E1842832800C71880 /* MongooseDaemonTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 19FB6B7518428A7200C71880 /* MongooseDaemon.framework */ = { + isa = PBXNativeTarget; + buildConfigurationList = 19FB6B9418428A7200C71880 /* Build configuration list for PBXNativeTarget "MongooseDaemon.framework" */; + buildPhases = ( + 19FB6B7118428A7200C71880 /* Sources */, + 19FB6B7218428A7200C71880 /* Frameworks */, + E2D85CA3184690D600044FD9 /* CopyFiles */, + 19BBA1961858E50C0001A27E /* appledoc */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MongooseDaemon.framework; + productName = MongooseDaemon; + productReference = 19FB6B7618428A7200C71880 /* MongooseDaemon.framework */; + productType = "com.apple.product-type.framework"; + }; + 19FB6BA118428FA600C71880 /* iOSMongooseExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 19FB6BC818428FA600C71880 /* Build configuration list for PBXNativeTarget "iOSMongooseExample" */; + buildPhases = ( + 19FB6B9E18428FA600C71880 /* Sources */, + 19FB6B9F18428FA600C71880 /* Frameworks */, + 19FB6BA018428FA600C71880 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = iOSMongooseExample; + productName = iOSMongooseExample; + productReference = 19FB6BA218428FA600C71880 /* iOSMongooseExample.app */; + productType = "com.apple.product-type.application"; + }; + 19FB6BB918428FA600C71880 /* iOSMongooseExampleTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 19FB6BCB18428FA600C71880 /* Build configuration list for PBXNativeTarget "iOSMongooseExampleTests" */; + buildPhases = ( + 19FB6BB618428FA600C71880 /* Sources */, + 19FB6BB718428FA600C71880 /* Frameworks */, + 19FB6BB818428FA600C71880 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 19FB6BBF18428FA600C71880 /* PBXTargetDependency */, + ); + name = iOSMongooseExampleTests; + productName = iOSMongooseExampleTests; + productReference = 19FB6BBA18428FA600C71880 /* iOSMongooseExampleTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 19FB6B031842832800C71880 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0510; + ORGANIZATIONNAME = CIM; + TargetAttributes = { + 19FB6B1D1842832800C71880 = { + TestTargetID = 19FB6B7518428A7200C71880; + }; + 19FB6BB918428FA600C71880 = { + TestTargetID = 19FB6B7518428A7200C71880; + }; + }; + }; + buildConfigurationList = 19FB6B061842832800C71880 /* Build configuration list for PBXProject "MongooseDaemon" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 19FB6B021842832800C71880; + productRefGroup = 19FB6B0C1842832800C71880 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 19FB6B0A1842832800C71880 /* libMongooseDaemon.a */, + 19FB6B7518428A7200C71880 /* MongooseDaemon.framework */, + 19FB6B1D1842832800C71880 /* MongooseDaemonTests */, + 19FB6BA118428FA600C71880 /* iOSMongooseExample */, + 19FB6BB918428FA600C71880 /* iOSMongooseExampleTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 19FB6B1C1842832800C71880 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 19FB6B2A1842832800C71880 /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 19FB6BA018428FA600C71880 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 19FB6BE21842A03A00C71880 /* CIMTextFieldCell.xib in Resources */, + 19FB6BD41842914E00C71880 /* HelloWorld.html in Resources */, + 19FB6BAD18428FA600C71880 /* InfoPlist.strings in Resources */, + 19FB6BDA18429C7C00C71880 /* CIMSwitchCell.xib in Resources */, + 19FB6BB518428FA600C71880 /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 19FB6BB818428FA600C71880 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 19FB6BC518428FA600C71880 /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 19BBA1941858B1F30001A27E /* appledoc */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = appledoc; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"generating documentation\"\nif type appledoc &> /dev/null ; then\n appledoc \\\n --output ${SOURCE_ROOT} \\\n --create-html \\\n --project-name ${PRODUCT_NAME} \\\n --project-version ${CURRENT_PROJECT_VERSION} \\\n --project-company CIM \\\n --company-id com.CIM \\\n --no-repeat-first-par \\\n --keep-undocumented-objects \\\n --keep-undocumented-members \\\n --logformat xcode \\\n --exit-threshold 2 \\\n ${CONFIGURATION_BUILD_DIR}/include/${PRODUCT_NAME}\nelse\n echo \"warning: appledoc not installed\"\nfi\n\n"; + showEnvVarsInLog = 0; + }; + 19BBA1961858E50C0001A27E /* appledoc */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = appledoc; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"generating documentation\"\nif type appledoc &> /dev/null ; then\n appledoc \\\n --output ${SOURCE_ROOT}/../Documents \\\n --create-html \\\n --project-name ${PRODUCT_NAME} \\\n --project-version ${CURRENT_PROJECT_VERSION} \\\n --project-company CIM \\\n --company-id com.CIM \\\n --no-repeat-first-par \\\n --keep-undocumented-objects \\\n --keep-undocumented-members \\\n --logformat xcode \\\n --exit-threshold 2 \\\n ${CONFIGURATION_BUILD_DIR}/include/${PRODUCT_NAME}\nelse\n echo \"warning: appledoc not installed\"\nfi\n\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 19FB6B071842832800C71880 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 19FB6B6F184286C600C71880 /* mongoose.c in Sources */, + 19FB6B6B1842843900C71880 /* MongooseDaemon.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 19FB6B1A1842832800C71880 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 19FB6B2C1842832800C71880 /* MongooseDaemonTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 19FB6B7118428A7200C71880 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 19FB6B9C18428ADC00C71880 /* MongooseDaemon.m in Sources */, + 19FB6B9A18428AD000C71880 /* mongoose.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 19FB6B9E18428FA600C71880 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 19FB6BB318428FA600C71880 /* CIMAppDelegate.m in Sources */, + 19FB6BDD18429D5800C71880 /* CIMSwitchCell.m in Sources */, + 19FB6BE01842A02B00C71880 /* CIMTextFieldCell.m in Sources */, + 19FB6BAF18428FA600C71880 /* main.m in Sources */, + 19FB6BD018428FF600C71880 /* CIMMongooseExampleViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 19FB6BB618428FA600C71880 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 19FB6BC718428FA600C71880 /* iOSMongooseExampleTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 19FB6B231842832800C71880 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 19FB6B0A1842832800C71880 /* libMongooseDaemon.a */; + targetProxy = 19FB6B221842832800C71880 /* PBXContainerItemProxy */; + }; + 19FB6BBF18428FA600C71880 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 19FB6BA118428FA600C71880 /* iOSMongooseExample */; + targetProxy = 19FB6BBE18428FA600C71880 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 1947CAAD185759B000C7CDE3 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 1947CAAE185759B000C7CDE3 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + 19FB6B281842832800C71880 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 19FB6B291842832800C71880 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + 19FB6BAB18428FA600C71880 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 19FB6BAC18428FA600C71880 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + 19FB6BC318428FA600C71880 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 19FB6BC418428FA600C71880 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 19FB6B2D1842832800C71880 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1.2; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_NAME = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 19FB6B2E1842832800C71880 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 1.2; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_NAME = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + 19FB6B301842832800C71880 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "MongooseDaemon/MongooseDaemon-Prefix.pch"; + OTHER_CFLAGS = ( + "-Wall", + "-Wextra", + "-Wno-unused-parameter", + "-Wno-shorten-64-to-32", + ); + PRODUCT_NAME = MongooseDaemon; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 19FB6B311842832800C71880 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "MongooseDaemon/MongooseDaemon-Prefix.pch"; + OTHER_CFLAGS = ( + "-Wall", + "-Wextra", + "-Wno-unused-parameter", + "-Wno-shorten-64-to-32", + ); + PRODUCT_NAME = MongooseDaemon; + SDKROOT = iphoneos; + }; + name = Release; + }; + 19FB6B331842832800C71880 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/MongooseDaemon.framework/Versions/A/MongooseDaemon"; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "MongooseDaemon/MongooseDaemon-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "MongooseDaemonTests/MongooseDaemonTests-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = xctest; + }; + name = Debug; + }; + 19FB6B341842832800C71880 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/MongooseDaemon.framework/Versions/A/MongooseDaemon"; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "MongooseDaemon/MongooseDaemon-Prefix.pch"; + INFOPLIST_FILE = "MongooseDaemonTests/MongooseDaemonTests-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = xctest; + }; + name = Release; + }; + 19FB6B9518428A7200C71880 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1.2; + FRAMEWORK_VERSION = A; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "MongooseDaemon/MongooseDaemon-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "MongooseDaemon/MongooseDaemon-Info.plist"; + OTHER_CFLAGS = ( + "-Wall", + "-Wextra", + "-Wno-unused-parameter", + "-Wno-shorten-64-to-32", + ); + PRODUCT_NAME = MongooseDaemon; + WRAPPER_EXTENSION = framework; + }; + name = Debug; + }; + 19FB6B9618428A7200C71880 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1.2; + FRAMEWORK_VERSION = A; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "MongooseDaemon/MongooseDaemon-Prefix.pch"; + INFOPLIST_FILE = "MongooseDaemon/MongooseDaemon-Info.plist"; + OTHER_CFLAGS = ( + "-Wall", + "-Wextra", + "-Wno-unused-parameter", + "-Wno-shorten-64-to-32", + ); + PRODUCT_NAME = MongooseDaemon; + WRAPPER_EXTENSION = framework; + }; + name = Release; + }; + 19FB6BC918428FA600C71880 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "iOSMongooseExample/iOSMongooseExample-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "iOSMongooseExample/iOSMongooseExample-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 19FB6BCA18428FA600C71880 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "iOSMongooseExample/iOSMongooseExample-Prefix.pch"; + INFOPLIST_FILE = "iOSMongooseExample/iOSMongooseExample-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; + 19FB6BCC18428FA600C71880 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/MongooseDaemon.framework/Versions/A/MongooseDaemon"; + CLANG_ENABLE_MODULES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "iOSMongooseExample/iOSMongooseExample-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "iOSMongooseExampleTests/iOSMongooseExampleTests-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = xctest; + }; + name = Debug; + }; + 19FB6BCD18428FA600C71880 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/MongooseDaemon.framework/Versions/A/MongooseDaemon"; + CLANG_ENABLE_MODULES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "iOSMongooseExample/iOSMongooseExample-Prefix.pch"; + INFOPLIST_FILE = "iOSMongooseExampleTests/iOSMongooseExampleTests-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + TEST_HOST = "$(BUNDLE_LOADER)"; + VALIDATE_PRODUCT = YES; + WRAPPER_EXTENSION = xctest; + }; + name = Release; + }; + 587062651850D18B0078C746 /* Distribution */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 1.2; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_NAME = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Distribution; + }; + 587062661850D18B0078C746 /* Distribution */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "MongooseDaemon/MongooseDaemon-Prefix.pch"; + OTHER_CFLAGS = ( + "-Wall", + "-Wextra", + "-Wno-unused-parameter", + "-Wno-shorten-64-to-32", + ); + PRODUCT_NAME = MongooseDaemon; + SDKROOT = iphoneos; + }; + name = Distribution; + }; + 587062671850D18B0078C746 /* Distribution */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1.2; + FRAMEWORK_VERSION = A; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "MongooseDaemon/MongooseDaemon-Prefix.pch"; + INFOPLIST_FILE = "MongooseDaemon/MongooseDaemon-Info.plist"; + OTHER_CFLAGS = ( + "-Wall", + "-Wextra", + "-Wno-unused-parameter", + "-Wno-shorten-64-to-32", + ); + PRODUCT_NAME = MongooseDaemon; + WRAPPER_EXTENSION = framework; + }; + name = Distribution; + }; + 587062681850D18B0078C746 /* Distribution */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/MongooseDaemon.framework/Versions/A/MongooseDaemon"; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "MongooseDaemon/MongooseDaemon-Prefix.pch"; + INFOPLIST_FILE = "MongooseDaemonTests/MongooseDaemonTests-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = xctest; + }; + name = Distribution; + }; + 587062691850D18B0078C746 /* Distribution */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "iOSMongooseExample/iOSMongooseExample-Prefix.pch"; + INFOPLIST_FILE = "iOSMongooseExample/iOSMongooseExample-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + WRAPPER_EXTENSION = app; + }; + name = Distribution; + }; + 5870626A1850D18B0078C746 /* Distribution */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/MongooseDaemon.framework/Versions/A/MongooseDaemon"; + CLANG_ENABLE_MODULES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "iOSMongooseExample/iOSMongooseExample-Prefix.pch"; + INFOPLIST_FILE = "iOSMongooseExampleTests/iOSMongooseExampleTests-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + TEST_HOST = "$(BUNDLE_LOADER)"; + VALIDATE_PRODUCT = YES; + WRAPPER_EXTENSION = xctest; + }; + name = Distribution; + }; + 58E95BCB185F6491007E65A5 /* MERC */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 2; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_NAME = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = MERC; + }; + 58E95BCC185F6491007E65A5 /* MERC */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "MongooseDaemon/MongooseDaemon-Prefix.pch"; + OTHER_CFLAGS = ( + "-Wall", + "-Wextra", + "-Wno-unused-parameter", + ); + PRODUCT_NAME = MongooseDaemon; + SDKROOT = iphoneos; + }; + name = MERC; + }; + 58E95BCD185F6491007E65A5 /* MERC */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; + COMBINE_HIDPI_IMAGES = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 2; + FRAMEWORK_VERSION = A; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "MongooseDaemon/MongooseDaemon-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "MongooseDaemon/MongooseDaemon-Info.plist"; + OTHER_CFLAGS = ( + "-Wall", + "-Wextra", + "-Wno-unused-parameter", + ); + PRODUCT_NAME = MongooseDaemon; + WRAPPER_EXTENSION = framework; + }; + name = MERC; + }; + 58E95BCE185F6491007E65A5 /* MERC */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/MongooseDaemon.framework/Versions/A/MongooseDaemon"; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "MongooseDaemon/MongooseDaemon-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "MongooseDaemonTests/MongooseDaemonTests-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = xctest; + }; + name = MERC; + }; + 58E95BCF185F6491007E65A5 /* MERC */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "iOSMongooseExample/iOSMongooseExample-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "iOSMongooseExample/iOSMongooseExample-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = app; + }; + name = MERC; + }; + 58E95BD0185F6491007E65A5 /* MERC */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; + BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/MongooseDaemon.framework/Versions/A/MongooseDaemon"; + CLANG_ENABLE_MODULES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "iOSMongooseExample/iOSMongooseExample-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "iOSMongooseExampleTests/iOSMongooseExampleTests-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = xctest; + }; + name = MERC; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 19FB6B061842832800C71880 /* Build configuration list for PBXProject "MongooseDaemon" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 19FB6B2D1842832800C71880 /* Debug */, + 58E95BCB185F6491007E65A5 /* MERC */, + 19FB6B2E1842832800C71880 /* Release */, + 587062651850D18B0078C746 /* Distribution */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 19FB6B2F1842832800C71880 /* Build configuration list for PBXNativeTarget "libMongooseDaemon.a" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 19FB6B301842832800C71880 /* Debug */, + 58E95BCC185F6491007E65A5 /* MERC */, + 19FB6B311842832800C71880 /* Release */, + 587062661850D18B0078C746 /* Distribution */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 19FB6B321842832800C71880 /* Build configuration list for PBXNativeTarget "MongooseDaemonTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 19FB6B331842832800C71880 /* Debug */, + 58E95BCE185F6491007E65A5 /* MERC */, + 19FB6B341842832800C71880 /* Release */, + 587062681850D18B0078C746 /* Distribution */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 19FB6B9418428A7200C71880 /* Build configuration list for PBXNativeTarget "MongooseDaemon.framework" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 19FB6B9518428A7200C71880 /* Debug */, + 58E95BCD185F6491007E65A5 /* MERC */, + 19FB6B9618428A7200C71880 /* Release */, + 587062671850D18B0078C746 /* Distribution */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 19FB6BC818428FA600C71880 /* Build configuration list for PBXNativeTarget "iOSMongooseExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 19FB6BC918428FA600C71880 /* Debug */, + 58E95BCF185F6491007E65A5 /* MERC */, + 19FB6BCA18428FA600C71880 /* Release */, + 587062691850D18B0078C746 /* Distribution */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 19FB6BCB18428FA600C71880 /* Build configuration list for PBXNativeTarget "iOSMongooseExampleTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 19FB6BCC18428FA600C71880 /* Debug */, + 58E95BD0185F6491007E65A5 /* MERC */, + 19FB6BCD18428FA600C71880 /* Release */, + 5870626A1850D18B0078C746 /* Distribution */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 19FB6B031842832800C71880 /* Project object */; +} diff --git a/MongooseDaemon/MongooseDaemon.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/MongooseDaemon/MongooseDaemon.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..f2967a1 --- /dev/null +++ b/MongooseDaemon/MongooseDaemon.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/MongooseDaemon/MongooseDaemon/MongooseDaemon-Info.plist b/MongooseDaemon/MongooseDaemon/MongooseDaemon-Info.plist new file mode 100644 index 0000000..eaeb87c --- /dev/null +++ b/MongooseDaemon/MongooseDaemon/MongooseDaemon-Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.CIM.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.2 + CFBundleSignature + ???? + CFBundleVersion + 1.2 + NSHumanReadableCopyright + Copyright © 2013 CIM. All rights reserved. + NSPrincipalClass + MongooseDaemon + + diff --git a/MongooseDaemon/MongooseDaemon/MongooseDaemon-Prefix.pch b/MongooseDaemon/MongooseDaemon/MongooseDaemon-Prefix.pch new file mode 100644 index 0000000..2506bbe --- /dev/null +++ b/MongooseDaemon/MongooseDaemon/MongooseDaemon-Prefix.pch @@ -0,0 +1,28 @@ +// +// Prefix header +// +// The contents of this file are implicitly included at the beginning of every source file. +// + +#ifdef __OBJC__ + #import +#endif + +#ifndef logMongoose +#define logMongoose 0 +#endif + + +#ifndef CIMLog +#if logMongoose +#import +#define CIMLog(c,s,...) do { \ +NSString *file = [[NSString stringWithUTF8String:__FILE__] lastPathComponent]; \ +NSString *threadName = [NSThread currentThread].name; \ +if (![threadName length]) threadName = [[NSThread currentThread] isMainThread] ? @"MAIN" : [NSString stringWithFormat:@"%i", pthread_mach_thread_np(pthread_self())]; \ +NSLog(@"%@:%d [%@] - %@", file, __LINE__, threadName, [NSString stringWithFormat:s, __VA_ARGS__]); \ +} while (0) +#else +#define CIMLog(c,s,...) do { } while (0) +#endif +#endif \ No newline at end of file diff --git a/MongooseDaemon/MongooseDaemon/MongooseDaemon.h b/MongooseDaemon/MongooseDaemon/MongooseDaemon.h new file mode 100644 index 0000000..d268530 --- /dev/null +++ b/MongooseDaemon/MongooseDaemon/MongooseDaemon.h @@ -0,0 +1,124 @@ +// +// MongooseDaemon.h +// +// Created by Rama McIntosh on 3/4/09. +// Copyright Rama McIntosh 2009. All rights reserved. +// + + +// +// Copyright (c) 2009, Rama McIntosh All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Rama McIntosh nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// + + +@protocol MongooseDaemonDelegate; + + +/** + `MongooseDaemon` is an objective C wrapper around the embedable `mongoose` http server for iOS or Mac development. It is ideal for both embedded http services as well as simply debugging your iOS or Mac applications file structure. `MongooseDaemon` is offered under a BSD style license. + + `Mongoose` is a lightweight embedable http server written by Sergey Lyubka and offered under a BSD style license. More information on mongoose can be found at the [project's github](https://github.com/valenok/mongoose). + */ +@interface MongooseDaemon : NSObject + +/** + The version of MongooseDaemon + */ ++ (NSString *)versionString; + + +/** + The version of the included mongoose library + */ ++ (NSString *)mongooseVersionString; + +/** + Optional delegate to allow for custom responses to be served from memory rather than the file system. + + @see MongooseDaemonDelegate + */ +@property (weak) id delegate; + +/** + The root directory of the local file system from which Mongoose will serve files. Defaults to the documents directory. + + @warning Must be a valid path. + @warning If the HTTP server is already running, this property will return without changing the value of the property. + */ +@property (nonatomic, strong) NSString *documentRoot; + +/** + An NSInteger defining the port on which Mongoose will listen for connections. Defaults to 8080. + + @warning Cannot be empty, can only contain valid port numbers. + */ +@property (nonatomic, assign) NSInteger listeningPort; + +/** + A boolean that indicates if Mongoose is currently running. + */ +@property (nonatomic, readonly, assign, getter = isRunning) BOOL running; + + +/** + Starts the Mongoose HTTP server. + + Blocks until the HTTP server is started. Does nothing if the server is running. + */ +- (void)start; + +/** + Blocks until the HTTP server is stopped. Does nothing if the server is stopped. + */ +- (void)stop; + +@end + + +/** + MongooseDaemonDelegate allows Clients to provide custom responses to incoming requests and more visibility into Mongoose as it is running. + */ +@protocol MongooseDaemonDelegate + +/** + Allows the delegate to provide the response to the incoming request instead of allowing Mongoose to serve from the file system. + + Returning nil will allow Mongoose to serve the request from the documents folder normally. + + @return A valid NSHTTPURLResponse object or nil + @param daemon A pointer to the MongooseDaemon instance calling the delegate + @param request The NSURLRequest being handled by MongooseDaemon + @param responseData OUT A pointer to the response data + */ +- (NSHTTPURLResponse *)mongooseDaemon:(MongooseDaemon *)daemon customResponseForRequest:(NSURLRequest *)request withResponseData:(NSData *__autoreleasing *)responseData; + + +@end + diff --git a/MongooseDaemon/MongooseDaemon/MongooseDaemon.m b/MongooseDaemon/MongooseDaemon/MongooseDaemon.m new file mode 100644 index 0000000..1190d62 --- /dev/null +++ b/MongooseDaemon/MongooseDaemon/MongooseDaemon.m @@ -0,0 +1,325 @@ +// +// MongooseDaemon.m +// +// Created by Rama McIntosh on 3/4/09. +// Copyright Rama McIntosh 2009. All rights reserved. +// + +// +// Copyright (c) 2009, Rama McIntosh All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Rama McIntosh nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#import "MongooseDaemon.h" +#import "MongooseDaemon_MongooseCallbacks.h" +#import "MongooseDaemonVersion.h" +#import "mongoose.h" +#include +#include +#include + + +// +// supported mongoose options +typedef NS_ENUM(NSUInteger, mongooseOptionIndex) { + mongooseOptionIndexDocumentRoot = 0, + mongooseOptionIndexListeningPort +}; +const NSUInteger kNumberOfSupportedOptions = 2; + +const char * kMongooseOptionDocumentRoot = "document_root"; +const char * kMongooseOptionListeningPort = "listening_port"; + + +@interface MongooseDaemon () + +@property (strong) dispatch_queue_t queue; + +@end + +@implementation MongooseDaemon { + struct mg_server *_server; +} + +@synthesize documentRoot = _documentRoot; +@synthesize listeningPort = _listeningPort; + +- (id)init { + self = [super init]; + if (self) { + // create a serial queue + static int queueCount = 0; + NSString *queueLabel = [NSString stringWithFormat:@"%@.MongooseQueue.%d", [[NSBundle mainBundle] bundleIdentifier], queueCount++]; + self.queue = dispatch_queue_create([queueLabel UTF8String], NULL); + dispatch_sync(self.queue, ^{ + CIMLog(logMongoose, @"[%@] created queue [%@]", self, self.queue); + + // list available options and their defaults + const char **options = mg_get_valid_option_names(); + NSMutableDictionary *mongooseOptions = [NSMutableDictionary dictionary]; + int i; + for (i = 0; options[i * 2] != NULL; i++) { + NSString *option = [NSString stringWithUTF8String:options[i * 2]]; + if (options[i * 2 + 1] == NULL) { + mongooseOptions[option] = [NSNull null]; + } else { + mongooseOptions[option] = [NSString stringWithUTF8String:options[i * 2 + 1]]; + } + } + CIMLog(logMongoose, @"Mongoose Options = %@", mongooseOptions); + + // set port and root defaults + _listeningPort = 8080; + _documentRoot = NSHomeDirectory(); + + }); + } + return self; +} + +- (void)dealloc { + [self stop]; + self.documentRoot = nil; +} + + +#pragma mark - start/stop + +- (void)start { + dispatch_sync(self.queue, ^{ + if (_server == NULL) { + + // start the web server + _server = mg_create_server((__bridge void *)self); + + // set supported options + NSUInteger i; + for (i = 0; i < [self numberOfSupportedOptions]; i++) { + CIMLog(logMongoose, @"%s = %s", [self nameOfOptionAtIndex:i], [self valueOfOptionAtIndex:i]); + mg_set_option(_server, [self nameOfOptionAtIndex:i], [self valueOfOptionAtIndex:i]); + CIMLog(logMongoose, @"%s ? %s", [self nameOfOptionAtIndex:i], mg_get_option(_server, [self nameOfOptionAtIndex:i])); + } + + // set callbacks + mg_set_http_error_handler(_server, &http_error_handler); + mg_set_request_handler(_server, &request_handler); + mg_set_auth_handler(_server, &auth_handler); + + [self poll]; + } + }); +} + +- (void)poll { + dispatch_async(self.queue, ^{ + if (_server) { + mg_poll_server(_server, 100); + [self poll]; + } + }); +} + +- (void)stop { + dispatch_sync(self.queue, ^{ + if (_server != NULL) { + mg_destroy_server(&_server); + _server = NULL; + } + }); +} + + +#pragma mark - Public Properties + ++ (NSString *)versionString { + NSString *version = [NSString stringWithFormat:@"%.1f", MongooseDaemonVersionNumber]; + return version; +} + ++ (NSString *)mongooseVersionString { + return [NSString stringWithUTF8String:MONGOOSE_VERSION]; +} + +- (BOOL)isRunning { + __block BOOL running; + dispatch_sync(self.queue, ^{ + running = (_server != NULL); + }); + return running; +} + +- (void)setListeningPort:(NSInteger)port { + dispatch_sync(self.queue, ^{ + _listeningPort = port; + if (_server != NULL) { + mg_set_listening_socket(_server, (int)_listeningPort); + } + }); +} + +- (NSInteger)listeningPort { + __block NSInteger listeningPort; + dispatch_sync(self.queue, ^{ + listeningPort = _listeningPort; + }); + return listeningPort; +} + +- (void)setDocumentRoot:(NSString *)documentRoot { + dispatch_sync(self.queue, ^{ + if (_server == NULL) { + _documentRoot = [documentRoot copy]; + } + }); +} + +- (NSString *)documentRoot { + __block NSString *documentRoot; + dispatch_sync(self.queue, ^{ + documentRoot = [_documentRoot copy]; + }); + return documentRoot; +} + + +#pragma mark - Mongoose Options + +- (NSUInteger)numberOfSupportedOptions { + return kNumberOfSupportedOptions; +} + +- (const char *)nameOfOptionAtIndex:(mongooseOptionIndex)index { + switch (index) { + case mongooseOptionIndexDocumentRoot: + return kMongooseOptionDocumentRoot; + + case mongooseOptionIndexListeningPort: + return kMongooseOptionListeningPort; + + default: + NSAssert1(false, @"Unexpected option index [%ld]", (long)index); + return NULL; + } +} + +- (const char *)valueOfOptionAtIndex:(mongooseOptionIndex)index { + switch (index) { + case mongooseOptionIndexDocumentRoot: + return [_documentRoot UTF8String]; + + case mongooseOptionIndexListeningPort: + return [[NSString stringWithFormat:@"%ld", (long)_listeningPort] UTF8String]; + + default: + NSAssert1(false, @"Unexpected option index [%ld]", (long)index); + return NULL; + } +} + + +@end + + +@implementation MongooseDaemon (MongooseCallbacks) + +// called on HTTP errors, should return MG_PROCESSED or MG_NOT_PROCESSED +int http_error_handler(struct mg_connection *connection) +{ + CIMLog(logMongoose, @"http_error_handler status = %d", connection->status_code); + return MG_ERROR_NOT_PROCESSED; +} + +// called on each request, should return MG_REQUEST_PROCESSED, MG_REQUEST_NOT_PROCESSED, or MG_REQUEST_CALL_AGAIN +int request_handler(struct mg_connection *connection) +{ + CIMLog(logMongoose, @"request_handler uri = %s", connection->uri); + + MongooseDaemon *daemon = (__bridge MongooseDaemon *)connection->server_param; + NSHTTPURLResponse *response = nil; + NSData *responseData = nil; + if ([daemon.delegate respondsToSelector:@selector(mongooseDaemon:customResponseForRequest:withResponseData:)]) { + NSURLRequest *request = requestFromMgConnection(connection); + response = [daemon.delegate mongooseDaemon:daemon customResponseForRequest:request withResponseData:&responseData]; + } + return sendResponse(connection, response, responseData); +} + +// called on each request, should return MG_AUTH_OK or MG_AUTH_FAIL +int auth_handler(struct mg_connection *connection) +{ + CIMLog(logMongoose, @"auth_handler uri = %s", connection->uri); + return MG_AUTH_OK; +} + + +#pragma mark - Private Helper Methods + +// extract an NSURLRequest from the mg_request_info object +NSURLRequest *requestFromMgConnection(struct mg_connection *connection) +{ + if (connection == NULL) { + return nil; + } + + NSString *uri = [NSString stringWithUTF8String:connection->uri]; + if (connection->query_string != NULL) { + uri = [uri stringByAppendingFormat:@"?%s", connection->query_string]; + } + NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:uri]]; + mutableRequest.HTTPMethod = [NSString stringWithUTF8String:connection->request_method]; + + for (int i = 0; i < connection->num_headers; i++) { + struct mg_header header = connection->http_headers[i]; + [mutableRequest setValue:[NSString stringWithUTF8String:header.value] forHTTPHeaderField:[NSString stringWithUTF8String:header.name]]; + } + + return [mutableRequest copy]; +} + +int sendResponse(struct mg_connection *connection, NSHTTPURLResponse *response, NSData *responseData) +{ + if (connection == NULL || !response) return MG_REQUEST_NOT_PROCESSED; + + // STATUS + mg_send_status(connection, response.statusCode); + + // HEADERS + NSMutableDictionary *headers = [[response allHeaderFields] mutableCopy]; + headers[@"Content-Length"] = @(responseData.length); + for (NSString *name in headers) { + mg_send_header(connection, [name UTF8String], [[headers[name] stringValue] UTF8String]); + } + + // DATA + mg_send_data(connection, responseData.bytes, responseData.length); + + return MG_REQUEST_PROCESSED; +} + + +@end diff --git a/MongooseDaemon/MongooseDaemon/MongooseDaemonVersion.h b/MongooseDaemon/MongooseDaemon/MongooseDaemonVersion.h new file mode 100644 index 0000000..44926c4 --- /dev/null +++ b/MongooseDaemon/MongooseDaemon/MongooseDaemonVersion.h @@ -0,0 +1,15 @@ +// +// MongooseDaemonVersion.h +// MongooseDaemon +// +// Created by Ibanez, Jose on 12/9/13. +// Copyright (c) 2013 CIM. All rights reserved. +// + +#ifndef MongooseDaemon_MongooseDaemonVersion_h +#define MongooseDaemon_MongooseDaemonVersion_h + +extern const unsigned char MongooseDaemonVersionString[]; +extern const double MongooseDaemonVersionNumber; + +#endif diff --git a/MongooseDaemon/MongooseDaemon/MongooseDaemon_MongooseCallbacks.h b/MongooseDaemon/MongooseDaemon/MongooseDaemon_MongooseCallbacks.h new file mode 100644 index 0000000..a7eb9a5 --- /dev/null +++ b/MongooseDaemon/MongooseDaemon/MongooseDaemon_MongooseCallbacks.h @@ -0,0 +1,23 @@ +// +// MongooseDaemon_MongooseCallbacks.h +// MongooseDaemon +// +// Created by Ibanez, Jose on 11/25/13. +// Copyright (c) 2013 CIM. All rights reserved. +// + +#import "MongooseDaemon.h" +#import "mongoose.h" + +@interface MongooseDaemon (MongooseCallbacks) + +// called on HTTP errors, should return MG_PROCESSED or MG_NOT_PROCESSED +int http_error_handler(struct mg_connection *connection); + +// called on each request, should return MG_REQUEST_PROCESSED, MG_REQUEST_NOT_PROCESSED, or MG_REQUEST_CALL_AGAIN +int request_handler(struct mg_connection *connection); + +// called on each request, should return MG_AUTH_OK or MG_AUTH_FAIL +int auth_handler(struct mg_connection *connection); + +@end diff --git a/MongooseDaemon/MongooseDaemon/en.lproj/InfoPlist.strings b/MongooseDaemon/MongooseDaemon/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/MongooseDaemon/MongooseDaemon/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/MongooseDaemon/MongooseDaemonTests/MongooseDaemonTests-Info.plist b/MongooseDaemon/MongooseDaemonTests/MongooseDaemonTests-Info.plist new file mode 100644 index 0000000..716d533 --- /dev/null +++ b/MongooseDaemon/MongooseDaemonTests/MongooseDaemonTests-Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.CIM.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.2 + CFBundleSignature + ???? + CFBundleVersion + 1.2 + + diff --git a/MongooseDaemon/MongooseDaemonTests/MongooseDaemonTests.m b/MongooseDaemon/MongooseDaemonTests/MongooseDaemonTests.m new file mode 100644 index 0000000..bd81cb4 --- /dev/null +++ b/MongooseDaemon/MongooseDaemonTests/MongooseDaemonTests.m @@ -0,0 +1,34 @@ +// +// MongooseDaemonTests.m +// MongooseDaemonTests +// +// Created by Ibanez, Jose on 11/24/13. +// Copyright (c) 2013 CIM. All rights reserved. +// + +#import + +@interface MongooseDaemonTests : XCTestCase + +@end + +@implementation MongooseDaemonTests + +- (void)setUp +{ + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown +{ + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample +{ + XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); +} + +@end diff --git a/MongooseDaemon/MongooseDaemonTests/en.lproj/InfoPlist.strings b/MongooseDaemon/MongooseDaemonTests/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/MongooseDaemon/MongooseDaemonTests/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/MongooseDaemon/iOSMongooseExample/CIMAppDelegate.h b/MongooseDaemon/iOSMongooseExample/CIMAppDelegate.h new file mode 100644 index 0000000..026f7a5 --- /dev/null +++ b/MongooseDaemon/iOSMongooseExample/CIMAppDelegate.h @@ -0,0 +1,15 @@ +// +// CIMAppDelegate.h +// iOSMongooseExample +// +// Created by Ibanez, Jose on 11/24/13. +// Copyright (c) 2013 CIM. All rights reserved. +// + +#import + +@interface CIMAppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +@end diff --git a/MongooseDaemon/iOSMongooseExample/CIMAppDelegate.m b/MongooseDaemon/iOSMongooseExample/CIMAppDelegate.m new file mode 100644 index 0000000..b924dd5 --- /dev/null +++ b/MongooseDaemon/iOSMongooseExample/CIMAppDelegate.m @@ -0,0 +1,51 @@ +// +// CIMAppDelegate.m +// iOSMongooseExample +// +// Created by Ibanez, Jose on 11/24/13. +// Copyright (c) 2013 CIM. All rights reserved. +// + +#import "CIMAppDelegate.h" +#import "CIMMongooseExampleViewController.h" + + +@implementation CIMAppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[[CIMMongooseExampleViewController alloc] init]]; + self.window.backgroundColor = [UIColor whiteColor]; + [self.window makeKeyAndVisible]; + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application +{ + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. +} + +- (void)applicationDidEnterBackground:(UIApplication *)application +{ + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *)application +{ + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application +{ + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. +} + +- (void)applicationWillTerminate:(UIApplication *)application +{ + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. +} + +@end diff --git a/MongooseDaemon/iOSMongooseExample/CIMMongooseExampleViewController.h b/MongooseDaemon/iOSMongooseExample/CIMMongooseExampleViewController.h new file mode 100644 index 0000000..bc663f1 --- /dev/null +++ b/MongooseDaemon/iOSMongooseExample/CIMMongooseExampleViewController.h @@ -0,0 +1,13 @@ +// +// CIMMongooseExampleViewController.h +// MongooseDaemon +// +// Created by Ibanez, Jose on 11/24/13. +// Copyright (c) 2013 CIM. All rights reserved. +// + +#import + +@interface CIMMongooseExampleViewController : UIViewController + +@end diff --git a/MongooseDaemon/iOSMongooseExample/CIMMongooseExampleViewController.m b/MongooseDaemon/iOSMongooseExample/CIMMongooseExampleViewController.m new file mode 100644 index 0000000..4077acd --- /dev/null +++ b/MongooseDaemon/iOSMongooseExample/CIMMongooseExampleViewController.m @@ -0,0 +1,252 @@ +// +// CIMMongooseExampleViewController.m +// MongooseDaemon +// +// Created by Ibanez, Jose on 11/24/13. +// Copyright (c) 2013 CIM. All rights reserved. +// + +#import "CIMMongooseExampleViewController.h" +#import "CIMSwitchCell.h" +#import "CIMTextFieldCell.h" +#import + + + + +typedef NS_ENUM(NSInteger, MongooseExampleTableViewRow) { + MongooseExampleTableViewRowEnable = 0, + MongooseExampleTableViewRowPort +}; +const NSInteger kMongooseExampleTableViewNumberOfRows = 2; + +NSString * const kMongooseExampleTableViewCellIdentifier = @"kMongooseExampleTableViewCellIdentifier"; +NSString * const kMongooseExampleTableViewSwitchCellIdentifier = @"kMongooseExampleTableViewSwitchCellIdentifier"; +NSString * const kMongooseExampleTableViewLabelCellIdentifier = @"kMongooseExampleTableViewLabelCellIdentifier"; + + +@interface CIMMongooseExampleViewController () + +@property (nonatomic) MongooseDaemon *theMongoose; +@property UITableView *tableView; + +@end + + +@implementation CIMMongooseExampleViewController + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + // Custom initialization + self.title = NSLocalizedString(@"Mongoose Example", nil); + self.editing = YES; + } + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + // Do any additional setup after loading the view. + + self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; + self.tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kMongooseExampleTableViewCellIdentifier]; + [self.tableView registerNib:[UINib nibWithNibName:@"CIMSwitchCell" bundle:nil] forCellReuseIdentifier:kMongooseExampleTableViewSwitchCellIdentifier]; + [self.tableView registerNib:[UINib nibWithNibName:@"CIMTextFieldCell" bundle:nil] forCellReuseIdentifier:kMongooseExampleTableViewLabelCellIdentifier]; + self.tableView.delegate = self; + self.tableView.dataSource = self; + [self.view addSubview:self.tableView]; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + [self.tableView reloadData]; +} + + +#pragma mark - Properties + +- (MongooseDaemon *)theMongoose { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _theMongoose = [[MongooseDaemon alloc] init]; + + _theMongoose.delegate = self; + + // set the document root directory to the documents folder + _theMongoose.documentRoot = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; + + // copy the hello world document to the documents folder + NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"HelloWorld" ofType:@"html"]; + NSString *destinationPath = [_theMongoose.documentRoot stringByAppendingPathComponent:@"index.html"]; + if (![[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) { + NSError *error = nil; + if (![[NSFileManager defaultManager] copyItemAtPath:sourcePath toPath:destinationPath error:&error]) { + NSLog(@"Copy [%@] to [%@] failed with error [%@]", sourcePath, destinationPath, error.localizedDescription); + } + } + }); + return _theMongoose; +} + + +#pragma mark - UITableViewDataSource + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return 3; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + switch (section) { + case 0: + return kMongooseExampleTableViewNumberOfRows; + default: + return 1; + } +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + UITableViewCell *cell = nil; + switch (indexPath.section) { + case 0: + switch (indexPath.row) { + case MongooseExampleTableViewRowEnable: { + cell = [tableView dequeueReusableCellWithIdentifier:kMongooseExampleTableViewSwitchCellIdentifier forIndexPath:indexPath]; + CIMSwitchCell *switchCell = (CIMSwitchCell *)cell; + switchCell.label.text = NSLocalizedString(@"Enabled", nil); + switchCell.theSwitch.on = self.theMongoose.isRunning; + [switchCell.theSwitch addTarget:self action:@selector(enableMongoose:) forControlEvents:UIControlEventValueChanged]; + switchCell.accessoryType = UITableViewCellAccessoryNone; + break; + } + case MongooseExampleTableViewRowPort: { + cell = [self.tableView dequeueReusableCellWithIdentifier:kMongooseExampleTableViewLabelCellIdentifier forIndexPath:indexPath]; + CIMTextFieldCell *textFieldCell = (CIMTextFieldCell *)cell; + textFieldCell.label.text = NSLocalizedString(@"Port", nil); + textFieldCell.textField.text = [NSString stringWithFormat:@"%ld", (long)self.theMongoose.listeningPort]; + textFieldCell.textField.enabled = !self.theMongoose.isRunning; + textFieldCell.accessoryType = UITableViewCellAccessoryNone; + break; + } + default: + NSAssert1(false, @"Unexpected TableView indexPath [%@]", indexPath); + break; + } + break; + + case 1: { + cell = [tableView dequeueReusableCellWithIdentifier:kMongooseExampleTableViewCellIdentifier forIndexPath:indexPath]; + cell.textLabel.text = NSLocalizedString(@"index.html", nil); + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + break; + } + case 2: { + cell = [tableView dequeueReusableCellWithIdentifier:kMongooseExampleTableViewLabelCellIdentifier forIndexPath:indexPath]; + CIMTextFieldCell *textFieldCell = (CIMTextFieldCell *)cell; + textFieldCell.label.text = NSLocalizedString(@"StatusCode", nil); + textFieldCell.textField.text = [NSString stringWithFormat:@"%d", 200]; + textFieldCell.textField.enabled = YES; + textFieldCell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + break; + } + default: + NSAssert1(false, @"Unexpected TableView indexPath [%@]", indexPath); + break; + } + + return cell; +} + + +#pragma mark - UITableViewDelegate + +- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath { + if (self.theMongoose.isRunning && (indexPath.section != 0)) { + return indexPath; + } else { + return nil; + } +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; + + UIViewController *vc = [[UIViewController alloc] init]; + vc.title = NSLocalizedString(@"Local Web Server", nil); + UIWebView *webView = [[UIWebView alloc] initWithFrame:vc.view.bounds]; + [vc.view addSubview:webView]; + + NSURL *localhost = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%ld", (long)self.theMongoose.listeningPort]]; + NSURL *url = nil; + if (indexPath.section == 1) { + url = localhost; + } else if (indexPath.section == 2) { + CIMTextFieldCell *textFieldCell = (CIMTextFieldCell *)[tableView cellForRowAtIndexPath:indexPath]; + url = [localhost URLByAppendingPathComponent:[NSString stringWithFormat:@"errors?code=%@", textFieldCell.textField.text]]; + } + NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url]; + [webView loadRequest:request]; + + [self.navigationController pushViewController:vc animated:YES]; +} + +- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { + switch (section) { + case 0: + return [NSString stringWithFormat:NSLocalizedString(@"Mongoose [%@] Settings", nil), [MongooseDaemon versionString]]; + case 1: + return NSLocalizedString(@"Static File", nil); + case 2: + return NSLocalizedString(@"Delegate", nil); + default: + NSAssert1(false, @"Unexpected TableView section [%ld]", (long)section); + return nil; + } +} + + +#pragma mark - Received Actions + +- (void)enableMongoose:(id)sender { + UISwitch *theSwitch = (UISwitch *)sender; + if (theSwitch.on) { + CIMTextFieldCell *textFieldCell = (CIMTextFieldCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:MongooseExampleTableViewRowPort inSection:0]]; + self.theMongoose.listeningPort = [textFieldCell.textField.text integerValue]; + [self.theMongoose start]; + } else { + [self.theMongoose stop]; + } + [self.tableView reloadData]; +} + + +#pragma mark - MongooseDaemonDelegate + +- (NSHTTPURLResponse *)mongooseDaemon:(MongooseDaemon *)daemon customResponseForRequest:(NSURLRequest *)request withResponseData:(NSData *__autoreleasing *)responseData { + NSLog(@"%s: [%@]%@", __PRETTY_FUNCTION__, request.HTTPMethod, [request.URL absoluteString]); + + if (![request.URL.pathComponents containsObject:@"errors"]) { + return nil; + } + + NSInteger statusCode = 200; + NSString *query = request.URL.query; + NSString *queryValue = [[query componentsSeparatedByString:@"="] lastObject]; + statusCode = [queryValue integerValue]; + + NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:request.URL + statusCode:statusCode + HTTPVersion:@"HTTP/1.1" + headerFields:nil]; + + NSString *responseString = [NSString stringWithFormat:@"Custom response!\nRequest [%@]\nMethod [%@]\nstatusCode [%ld]", request.URL, request.HTTPMethod, (long)statusCode]; + *responseData = [responseString dataUsingEncoding:NSUTF8StringEncoding]; + return response; +} + + +@end diff --git a/MongooseDaemon/iOSMongooseExample/CIMSwitchCell.h b/MongooseDaemon/iOSMongooseExample/CIMSwitchCell.h new file mode 100644 index 0000000..73cbf2f --- /dev/null +++ b/MongooseDaemon/iOSMongooseExample/CIMSwitchCell.h @@ -0,0 +1,16 @@ +// +// CIMSwitchCell.h +// MongooseDaemon +// +// Created by Ibanez, Jose on 11/24/13. +// Copyright (c) 2013 CIM. All rights reserved. +// + +#import + +@interface CIMSwitchCell : UITableViewCell + +@property (weak) IBOutlet UILabel *label; +@property (weak) IBOutlet UISwitch *theSwitch; + +@end diff --git a/MongooseDaemon/iOSMongooseExample/CIMSwitchCell.m b/MongooseDaemon/iOSMongooseExample/CIMSwitchCell.m new file mode 100644 index 0000000..9efab1d --- /dev/null +++ b/MongooseDaemon/iOSMongooseExample/CIMSwitchCell.m @@ -0,0 +1,29 @@ +// +// CIMSwitchCell.m +// MongooseDaemon +// +// Created by Ibanez, Jose on 11/24/13. +// Copyright (c) 2013 CIM. All rights reserved. +// + +#import "CIMSwitchCell.h" + +@implementation CIMSwitchCell + +- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier +{ + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + if (self) { + // Initialization code + } + return self; +} + +- (void)setSelected:(BOOL)selected animated:(BOOL)animated +{ + [super setSelected:selected animated:animated]; + + // Configure the view for the selected state +} + +@end diff --git a/MongooseDaemon/iOSMongooseExample/CIMSwitchCell.xib b/MongooseDaemon/iOSMongooseExample/CIMSwitchCell.xib new file mode 100644 index 0000000..7627d4d --- /dev/null +++ b/MongooseDaemon/iOSMongooseExample/CIMSwitchCell.xib @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MongooseDaemon/iOSMongooseExample/CIMTextFieldCell.h b/MongooseDaemon/iOSMongooseExample/CIMTextFieldCell.h new file mode 100644 index 0000000..eab9816 --- /dev/null +++ b/MongooseDaemon/iOSMongooseExample/CIMTextFieldCell.h @@ -0,0 +1,16 @@ +// +// CIMLabelCell.h +// MongooseDaemon +// +// Created by Ibanez, Jose on 11/24/13. +// Copyright (c) 2013 CIM. All rights reserved. +// + +#import + +@interface CIMTextFieldCell : UITableViewCell + +@property (nonatomic, weak) IBOutlet UILabel *label; +@property (nonatomic, weak) IBOutlet UITextField *textField; + +@end diff --git a/MongooseDaemon/iOSMongooseExample/CIMTextFieldCell.m b/MongooseDaemon/iOSMongooseExample/CIMTextFieldCell.m new file mode 100644 index 0000000..3ab5125 --- /dev/null +++ b/MongooseDaemon/iOSMongooseExample/CIMTextFieldCell.m @@ -0,0 +1,54 @@ +// +// CIMLabelCell.m +// MongooseDaemon +// +// Created by Ibanez, Jose on 11/24/13. +// Copyright (c) 2013 CIM. All rights reserved. +// + +#import "CIMTextFieldCell.h" + +@implementation CIMTextFieldCell + +- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier +{ + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + if (self) { + // Initialization code + self.textField.delegate = self; + } + return self; +} + +- (void)awakeFromNib +{ + [super awakeFromNib]; + self.textField.delegate = self; +} + +- (void)setSelected:(BOOL)selected animated:(BOOL)animated +{ + [super setSelected:selected animated:animated]; + + // Configure the view for the selected state +} + +- (void)prepareForReuse +{ + [super prepareForReuse]; + self.textField.delegate = self; +} + + +#pragma mark - UITextFieldDelegate + +- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { + return YES; +} + +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + [textField resignFirstResponder]; + return YES; +} + +@end diff --git a/MongooseDaemon/iOSMongooseExample/CIMTextFieldCell.xib b/MongooseDaemon/iOSMongooseExample/CIMTextFieldCell.xib new file mode 100644 index 0000000..f9679c7 --- /dev/null +++ b/MongooseDaemon/iOSMongooseExample/CIMTextFieldCell.xib @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MongooseDaemon/iOSMongooseExample/HelloWorld.html b/MongooseDaemon/iOSMongooseExample/HelloWorld.html new file mode 100644 index 0000000..4b4e46d --- /dev/null +++ b/MongooseDaemon/iOSMongooseExample/HelloWorld.html @@ -0,0 +1,11 @@ + + + + Hello, World! + + + + Hello, World! + + + \ No newline at end of file diff --git a/MongooseDaemon/iOSMongooseExample/Images.xcassets/AppIcon.appiconset/Contents.json b/MongooseDaemon/iOSMongooseExample/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..91bf9c1 --- /dev/null +++ b/MongooseDaemon/iOSMongooseExample/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,53 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/MongooseDaemon/iOSMongooseExample/Images.xcassets/LaunchImage.launchimage/Contents.json b/MongooseDaemon/iOSMongooseExample/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000..6f870a4 --- /dev/null +++ b/MongooseDaemon/iOSMongooseExample/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,51 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/MongooseDaemon/iOSMongooseExample/en.lproj/InfoPlist.strings b/MongooseDaemon/iOSMongooseExample/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/MongooseDaemon/iOSMongooseExample/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/MongooseDaemon/iOSMongooseExample/iOSMongooseExample-Info.plist b/MongooseDaemon/iOSMongooseExample/iOSMongooseExample-Info.plist new file mode 100644 index 0000000..b01cca6 --- /dev/null +++ b/MongooseDaemon/iOSMongooseExample/iOSMongooseExample-Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.CIM.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.2 + CFBundleSignature + ???? + CFBundleVersion + 1.2 + LSRequiresIPhoneOS + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/MongooseDaemon/iOSMongooseExample/iOSMongooseExample-Prefix.pch b/MongooseDaemon/iOSMongooseExample/iOSMongooseExample-Prefix.pch new file mode 100644 index 0000000..743435c --- /dev/null +++ b/MongooseDaemon/iOSMongooseExample/iOSMongooseExample-Prefix.pch @@ -0,0 +1,16 @@ +// +// Prefix header +// +// The contents of this file are implicitly included at the beginning of every source file. +// + +#import + +#ifndef __IPHONE_3_0 +#warning "This project uses features only available in iOS SDK 3.0 and later." +#endif + +#ifdef __OBJC__ + #import + #import +#endif diff --git a/MongooseDaemon/iOSMongooseExample/main.m b/MongooseDaemon/iOSMongooseExample/main.m new file mode 100644 index 0000000..08e7cf9 --- /dev/null +++ b/MongooseDaemon/iOSMongooseExample/main.m @@ -0,0 +1,18 @@ +// +// main.m +// iOSMongooseExample +// +// Created by Ibanez, Jose on 11/24/13. +// Copyright (c) 2013 CIM. All rights reserved. +// + +#import + +#import "CIMAppDelegate.h" + +int main(int argc, char * argv[]) +{ + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([CIMAppDelegate class])); + } +} diff --git a/MongooseDaemon/iOSMongooseExampleTests/en.lproj/InfoPlist.strings b/MongooseDaemon/iOSMongooseExampleTests/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/MongooseDaemon/iOSMongooseExampleTests/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/MongooseDaemon/iOSMongooseExampleTests/iOSMongooseExampleTests-Info.plist b/MongooseDaemon/iOSMongooseExampleTests/iOSMongooseExampleTests-Info.plist new file mode 100644 index 0000000..716d533 --- /dev/null +++ b/MongooseDaemon/iOSMongooseExampleTests/iOSMongooseExampleTests-Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.CIM.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.2 + CFBundleSignature + ???? + CFBundleVersion + 1.2 + + diff --git a/MongooseDaemon/iOSMongooseExampleTests/iOSMongooseExampleTests.m b/MongooseDaemon/iOSMongooseExampleTests/iOSMongooseExampleTests.m new file mode 100644 index 0000000..39bbb35 --- /dev/null +++ b/MongooseDaemon/iOSMongooseExampleTests/iOSMongooseExampleTests.m @@ -0,0 +1,34 @@ +// +// iOSMongooseExampleTests.m +// iOSMongooseExampleTests +// +// Created by Ibanez, Jose on 11/24/13. +// Copyright (c) 2013 CIM. All rights reserved. +// + +#import + +@interface iOSMongooseExampleTests : XCTestCase + +@end + +@implementation iOSMongooseExampleTests + +- (void)setUp +{ + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown +{ + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample +{ + XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); +} + +@end diff --git a/README.md b/README.md new file mode 100644 index 0000000..30dac5c --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +#MongooseDaemon + +`MongooseDaemon` is an objective C wrapper around +the embedable `mongoose` http server for iOS or Mac +development. It is ideal for both embedded http services +as well as simply debugging your iOS or Mac applications +file structure. `MongooseDaemon` is offered under a BSD +style license. + +`Mongoose` is a lightweight embedable http server +written by Sergey Lyubka and offered under +a BSD style license. More information on +mongoose can be found at the [project's +github](https://github.com/valenok/mongoose). + +##Usage + +As of version 1.1, MongooseDaemon is now compiled into a static library for iOS projects and into a framework for Mac OS projects and is meant to be included as a subproject in your workspace. + +1. Add MongooseDaemon as a submodule to your project using `git submodule add git@github.com:CIM/MongooseDaemon.git`. +2. Add MongooseDaemon's dependencies using `git submodule update --recursive --init` +3. Add `MongooseDaemon.xcodeproj` to your workspace. +4. Import MongooseDaemon using `#import ` + +An example iOS app is included with the project that demonstrates how to use MongoseDaemon. + +##TODO + +- Add support for SSL (!) +- Add support for more of mongoose's options. +- Mac OS example project. +- Make a CocoaPod because everything uses CocoaPods now. +- Add some tests or else no one will take this serioiusly. + +--- + +Copyright (c) 2008 Rama McIntosh, 2013 CIM +Released under the BSD license found in the file LICENSE diff --git a/README.rdoc b/README.rdoc deleted file mode 100644 index 0ea0156..0000000 --- a/README.rdoc +++ /dev/null @@ -1,80 +0,0 @@ -MongooseDaemon -=============== - -MongooseDaemon is an objective C wrapper around -the embedable mongoose http server for iPhone -development. It is offered under a BSD -style license. - -MongooseDaemon is ideal for both embedded http services -as well as simply debugging your iPhone applications -file structure. - -Mongoose is a lightweight embedable http server -written by Sergey Lyubka and offered under -a BSD style license. More information on -mongoose can be found at the project's -site here: http://code.google.com/p/mongoose/ - -NOTE: The mongoose.h and mongoose.c are taken -directly from mongoose-2.6 and are unmodified. -They are simply forked in this package for -convenience and stability. - -Usage -======= - - -Clone the mongoose directory and add it to your X-Code project. - -Then you can start it within one of you classes to -provide http access to your iPhone application. - - -For example, to start MongooseDaemon when your application -starts on port 8080: - -Add the following to MyAppDelegate.h: - ... - #import "MongooseDaemon.h" - .... - @class MongooseDaemon - - @interface MyAppDelegate : NSObject { - ... - MongooseDaemon *mongooseDaemon; - ... - } - -And add the following to MyAppDelegate.m: - ... - @implementation LightyAppDelegate - ... - - (void)applicationDidFinishLaunching:(UIApplication *)application { - mongooseDaemon = [[MongooseDaemon alloc] init]; - [mongooseDaemon startMongooseDaemon:@"8080"]; - - ... - - (void)dealloc { - ... - [mongooseDaemon stopMongooseDaemon]; - [mongooseDaemon release]; - ... - } - -And that's it...an embeded http server with only -about 9 lines of code! - -TODO -======= -1) Add a helper method to return the server's URL for -convience - -2) Add support for some of mongoose's rich feature set. - -3) Investigate if there is anyway to server files over endge/3g instead of just WiFi. - -============== - -Copyright (c) 2008 Rama McIntosh -Released under the BSD license found in the file LICENSE diff --git a/mongoose b/mongoose new file mode 160000 index 0000000..de509ae --- /dev/null +++ b/mongoose @@ -0,0 +1 @@ +Subproject commit de509ae9525b1e81d538ab82406c3b558c25bd57 diff --git a/mongoose.c b/mongoose.c deleted file mode 100644 index 981c70f..0000000 --- a/mongoose.c +++ /dev/null @@ -1,4729 +0,0 @@ -/* - * Copyright (c) 2004-2009 Sergey Lyubka - * Portions Copyright (c) 2009 Gilbert Wellisch - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * $Id: mongoose.c 446 2009-07-08 21:06:56Z valenok $ - */ - -#if defined(_WIN32) -#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */ -#endif /* _WIN32 */ - -#ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */ -#include -#include -#include -#include -#include -#endif /* !_WIN32_WCE */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(_WIN32) /* Windows specific #includes and #defines */ -#define _WIN32_WINNT 0x0400 /* To make it link in VS2005 */ -#include - -#ifndef _WIN32_WCE -#include -#include -#include -#else /* _WIN32_WCE */ -/* Windows CE-specific definitions */ -#include -#define NO_CGI /* WinCE has no pipes */ -#define NO_SSI /* WinCE has no pipes */ - -#define FILENAME_MAX MAX_PATH -#define BUFSIZ 4096 -typedef long off_t; - -#define errno GetLastError() -#define strerror(x) _ultoa(x, (char *) _alloca(sizeof(x) *3 ), 10) -#endif /* _WIN32_WCE */ - -#define EPOCH_DIFF 0x019DB1DED53E8000 /* 116444736000000000 nsecs */ -#define RATE_DIFF 10000000 /* 100 nsecs */ -#define MAKEUQUAD(lo, hi) ((uint64_t)(((uint32_t)(lo)) | \ - ((uint64_t)((uint32_t)(hi))) << 32)) -#define SYS2UNIX_TIME(lo, hi) \ - (time_t) ((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF) - -/* - * Visual Studio 6 does not know __func__ or __FUNCTION__ - * The rest of MS compilers use __FUNCTION__, not C99 __func__ - */ -#if defined(_MSC_VER) && _MSC_VER < 1300 -#define STRX(x) #x -#define STR(x) STRX(x) -#define __func__ "line " STR(__LINE__) -#else -#define __func__ __FUNCTION__ -#endif /* _MSC_VER */ - -#define ERRNO GetLastError() -#define NO_SOCKLEN_T -#define SSL_LIB "ssleay32.dll" -#define CRYPTO_LIB "libeay32.dll" -#define DIRSEP '\\' -#define IS_DIRSEP_CHAR(c) ((c) == '/' || (c) == '\\') -#define O_NONBLOCK 0 -#define EWOULDBLOCK WSAEWOULDBLOCK -#define _POSIX_ -#define UINT64_FMT "I64" - -#define SHUT_WR 1 -#define snprintf _snprintf -#define vsnprintf _vsnprintf -#define sleep(x) Sleep((x) * 1000) - -#define popen(x, y) _popen(x, y) -#define pclose(x) _pclose(x) -#define close(x) _close(x) -#define dlsym(x,y) GetProcAddress((HINSTANCE) (x), (y)) -#define RTLD_LAZY 0 -#define fseeko(x, y, z) fseek((x), (y), (z)) -#define write(x, y, z) _write((x), (y), (unsigned) z) -#define read(x, y, z) _read((x), (y), (unsigned) z) -#define flockfile(x) (void) 0 -#define funlockfile(x) (void) 0 - -#ifdef HAVE_STRTOUI64 -#define strtoull(x, y, z) _strtoui64(x, y, z) -#else -#define strtoull(x, y, z) strtoul(x, y, z) -#endif /* HAVE_STRTOUI64 */ - -#if !defined(fileno) -#define fileno(x) _fileno(x) -#endif /* !fileno MINGW #defines fileno */ - -typedef HANDLE pthread_mutex_t; -typedef HANDLE pthread_cond_t; -typedef DWORD pthread_t; -typedef HANDLE pid_t; - -struct timespec { - long tv_nsec; - long tv_sec; -}; - -static int pthread_mutex_lock(pthread_mutex_t *); -static int pthread_mutex_unlock(pthread_mutex_t *); - -#if defined(HAVE_STDINT) -#include -#else -typedef unsigned int uint32_t; -typedef unsigned short uint16_t; -#if _MSC_VER > 1200 -typedef unsigned __int64 uint64_t; -#else -/* VC6 cannot cast double to unsigned __int64, needed by print_dir_entry() */ -typedef __int64 uint64_t; -#endif /* _MSC_VER */ -#endif /* HAVE_STDINT */ - -/* - * POSIX dirent interface - */ -struct dirent { - char d_name[FILENAME_MAX]; -}; - -typedef struct DIR { - HANDLE handle; - WIN32_FIND_DATAW info; - struct dirent result; -} DIR; - -#else /* UNIX specific */ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#define SSL_LIB "libssl.so" -#define CRYPTO_LIB "libcrypto.so" -#define DIRSEP '/' -#define IS_DIRSEP_CHAR(c) ((c) == '/') -#define O_BINARY 0 -#define closesocket(a) close(a) -#define mg_fopen(x, y) fopen(x, y) -#define mg_mkdir(x, y) mkdir(x, y) -#define mg_remove(x) remove(x) -#define mg_rename(x, y) rename(x, y) -#define mg_getcwd(x, y) getcwd(x, y) -#define ERRNO errno -#define INVALID_SOCKET (-1) -#define UINT64_FMT "ll" -typedef int SOCKET; - -#endif /* End of Windows and UNIX specific includes */ - -#include "mongoose.h" - -#define MONGOOSE_VERSION "2.8" -#define PASSWORDS_FILE_NAME ".htpasswd" -#define CGI_ENVIRONMENT_SIZE 4096 -#define MAX_CGI_ENVIR_VARS 64 -#define MAX_REQUEST_SIZE 8192 -#define MAX_LISTENING_SOCKETS 10 -#define MAX_CALLBACKS 20 -#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) -#define UNKNOWN_CONTENT_LENGTH ((uint64_t) ~0) -#define DEBUG_MGS_PREFIX "*** Mongoose debug *** " - -#if defined(DEBUG) -#define DEBUG_TRACE(x) do {printf x; putchar('\n'); fflush(stdout);} while (0) -#else -#define DEBUG_TRACE(x) -#endif /* DEBUG */ - -/* - * Darwin prior to 7.0 and Win32 do not have socklen_t - */ -#ifdef NO_SOCKLEN_T -typedef int socklen_t; -#endif /* NO_SOCKLEN_T */ - -#if !defined(FALSE) -enum {FALSE, TRUE}; -#endif /* !FALSE */ - -typedef int bool_t; -typedef void * (*mg_thread_func_t)(void *); - -static const char *http_500_error = "Internal Server Error"; - -/* - * Snatched from OpenSSL includes. I put the prototypes here to be independent - * from the OpenSSL source installation. Having this, mongoose + SSL can be - * built on any system with binary SSL libraries installed. - */ -typedef struct ssl_st SSL; -typedef struct ssl_method_st SSL_METHOD; -typedef struct ssl_ctx_st SSL_CTX; - -#define SSL_ERROR_WANT_READ 2 -#define SSL_ERROR_WANT_WRITE 3 -#define SSL_FILETYPE_PEM 1 -#define CRYPTO_LOCK 1 - -/* - * Dynamically loaded SSL functionality - */ -struct ssl_func { - const char *name; /* SSL function name */ - void (*ptr)(void); /* Function pointer */ -}; - -#define SSL_free(x) (* (void (*)(SSL *)) ssl_sw[0].ptr)(x) -#define SSL_accept(x) (* (int (*)(SSL *)) ssl_sw[1].ptr)(x) -#define SSL_connect(x) (* (int (*)(SSL *)) ssl_sw[2].ptr)(x) -#define SSL_read(x,y,z) (* (int (*)(SSL *, void *, int)) \ - ssl_sw[3].ptr)((x),(y),(z)) -#define SSL_write(x,y,z) (* (int (*)(SSL *, const void *,int)) \ - ssl_sw[4].ptr)((x), (y), (z)) -#define SSL_get_error(x,y)(* (int (*)(SSL *, int)) ssl_sw[5])((x), (y)) -#define SSL_set_fd(x,y) (* (int (*)(SSL *, SOCKET)) ssl_sw[6].ptr)((x), (y)) -#define SSL_new(x) (* (SSL * (*)(SSL_CTX *)) ssl_sw[7].ptr)(x) -#define SSL_CTX_new(x) (* (SSL_CTX * (*)(SSL_METHOD *)) ssl_sw[8].ptr)(x) -#define SSLv23_server_method() (* (SSL_METHOD * (*)(void)) ssl_sw[9].ptr)() -#define SSL_library_init() (* (int (*)(void)) ssl_sw[10].ptr)() -#define SSL_CTX_use_PrivateKey_file(x,y,z) (* (int (*)(SSL_CTX *, \ - const char *, int)) ssl_sw[11].ptr)((x), (y), (z)) -#define SSL_CTX_use_certificate_file(x,y,z) (* (int (*)(SSL_CTX *, \ - const char *, int)) ssl_sw[12].ptr)((x), (y), (z)) -#define SSL_CTX_set_default_passwd_cb(x,y) \ - (* (void (*)(SSL_CTX *, mg_spcb_t)) ssl_sw[13].ptr)((x),(y)) -#define SSL_CTX_free(x) (* (void (*)(SSL_CTX *)) ssl_sw[14].ptr)(x) - -#define CRYPTO_num_locks() (* (int (*)(void)) crypto_sw[0].ptr)() -#define CRYPTO_set_locking_callback(x) \ - (* (void (*)(void (*)(int, int, const char *, int))) \ - crypto_sw[1].ptr)(x) -#define CRYPTO_set_id_callback(x) \ - (* (void (*)(unsigned long (*)(void))) crypto_sw[2].ptr)(x) - -/* - * set_ssl_option() function when called, updates this array. - * It loads SSL library dynamically and changes NULLs to the actual addresses - * of respective functions. The macros above (like SSL_connect()) are really - * just calling these functions indirectly via the pointer. - */ -static struct ssl_func ssl_sw[] = { - {"SSL_free", NULL}, - {"SSL_accept", NULL}, - {"SSL_connect", NULL}, - {"SSL_read", NULL}, - {"SSL_write", NULL}, - {"SSL_get_error", NULL}, - {"SSL_set_fd", NULL}, - {"SSL_new", NULL}, - {"SSL_CTX_new", NULL}, - {"SSLv23_server_method", NULL}, - {"SSL_library_init", NULL}, - {"SSL_CTX_use_PrivateKey_file", NULL}, - {"SSL_CTX_use_certificate_file",NULL}, - {"SSL_CTX_set_default_passwd_cb",NULL}, - {"SSL_CTX_free", NULL}, - {NULL, NULL} -}; - -/* - * Similar array as ssl_sw. These functions are located in different lib. - */ -static struct ssl_func crypto_sw[] = { - {"CRYPTO_num_locks", NULL}, - {"CRYPTO_set_locking_callback", NULL}, - {"CRYPTO_set_id_callback", NULL}, - {NULL, NULL} -}; - -/* - * Month names - */ -static const char *month_names[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" -}; - -/* - * Unified socket address. For IPv6 support, add IPv6 address structure - * in the union u. - */ -struct usa { - socklen_t len; - union { - struct sockaddr sa; - struct sockaddr_in sin; - } u; -}; - -/* - * Specifies a string (chunk of memory). - * Used to traverse comma separated lists of options. - */ -struct vec { - const char *ptr; - size_t len; -}; - -/* - * Structure used by mg_stat() function. Uses 64 bit file length. - */ -struct mgstat { - bool_t is_directory; /* Directory marker */ - uint64_t size; /* File size */ - time_t mtime; /* Modification time */ -}; - -struct mg_option { - const char *name; - const char *description; - const char *default_value; - int index; - bool_t (*setter)(struct mg_context *, const char *); -}; - -/* - * Numeric indexes for the option values in context, ctx->options - */ -enum mg_option_index { - OPT_ROOT, OPT_INDEX_FILES, OPT_PORTS, OPT_DIR_LIST, OPT_CGI_EXTENSIONS, - OPT_CGI_INTERPRETER, OPT_CGI_ENV, OPT_SSI_EXTENSIONS, OPT_AUTH_DOMAIN, - OPT_AUTH_GPASSWD, OPT_AUTH_PUT, OPT_ACCESS_LOG, OPT_ERROR_LOG, - OPT_SSL_CERTIFICATE, OPT_ALIASES, OPT_ACL, OPT_UID, OPT_PROTECT, - OPT_SERVICE, OPT_HIDE, OPT_ADMIN_URI, OPT_MAX_THREADS, OPT_IDLE_TIME, - OPT_MIME_TYPES, - NUM_OPTIONS -}; - -/* - * Structure used to describe listening socket, or socket which was - * accept()-ed by the master thread and queued for future handling - * by the worker thread. - */ -struct socket { - SOCKET sock; /* Listening socket */ - struct usa lsa; /* Local socket address */ - struct usa rsa; /* Remote socket address */ - bool_t is_ssl; /* Is socket SSL-ed */ -}; - -/* - * Callback function, and where it is bound to - */ -struct callback { - char *uri_regex; /* URI regex to handle */ - mg_callback_t func; /* user callback */ - bool_t is_auth; /* func is auth checker */ - int status_code; /* error code to handle */ - void *user_data; /* opaque user data */ -}; - -/* - * Mongoose context - */ -struct mg_context { - int stop_flag; /* Should we stop event loop */ - SSL_CTX *ssl_ctx; /* SSL context */ - - FILE *access_log; /* Opened access log */ - FILE *error_log; /* Opened error log */ - - struct socket listeners[MAX_LISTENING_SOCKETS]; - int num_listeners; - - struct callback callbacks[MAX_CALLBACKS]; - int num_callbacks; - - char *options[NUM_OPTIONS]; /* Configured opions */ - pthread_mutex_t opt_mutex[NUM_OPTIONS]; /* Option protector */ - - int max_threads; /* Maximum number of threads */ - int num_threads; /* Number of threads */ - int num_idle; /* Number of idle threads */ - pthread_mutex_t thr_mutex; /* Protects (max|num)_threads */ - pthread_cond_t thr_cond; - pthread_mutex_t bind_mutex; /* Protects bind operations */ - - struct socket queue[20]; /* Accepted sockets */ - int sq_head; /* Head of the socket queue */ - int sq_tail; /* Tail of the socket queue */ - pthread_cond_t empty_cond; /* Socket queue empty condvar */ - pthread_cond_t full_cond; /* Socket queue full condvar */ - - mg_spcb_t ssl_password_callback; - mg_callback_t log_callback; -}; - -/* - * Client connection. - */ -struct mg_connection { - struct mg_request_info request_info; - struct mg_context *ctx; /* Mongoose context we belong to*/ - SSL *ssl; /* SSL descriptor */ - struct socket client; /* Connected client */ - time_t birth_time; /* Time connection was accepted */ - bool_t free_post_data; /* post_data was malloc-ed */ - bool_t embedded_auth; /* Used for authorization */ - uint64_t num_bytes_sent; /* Total bytes sent to client */ -}; - -/* - * Print error message to the opened error log stream. - */ -static void -cry(struct mg_connection *conn, const char *fmt, ...) -{ - char buf[BUFSIZ]; - va_list ap; - - va_start(ap, fmt); - (void) vsnprintf(buf, sizeof(buf), fmt, ap); - conn->ctx->log_callback(conn, &conn->request_info, buf); - va_end(ap); -} - -/* - * Return fake connection structure. Used for logging, if connection - * is not applicable at the moment of logging. - */ -static struct mg_connection * -fc(struct mg_context *ctx) -{ - static struct mg_connection fake_connection; - fake_connection.ctx = ctx; - return (&fake_connection); -} - -/* - * If an embedded code does not intercept logging by calling - * mg_set_log_callback(), this function is used for logging. It prints - * stuff to the conn->error_log, which is stderr unless "error_log" - * option was set. - */ -static void -builtin_error_log(struct mg_connection *conn, - const struct mg_request_info *request_info, void *message) -{ - FILE *fp; - time_t timestamp; - - fp = conn->ctx->error_log; - flockfile(fp); - - timestamp = time(NULL); - - (void) fprintf(fp, - "[%010lu] [error] [client %s] ", - (unsigned long) timestamp, - inet_ntoa(conn->client.rsa.u.sin.sin_addr)); - - if (request_info->request_method != NULL) - (void) fprintf(fp, "%s %s: ", - request_info->request_method, - request_info->uri); - - (void) fprintf(fp, "%s", (char *) message); - - fputc('\n', fp); - - funlockfile(fp); -} - -const char * -mg_version(void) -{ - return (MONGOOSE_VERSION); -} - -static void -mg_strlcpy(register char *dst, register const char *src, size_t n) -{ - for (; *src != '\0' && n > 1; n--) - *dst++ = *src++; - *dst = '\0'; -} - -static int -lowercase(const char *s) -{ - return (tolower(* (unsigned char *) s)); -} - -static int -mg_strncasecmp(const char *s1, const char *s2, size_t len) -{ - int diff = 0; - - if (len > 0) - do { - diff = lowercase(s1++) - lowercase(s2++); - } while (diff == 0 && s1[-1] != '\0' && --len > 0); - - return (diff); -} - -static int -mg_strcasecmp(const char *s1, const char *s2) -{ - int diff; - - do { - diff = lowercase(s1++) - lowercase(s2++); - } while (diff == 0 && s1[-1] != '\0'); - - return (diff); -} - -static char * -mg_strndup(const char *ptr, size_t len) -{ - char *p; - - if ((p = (char *) malloc(len + 1)) != NULL) - mg_strlcpy(p, ptr, len + 1); - - return (p); - -} - -static char * -mg_strdup(const char *str) -{ - return (mg_strndup(str, strlen(str))); -} - -/* - * Like snprintf(), but never returns negative value, or the value - * that is larger than a supplied buffer. - * Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability - * in his audit report. - */ -static int -mg_vsnprintf(struct mg_connection *conn, - char *buf, size_t buflen, const char *fmt, va_list ap) -{ - int n; - - if (buflen == 0) - return (0); - - n = vsnprintf(buf, buflen, fmt, ap); - - if (n < 0) { - cry(conn, "vsnprintf error"); - n = 0; - } else if (n >= (int) buflen) { - cry(conn, "truncating vsnprintf buffer: [%.*s]", - n > 200 ? 200 : n, buf); - n = (int) buflen - 1; - } - buf[n] = '\0'; - - return (n); -} - -static int -mg_snprintf(struct mg_connection *conn, - char *buf, size_t buflen, const char *fmt, ...) -{ - va_list ap; - int n; - - va_start(ap, fmt); - n = mg_vsnprintf(conn, buf, buflen, fmt, ap); - va_end(ap); - - return (n); -} - -/* - * Convert string representing a boolean value to a boolean value - */ -static bool_t -is_true(const char *str) -{ - static const char *trues[] = {"1", "yes", "true", "ja", NULL}; - int i; - - for (i = 0; trues[i] != NULL; i++) - if (str != NULL && mg_strcasecmp(str, trues[i]) == 0) - return (TRUE); - - return (FALSE); -} - -/* - * Skip the characters until one of the delimiters characters found. - * 0-terminate resulting word. Skip the rest of the delimiters if any. - * Advance pointer to buffer to the next word. Return found 0-terminated word. - */ -static char * -skip(char **buf, const char *delimiters) -{ - char *p, *begin_word, *end_word, *end_delimiters; - - begin_word = *buf; - end_word = begin_word + strcspn(begin_word, delimiters); - end_delimiters = end_word + strspn(end_word, delimiters); - - for (p = end_word; p < end_delimiters; p++) - *p = '\0'; - - *buf = end_delimiters; - - return (begin_word); -} - -/* - * Return HTTP header value, or NULL if not found. - */ -static const char * -get_header(const struct mg_request_info *ri, const char *name) -{ - int i; - - for (i = 0; i < ri->num_headers; i++) - if (!mg_strcasecmp(name, ri->http_headers[i].name)) - return (ri->http_headers[i].value); - - return (NULL); -} - -const char * -mg_get_header(const struct mg_connection *conn, const char *name) -{ - return (get_header(&conn->request_info, name)); -} - -/* - * A helper function for traversing comma separated list of values. - * It returns a list pointer shifted to the next value, of NULL if the end - * of the list found. - * Value is stored in val vector. If value has form "x=y", then eq_val - * vector is initialized to point to the "y" part, and val vector length - * is adjusted to point only to "x". - */ -static const char * -next_option(const char *list, struct vec *val, struct vec *eq_val) -{ - if (list == NULL || *list == '\0') { - /* End of the list */ - list = NULL; - } else { - val->ptr = list; - if ((list = strchr(val->ptr, ',')) != NULL) { - /* Comma found. Store length and shift the list ptr */ - val->len = list - val->ptr; - list++; - } else { - /* This value is the last one */ - list = val->ptr + strlen(val->ptr); - val->len = list - val->ptr; - } - - if (eq_val != NULL) { - /* - * Value has form "x=y", adjust pointers and lengths - * so that val points to "x", and eq_val points to "y". - */ - eq_val->len = 0; - eq_val->ptr = memchr(val->ptr, '=', val->len); - if (eq_val->ptr != NULL) { - eq_val->ptr++; /* Skip over '=' character */ - eq_val->len = val->ptr + val->len - eq_val->ptr; - val->len = (eq_val->ptr - val->ptr) - 1; - } - } - } - - return (list); -} - -#if !(defined(NO_CGI) && defined(NO_SSI)) -/* - * Verify that given file has certain extension - */ -static bool_t -match_extension(const char *path, const char *ext_list) -{ - struct vec ext_vec; - size_t path_len; - - path_len = strlen(path); - - while ((ext_list = next_option(ext_list, &ext_vec, NULL)) != NULL) - if (ext_vec.len < path_len && - mg_strncasecmp(path + path_len - ext_vec.len, - ext_vec.ptr, ext_vec.len) == 0) - return (TRUE); - - return (FALSE); -} -#endif /* !(NO_CGI && NO_SSI) */ - -/* - * Return TRUE if "uri" matches "regexp". - * '*' in the regexp means zero or more characters. - */ -static bool_t -match_regex(const char *uri, const char *regexp) -{ - if (*regexp == '\0') - return (*uri == '\0'); - - if (*regexp == '*') - do { - if (match_regex(uri, regexp + 1)) - return (TRUE); - } while (*uri++ != '\0'); - - if (*uri != '\0' && *regexp == *uri) - return (match_regex(uri + 1, regexp + 1)); - - return (FALSE); -} - -static const struct callback * -find_callback(struct mg_context *ctx, bool_t is_auth, - const char *uri, int status_code) -{ - const struct callback *cb, *found; - int i; - - found = NULL; - pthread_mutex_lock(&ctx->bind_mutex); - for (i = 0; i < ctx->num_callbacks; i++) { - cb = ctx->callbacks + i; - if ((uri != NULL && cb->uri_regex != NULL && - ((is_auth && cb->is_auth) || (!is_auth && !cb->is_auth)) && - match_regex(uri, cb->uri_regex)) || (uri == NULL && - (cb->status_code == 0 || - cb->status_code == status_code))) { - found = cb; - break; - } - } - pthread_mutex_unlock(&ctx->bind_mutex); - - return (found); -} - -/* - * For use by external application. This sets custom logging function. - */ -void -mg_set_log_callback(struct mg_context *ctx, mg_callback_t log_callback) -{ - /* If NULL is specified as a callback, revert back to the default */ - if (log_callback == NULL) - ctx->log_callback = &builtin_error_log; - else - ctx->log_callback = log_callback; -} - -/* - * Send error message back to the client. - */ -static void -send_error(struct mg_connection *conn, int status, const char *reason, - const char *fmt, ...) -{ - const struct callback *cb; - char buf[BUFSIZ]; - va_list ap; - int len; - - conn->request_info.status_code = status; - - /* If error handler is set, call it. Otherwise, send error message */ - if ((cb = find_callback(conn->ctx, FALSE, NULL, status)) != NULL) { - cb->func(conn, &conn->request_info, cb->user_data); - } else { - buf[0] = '\0'; - len = 0; - - /* Errors 1xx, 204 and 304 MUST NOT send a body */ - if (status > 199 && status != 204 && status != 304) { - len = mg_snprintf(conn, buf, sizeof(buf), - "Error %d: %s\n", status, reason); - cry(conn, "%s", buf); - - va_start(ap, fmt); - len += mg_vsnprintf(conn, buf + len, sizeof(buf) - len, - fmt, ap); - va_end(ap); - conn->num_bytes_sent = len; - } - - (void) mg_printf(conn, - "HTTP/1.1 %d %s\r\n" - "Content-Type: text/plain\r\n" - "Content-Length: %d\r\n" - "Connection: close\r\n" - "\r\n%s", status, reason, len, buf); - } -} - -#ifdef _WIN32 -static int -pthread_mutex_init(pthread_mutex_t *mutex, void *unused) -{ - unused = NULL; - *mutex = CreateMutex(NULL, FALSE, NULL); - return (*mutex == NULL ? -1 : 0); -} - -static int -pthread_mutex_destroy(pthread_mutex_t *mutex) -{ - return (CloseHandle(*mutex) == 0 ? -1 : 0); -} - -static int -pthread_mutex_lock(pthread_mutex_t *mutex) -{ - return (WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1); -} - -static int -pthread_mutex_unlock(pthread_mutex_t *mutex) -{ - return (ReleaseMutex(*mutex) == 0 ? -1 : 0); -} - -static int -pthread_cond_init(pthread_cond_t *cv, const void *unused) -{ - unused = NULL; - *cv = CreateEvent(NULL, FALSE, FALSE, NULL); - return (*cv == NULL ? -1 : 0); -} - -static int -pthread_cond_timedwait(pthread_cond_t *cv, pthread_mutex_t *mutex, - const struct timespec *ts) -{ - DWORD status; - DWORD msec = INFINITE; - time_t now; - - if (ts != NULL) { - now = time(NULL); - msec = 1000 * (now > ts->tv_sec ? 0 : ts->tv_sec - now); - } - - (void) ReleaseMutex(*mutex); - status = WaitForSingleObject(*cv, msec); - (void) WaitForSingleObject(*mutex, INFINITE); - - return (status == WAIT_OBJECT_0 ? 0 : -1); -} - -static int -pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex) -{ - return (pthread_cond_timedwait(cv, mutex, NULL)); -} - -static int -pthread_cond_signal(pthread_cond_t *cv) -{ - return (SetEvent(*cv) == 0 ? -1 : 0); -} - -static int -pthread_cond_destroy(pthread_cond_t *cv) -{ - return (CloseHandle(*cv) == 0 ? -1 : 0); -} - -static pthread_t -pthread_self(void) -{ - return (GetCurrentThreadId()); -} - -/* - * Change all slashes to backslashes. It is Windows. - */ -static void -fix_directory_separators(char *path) -{ - int i; - - for (i = 0; path[i] != '\0'; i++) { - if (path[i] == '/') - path[i] = '\\'; - /* i > 0 check is to preserve UNC paths, \\server\file.txt */ - if (path[i] == '\\' && i > 0) - while (path[i + 1] == '\\' || path[i + 1] == '/') - (void) memmove(path + i + 1, - path + i + 2, strlen(path + i + 1)); - } -} - -/* - * Encode 'path' which is assumed UTF-8 string, into UNICODE string. - * wbuf and wbuf_len is a target buffer and its length. - */ -static void -to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len) -{ - char buf[FILENAME_MAX], *p; - - mg_strlcpy(buf, path, sizeof(buf)); - fix_directory_separators(buf); - - /* Point p to the end of the file name */ - p = buf + strlen(buf) - 1; - - /* Trim trailing backslash character */ - while (p > buf && *p == '\\' && p[-1] != ':') - *p-- = '\0'; - - /* - * Protect from CGI code disclosure. - * This is very nasty hole. Windows happily opens files with - * some garbage in the end of file name. So fopen("a.cgi ", "r") - * actually opens "a.cgi", and does not return an error! - */ - if (*p == 0x20 || *p == 0x2e || *p == 0x2b || (*p & ~0x7f)) { - (void) fprintf(stderr, "Rejecting suspicious path: [%s]", buf); - buf[0] = '\0'; - } - - (void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len); -} - -#if defined(_WIN32_WCE) - -static time_t -time(time_t *ptime) -{ - time_t t; - SYSTEMTIME st; - FILETIME ft; - - GetSystemTime(&st); - SystemTimeToFileTime(&st, &ft); - t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime); - - if (ptime != NULL) - *ptime = t; - - return (t); -} - -static time_t -mktime(struct tm *ptm) -{ - SYSTEMTIME st; - FILETIME ft, lft; - - st.wYear = ptm->tm_year + 1900; - st.wMonth = ptm->tm_mon + 1; - st.wDay = ptm->tm_mday; - st.wHour = ptm->tm_hour; - st.wMinute = ptm->tm_min; - st.wSecond = ptm->tm_sec; - st.wMilliseconds = 0; - - SystemTimeToFileTime(&st, &ft); - LocalFileTimeToFileTime(&ft, &lft); - return (time_t)((MAKEUQUAD(lft.dwLowDateTime, lft.dwHighDateTime) - - EPOCH_DIFF) / RATE_DIFF); -} - -static struct tm * -localtime(const time_t *ptime, struct tm *ptm) -{ - uint64_t t = ((uint64_t)*ptime) * RATE_DIFF + EPOCH_DIFF; - FILETIME ft, lft; - SYSTEMTIME st; - TIME_ZONE_INFORMATION tzinfo; - - if (ptm == NULL) - return NULL; - - * (uint64_t *) &ft = t; - FileTimeToLocalFileTime(&ft, &lft); - FileTimeToSystemTime(&lft, &st); - ptm->tm_year = st.wYear - 1900; - ptm->tm_mon = st.wMonth - 1; - ptm->tm_wday = st.wDayOfWeek; - ptm->tm_mday = st.wDay; - ptm->tm_hour = st.wHour; - ptm->tm_min = st.wMinute; - ptm->tm_sec = st.wSecond; - ptm->tm_yday = 0; // hope nobody uses this - ptm->tm_isdst = ((GetTimeZoneInformation(&tzinfo) == - TIME_ZONE_ID_DAYLIGHT) ? 1 : 0); - - return ptm; -} - -static size_t -strftime(char *dst, size_t dst_size, const char *fmt, const struct tm *tm) -{ - (void) snprintf(dst, dst_size, "implement strftime() for WinCE"); - return (0); -} -#endif - -static int -mg_rename(const char* oldname, const char* newname) -{ - wchar_t woldbuf[FILENAME_MAX]; - wchar_t wnewbuf[FILENAME_MAX]; - - to_unicode(oldname, woldbuf, ARRAY_SIZE(woldbuf)); - to_unicode(newname, wnewbuf, ARRAY_SIZE(wnewbuf)); - - return (MoveFileW(woldbuf, wnewbuf) ? 0 : -1); -} - - -static FILE * -mg_fopen(const char *path, const char *mode) -{ - wchar_t wbuf[FILENAME_MAX], wmode[20]; - - to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); - MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode)); - - return (_wfopen(wbuf, wmode)); -} - -static int -mg_stat(const char *path, struct mgstat *stp) -{ - int ok = -1; /* Error */ - wchar_t wbuf[FILENAME_MAX]; - WIN32_FILE_ATTRIBUTE_DATA info; - - to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); - - if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) { - stp->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh); - stp->mtime = SYS2UNIX_TIME(info.ftLastWriteTime.dwLowDateTime, - info.ftLastWriteTime.dwHighDateTime); - stp->is_directory = - info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; - ok = 0; /* Success */ - } - - return (ok); -} - -static int -mg_remove(const char *path) -{ - wchar_t wbuf[FILENAME_MAX]; - - to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); - - return (DeleteFileW(wbuf) ? 0 : -1); -} - -static int -mg_mkdir(const char *path, int mode) -{ - char buf[FILENAME_MAX]; - wchar_t wbuf[FILENAME_MAX]; - - mode = 0; /* Unused */ - mg_strlcpy(buf, path, sizeof(buf)); - fix_directory_separators(buf); - - (void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf)); - - return (CreateDirectoryW(wbuf, NULL) ? 0 : -1); -} - -static char * -mg_getcwd(char *buf, int buf_len) -{ - wchar_t wbuf[FILENAME_MAX], *basename; - - if (GetModuleFileNameW(NULL, wbuf, ARRAY_SIZE(wbuf))) { - if ((basename = wcsrchr(wbuf, DIRSEP)) != NULL) { - *basename = L'\0'; - if (WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, buf, - buf_len, NULL, NULL) > 0) - return (buf); - } - } - - return (NULL); -} - -/* - * Implementation of POSIX opendir/closedir/readdir for Windows. - */ -static DIR * -opendir(const char *name) -{ - DIR *dir = NULL; - wchar_t wpath[FILENAME_MAX]; - DWORD attrs; - - if (name == NULL) { - SetLastError(ERROR_BAD_ARGUMENTS); - } else if ((dir = (DIR *) malloc(sizeof(*dir))) == NULL) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - } else { - to_unicode(name, wpath, ARRAY_SIZE(wpath)); - attrs = GetFileAttributesW(wpath); - if (attrs != 0xFFFFFFFF && - ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) { - (void) wcscat(wpath, L"\\*"); - dir->handle = FindFirstFileW(wpath, &dir->info); - dir->result.d_name[0] = '\0'; - } else { - free(dir); - dir = NULL; - } - } - - return (dir); -} - -static int -closedir(DIR *dir) -{ - int result = 0; - - if (dir != NULL) { - if (dir->handle != INVALID_HANDLE_VALUE) - result = FindClose(dir->handle) ? 0 : -1; - - free(dir); - } else { - result = -1; - SetLastError(ERROR_BAD_ARGUMENTS); - } - - return (result); -} - -struct dirent * -readdir(DIR *dir) -{ - struct dirent *result = 0; - - if (dir) { - if (dir->handle != INVALID_HANDLE_VALUE) { - result = &dir->result; - (void) WideCharToMultiByte(CP_UTF8, 0, - dir->info.cFileName, -1, result->d_name, - sizeof(result->d_name), NULL, NULL); - - if (!FindNextFileW(dir->handle, &dir->info)) { - (void) FindClose(dir->handle); - dir->handle = INVALID_HANDLE_VALUE; - } - - } else { - SetLastError(ERROR_FILE_NOT_FOUND); - } - } else { - SetLastError(ERROR_BAD_ARGUMENTS); - } - - return (result); -} - -#define set_close_on_exec(fd) /* No FD_CLOEXEC on Windows */ - -static int -start_thread(struct mg_context *ctx, mg_thread_func_t func, void *param) -{ - HANDLE hThread; - - ctx = NULL; /* Unused */ - - hThread = CreateThread(NULL, 0, - (LPTHREAD_START_ROUTINE) func, param, 0, NULL); - - if (hThread != NULL) - (void) CloseHandle(hThread); - - return (hThread == NULL ? -1 : 0); -} - -static HANDLE -dlopen(const char *dll_name, int flags) -{ - wchar_t wbuf[FILENAME_MAX]; - - flags = 0; /* Unused */ - to_unicode(dll_name, wbuf, ARRAY_SIZE(wbuf)); - - return (LoadLibraryW(wbuf)); -} - -#if !defined(NO_CGI) -static int -kill(pid_t pid, int sig_num) -{ - (void) TerminateProcess(pid, sig_num); - (void) CloseHandle(pid); - return (0); -} - -static pid_t -spawn_process(struct mg_connection *conn, const char *prog, char *envblk, - char *envp[], int fd_stdin, int fd_stdout, const char *dir) -{ - HANDLE me; - char *p, *interp, cmdline[FILENAME_MAX], line[FILENAME_MAX]; - FILE *fp; - STARTUPINFOA si; - PROCESS_INFORMATION pi; - - envp = NULL; /* Unused */ - - (void) memset(&si, 0, sizeof(si)); - (void) memset(&pi, 0, sizeof(pi)); - - /* XXX redirect CGI errors to the error log file */ - si.cb = sizeof(si); - si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - si.wShowWindow = SW_HIDE; - - me = GetCurrentProcess(); - (void) DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdin), me, - &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS); - (void) DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdout), me, - &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS); - - /* If CGI file is a script, try to read the interpreter line */ - interp = conn->ctx->options[OPT_CGI_INTERPRETER]; - if (interp == NULL) { - line[2] = '\0'; - (void) mg_snprintf(conn, cmdline, sizeof(cmdline), "%s%c%s", - dir, DIRSEP, prog); - if ((fp = fopen(cmdline, "r")) != NULL) { - (void) fgets(line, sizeof(line), fp); - if (memcmp(line, "#!", 2) != 0) - line[2] = '\0'; - /* Trim whitespaces from interpreter name */ - for (p = &line[strlen(line) - 1]; p > line && - isspace(*p); p--) - *p = '\0'; - (void) fclose(fp); - } - interp = line + 2; - } - - if ((p = (char *) strrchr(prog, '/')) != NULL) - prog = p + 1; - - (void) mg_snprintf(conn, cmdline, sizeof(cmdline), "%s%s%s", - interp, interp[0] == '\0' ? "" : " ", prog); - - (void) mg_snprintf(conn, line, sizeof(line), "%s", dir); - fix_directory_separators(line); - - DEBUG_TRACE((DEBUG_MGS_PREFIX "%s: Running [%s]", __func__, cmdline)); - if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, - CREATE_NEW_PROCESS_GROUP, envblk, line, &si, &pi) == 0) { - cry(conn, "%s: CreateProcess(%s): %d", - __func__, cmdline, ERRNO); - pi.hProcess = (pid_t) -1; - } else { - (void) close(fd_stdin); - (void) close(fd_stdout); - } - - (void) CloseHandle(si.hStdOutput); - (void) CloseHandle(si.hStdInput); - (void) CloseHandle(pi.hThread); - - return ((pid_t) pi.hProcess); -} - -static int -pipe(int *fds) -{ - return (_pipe(fds, BUFSIZ, _O_BINARY)); -} -#endif /* !NO_CGI */ - -static int -set_non_blocking_mode(struct mg_connection *conn, SOCKET sock) -{ - unsigned long on = 1; - - conn = NULL; /* unused */ - return (ioctlsocket(sock, FIONBIO, &on)); -} - -#else - -static int -mg_stat(const char *path, struct mgstat *stp) -{ - struct stat st; - int ok; - - if (stat(path, &st) == 0) { - ok = 0; - stp->size = st.st_size; - stp->mtime = st.st_mtime; - stp->is_directory = S_ISDIR(st.st_mode); - } else { - ok = -1; - } - - return (ok); -} - -static void -set_close_on_exec(int fd) -{ - (void) fcntl(fd, F_SETFD, FD_CLOEXEC); -} - -static int -start_thread(struct mg_context *ctx, mg_thread_func_t func, void *param) -{ - pthread_t thread_id; - pthread_attr_t attr; - int retval; - - (void) pthread_attr_init(&attr); - (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - if ((retval = pthread_create(&thread_id, &attr, func, param)) != 0) - cry(fc(ctx), "%s: %s", __func__, strerror(retval)); - - return (retval); -} - -#ifndef NO_CGI -static pid_t -spawn_process(struct mg_connection *conn, const char *prog, char *envblk, - char *envp[], int fd_stdin, int fd_stdout, const char *dir) -{ - pid_t pid; - const char *interp; - - envblk = NULL; /* unused */ - - if ((pid = fork()) == -1) { - /* Parent */ - send_error(conn, 500, http_500_error, - "fork(): %s", strerror(ERRNO)); - } else if (pid == 0) { - /* Child */ - if (chdir(dir) != 0) { - cry(conn, "%s: chdir(%s): %s", - __func__, dir, strerror(ERRNO)); - } else if (dup2(fd_stdin, 0) == -1) { - cry(conn, "%s: dup2(stdin, %d): %s", - __func__, fd_stdin, strerror(ERRNO)); - } else if (dup2(fd_stdout, 1) == -1) { - cry(conn, "%s: dup2(stdout, %d): %s", - __func__, fd_stdout, strerror(ERRNO)); - } else { - /* If error file is specified, send errors there */ - if (conn->ctx->error_log != NULL) - (void) dup2(fileno(conn->ctx->error_log), 2); - - (void) close(fd_stdin); - (void) close(fd_stdout); - - /* Execute CGI program */ - interp = conn->ctx->options[OPT_CGI_INTERPRETER]; - if (interp == NULL) { - (void) execle(prog, prog, NULL, envp); - cry(conn, "%s: execle(%s): %s", - __func__, prog, strerror(ERRNO)); - } else { - (void) execle(interp, interp, prog, NULL, envp); - cry(conn, "%s: execle(%s %s): %s", - __func__, interp, prog, strerror(ERRNO)); - } - } - exit(EXIT_FAILURE); - } else { - /* Parent. Close stdio descriptors */ - (void) close(fd_stdin); - (void) close(fd_stdout); - } - - return (pid); -} -#endif /* !NO_CGI */ - -static int -set_non_blocking_mode(struct mg_connection *conn, SOCKET sock) -{ - int flags, ok = -1; - - if ((flags = fcntl(sock, F_GETFL, 0)) == -1) { - cry(conn, "%s: fcntl(F_GETFL): %d", __func__, ERRNO); - } else if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) != 0) { - cry(conn, "%s: fcntl(F_SETFL): %d", __func__, ERRNO); - } else { - ok = 0; /* Success */ - } - - return (ok); -} -#endif /* _WIN32 */ - -static void -lock_option(struct mg_context *ctx, int opt_index) -{ - if (pthread_mutex_lock(&ctx->opt_mutex[opt_index]) != 0) - cry(fc(ctx), "pthread_mutex_lock: %s", strerror(ERRNO)); -} - -static void -unlock_option(struct mg_context *ctx, int opt_index) -{ - if (pthread_mutex_unlock(&ctx->opt_mutex[opt_index]) != 0) - cry(fc(ctx), "pthread_mutex_unlock: %s", strerror(ERRNO)); -} - -/* - * Write data to the IO channel - opened file descriptor, socket or SSL - * descriptor. Return number of bytes written. - */ -static uint64_t -push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf, uint64_t len) -{ - uint64_t sent; - int n, k; - - sent = 0; - while (sent < len) { - - /* How many bytes we send in this iteration */ - k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent); - - if (ssl != NULL) { - n = SSL_write(ssl, buf + sent, k); - } else if (fp != NULL) { - n = fwrite(buf + sent, 1, k, fp); - if (ferror(fp)) - n = -1; - } else { - n = send(sock, buf + sent, k, 0); - } - - if (n < 0) - break; - - sent += n; - } - - return (sent); -} - -/* - * Read from IO channel - opened file descriptor, socket, or SSL descriptor. - * Return number of bytes read. - */ -static int -pull(FILE *fp, SOCKET sock, SSL *ssl, char *buf, int len) -{ - int nread; - - if (ssl != NULL) { - nread = SSL_read(ssl, buf, len); - } else if (fp != NULL) { - nread = fread(buf, 1, (size_t) len, fp); - if (ferror(fp)) - nread = -1; - } else { - nread = recv(sock, buf, (size_t) len, 0); - } - - return (nread); -} - -int -mg_write(struct mg_connection *conn, const void *buf, int len) -{ - assert(len >= 0); - return ((int) push(NULL, conn->client.sock, conn->ssl, - (const char *) buf, (uint64_t) len)); -} - -int -mg_printf(struct mg_connection *conn, const char *fmt, ...) -{ - char buf[MAX_REQUEST_SIZE]; - int len; - va_list ap; - - va_start(ap, fmt); - len = mg_vsnprintf(conn, buf, sizeof(buf), fmt, ap); - va_end(ap); - - return (mg_write(conn, buf, len)); -} - -/* - * Return content length of the request, or UNKNOWN_CONTENT_LENGTH constant if - * Content-Length header is not set. - */ -static uint64_t -get_content_length(const struct mg_connection *conn) -{ - const char *cl = mg_get_header(conn, "Content-Length"); - return (cl == NULL ? UNKNOWN_CONTENT_LENGTH : strtoull(cl, NULL, 10)); -} - -/* - * URL-decode input buffer into destination buffer. - * 0-terminate the destination buffer. Return the length of decoded data. - * form-url-encoded data differs from URI encoding in a way that it - * uses '+' as character for space, see RFC 1866 section 8.2.1 - * http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt - */ -static size_t -url_decode(const char *src, size_t src_len, char *dst, size_t dst_len, - bool_t is_form_url_encoded) -{ - size_t i, j; - int a, b; -#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') - - for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { - if (src[i] == '%' && - isxdigit(* (unsigned char *) (src + i + 1)) && - isxdigit(* (unsigned char *) (src + i + 2))) { - a = tolower(* (unsigned char *) (src + i + 1)); - b = tolower(* (unsigned char *) (src + i + 2)); - dst[j] = ((HEXTOI(a) << 4) | HEXTOI(b)) & 0xff; - i += 2; - } else if (is_form_url_encoded && src[i] == '+') { - dst[j] = ' '; - } else { - dst[j] = src[i]; - } - } - - dst[j] = '\0'; /* Null-terminate the destination */ - - return (j); -} - -/* - * Search for a form variable in a given buffer. - * Semantic is the same as for mg_get_var(). - */ -static char * -get_var(const char *name, const char *buf, size_t buf_len) -{ - const char *p, *e, *s; - char *val; - size_t var_len, len; - - var_len = strlen(name); - e = buf + buf_len; - val = NULL; - - /* buf is "var1=val1&var2=val2...". Find variable first */ - for (p = buf; p + var_len < e; p++) - if ((p == buf || p[-1] == '&') && p[var_len] == '=' && - !mg_strncasecmp(name, p, var_len)) { - - /* Point p to variable value */ - p += var_len + 1; - - /* Point s to the end of the value */ - s = (const char *) memchr(p, '&', e - p); - if (s == NULL) - s = e; - - /* Try to allocate the buffer */ - len = s - p; - if ((val = (char *) malloc(len + 1)) != NULL) - (void) url_decode(p, len, val, len + 1, TRUE); - break; - } - - return (val); -} - -/* - * Free the pointer returned by mg_get_var(). This is needed for languages - * like python, to have an ability to free allocated data without - * loading C runtime library and calling free(). - */ -void -mg_free(char *data) -{ - free(data); -} - -/* - * Return form data variable. - * It can be specified in query string, or in the POST data. - * Return NULL if the variable not found, or allocated 0-terminated value. - * It is caller's responsibility to free the returned value. - */ -char * -mg_get_var(const struct mg_connection *conn, const char *name) -{ - const struct mg_request_info *ri = &conn->request_info; - char *v1, *v2; - - v1 = v2 = NULL; - - /* Look in both query_string and POST data */ - if (ri->query_string != NULL) - v1 = get_var(name, ri->query_string, strlen(ri->query_string)); - if (ri->post_data_len > 0) - v2 = get_var(name, ri->post_data, ri->post_data_len); - - /* If they both have queried variable, POST data wins */ - if (v1 != NULL && v2 != NULL) - free(v1); - - return (v2 == NULL ? v1 : v2); -} - -/* - * Transform URI to the file name. - */ -static void -convert_uri_to_file_name(struct mg_connection *conn, const char *uri, - char *buf, size_t buf_len) -{ - struct mg_context *ctx = conn->ctx; - struct vec uri_vec, path_vec; - const char *list; - - lock_option(ctx, OPT_ROOT); - mg_snprintf(conn, buf, buf_len, "%s%s", ctx->options[OPT_ROOT], uri); - unlock_option(ctx, OPT_ROOT); - - /* If requested URI has aliased prefix, use alternate root */ - lock_option(ctx, OPT_ALIASES); - list = ctx->options[OPT_ALIASES]; - - while ((list = next_option(list, &uri_vec, &path_vec)) != NULL) { - if (memcmp(uri, uri_vec.ptr, uri_vec.len) == 0) { - (void) mg_snprintf(conn, buf, buf_len, "%.*s%s", - path_vec.len, path_vec.ptr, uri + uri_vec.len); - break; - } - } - unlock_option(ctx, OPT_ALIASES); - -#ifdef _WIN32 - fix_directory_separators(buf); -#endif /* _WIN32 */ -} - -/* - * Setup listening socket on given address, return socket. - * Address format: [local_ip_address:]port_number - */ -static SOCKET -mg_open_listening_port(struct mg_context *ctx, const char *str, struct usa *usa) -{ - SOCKET sock; - int on = 1, a, b, c, d, port; - - /* MacOS needs that. If we do not zero it, bind() will fail. */ - (void) memset(usa, 0, sizeof(*usa)); - - if (sscanf(str, "%d.%d.%d.%d:%d", &a, &b, &c, &d, &port) == 5) { - /* IP address to bind to is specified */ - usa->u.sin.sin_addr.s_addr = - htonl((a << 24) | (b << 16) | (c << 8) | d); - } else if (sscanf(str, "%d", &port) == 1) { - /* Only port number is specified. Bind to all addresses */ - usa->u.sin.sin_addr.s_addr = htonl(INADDR_ANY); - } else { - return (INVALID_SOCKET); - } - - usa->len = sizeof(usa->u.sin); - usa->u.sin.sin_family = AF_INET; - usa->u.sin.sin_port = htons((uint16_t) port); - - if ((sock = socket(PF_INET, SOCK_STREAM, 6)) != INVALID_SOCKET && - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, - (char *) &on, sizeof(on)) == 0 && - bind(sock, &usa->u.sa, usa->len) == 0 && - listen(sock, 128) == 0) { - /* Success */ - set_close_on_exec(sock); - } else { - /* Error */ - cry(fc(ctx), "%s(%d): %s", __func__, port, strerror(ERRNO)); - if (sock != INVALID_SOCKET) - (void) closesocket(sock); - sock = INVALID_SOCKET; - } - - return (sock); -} - -/* - * Check whether full request is buffered. Return: - * -1 if request is malformed - * 0 if request is not yet fully buffered - * >0 actual request length, including last \r\n\r\n - */ -static int -get_request_len(const char *buf, size_t buflen) -{ - const char *s, *e; - int len = 0; - - for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++) - /* Control characters are not allowed but >=128 is. */ - if (!isprint(* (unsigned char *) s) && *s != '\r' && - *s != '\n' && * (unsigned char *) s < 128) - len = -1; - else if (s[0] == '\n' && s[1] == '\n') - len = (int) (s - buf) + 2; - else if (s[0] == '\n' && &s[1] < e && - s[1] == '\r' && s[2] == '\n') - len = (int) (s - buf) + 3; - - return (len); -} - -/* - * Convert month to the month number. Return -1 on error, or month number - */ -static int -montoi(const char *s) -{ - size_t i; - - for (i = 0; i < sizeof(month_names) / sizeof(month_names[0]); i++) - if (!strcmp(s, month_names[i])) - return ((int) i); - - return (-1); -} - -/* - * Parse date-time string, and return the corresponding time_t value - */ -static time_t -date_to_epoch(const char *s) -{ - time_t current_time; - struct tm tm, *tmp; - char mon[32]; - int sec, min, hour, mday, month, year; - - (void) memset(&tm, 0, sizeof(tm)); - sec = min = hour = mday = month = year = 0; - - if (((sscanf(s, "%d/%3s/%d %d:%d:%d", - &mday, mon, &year, &hour, &min, &sec) == 6) || - (sscanf(s, "%d %3s %d %d:%d:%d", - &mday, mon, &year, &hour, &min, &sec) == 6) || - (sscanf(s, "%*3s, %d %3s %d %d:%d:%d", - &mday, mon, &year, &hour, &min, &sec) == 6) || - (sscanf(s, "%d-%3s-%d %d:%d:%d", - &mday, mon, &year, &hour, &min, &sec) == 6)) && - (month = montoi(mon)) != -1) { - tm.tm_mday = mday; - tm.tm_mon = month; - tm.tm_year = year; - tm.tm_hour = hour; - tm.tm_min = min; - tm.tm_sec = sec; - } - - if (tm.tm_year > 1900) - tm.tm_year -= 1900; - else if (tm.tm_year < 70) - tm.tm_year += 100; - - /* Set Daylight Saving Time field */ - current_time = time(NULL); - tmp = localtime(¤t_time); - tm.tm_isdst = tmp->tm_isdst; - - return (mktime(&tm)); -} - -/* - * Protect against directory disclosure attack by removing '..', - * excessive '/' and '\' characters - */ -static void -remove_double_dots_and_double_slashes(char *s) -{ - char *p = s; - - while (*s != '\0') { - *p++ = *s++; - if (s[-1] == '/' || s[-1] == '\\') { - /* Skip all following slashes and backslashes */ - while (*s == '/' || *s == '\\') - s++; - - /* Skip all double-dots */ - while (*s == '.' && s[1] == '.') - s += 2; - } - } - *p = '\0'; -} - -/* - * Built-in mime types - */ -static const struct { - const char *extension; - size_t ext_len; - const char *mime_type; - size_t mime_type_len; -} mime_types[] = { - {".html", 5, "text/html", 9}, - {".htm", 4, "text/html", 9}, - {".shtm", 5, "text/html", 9}, - {".shtml", 6, "text/html", 9}, - {".css", 4, "text/css", 8}, - {".js", 3, "application/x-javascript", 24}, - {".ico", 4, "image/x-icon", 12}, - {".gif", 4, "image/gif", 9}, - {".jpg", 4, "image/jpeg", 10}, - {".jpeg", 5, "image/jpeg", 10}, - {".png", 4, "image/png", 9}, - {".svg", 4, "image/svg+xml", 13}, - {".torrent", 8, "application/x-bittorrent", 24}, - {".wav", 4, "audio/x-wav", 11}, - {".mp3", 4, "audio/x-mp3", 11}, - {".mid", 4, "audio/mid", 9}, - {".m3u", 4, "audio/x-mpegurl", 15}, - {".ram", 4, "audio/x-pn-realaudio", 20}, - {".ra", 3, "audio/x-pn-realaudio", 20}, - {".doc", 4, "application/msword", 19}, - {".exe", 4, "application/octet-stream", 24}, - {".zip", 4, "application/x-zip-compressed", 28}, - {".xls", 4, "application/excel", 17}, - {".tgz", 4, "application/x-tar-gz", 20}, - {".tar", 4, "application/x-tar", 17}, - {".gz", 3, "application/x-gunzip", 20}, - {".arj", 4, "application/x-arj-compressed", 28}, - {".rar", 4, "application/x-arj-compressed", 28}, - {".rtf", 4, "application/rtf", 15}, - {".pdf", 4, "application/pdf", 15}, - {".swf", 4, "application/x-shockwave-flash",29}, - {".mpg", 4, "video/mpeg", 10}, - {".mpeg", 5, "video/mpeg", 10}, - {".asf", 4, "video/x-ms-asf", 14}, - {".avi", 4, "video/x-msvideo", 15}, - {".bmp", 4, "image/bmp", 9}, - {NULL, 0, NULL, 0} -}; - -/* - * Look at the "path" extension and figure what mime type it has. - * Store mime type in the vector. - */ -static void -get_mime_type(struct mg_context *ctx, const char *path, struct vec *vec) -{ - struct vec ext_vec, mime_vec; - const char *list, *ext; - size_t i, path_len; - - path_len = strlen(path); - - /* - * Scan user-defined mime types first, in case user wants to - * override default mime types. - */ - lock_option(ctx, OPT_MIME_TYPES); - list = ctx->options[OPT_MIME_TYPES]; - while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) { - /* ext now points to the path suffix */ - ext = path + path_len - ext_vec.len; - if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) { - *vec = mime_vec; - unlock_option(ctx, OPT_MIME_TYPES); - return; - } - } - unlock_option(ctx, OPT_MIME_TYPES); - - /* Now scan built-in mime types */ - for (i = 0; mime_types[i].extension != NULL; i++) { - ext = path + (path_len - mime_types[i].ext_len); - if (path_len > mime_types[i].ext_len && - mg_strcasecmp(ext, mime_types[i].extension) == 0) { - vec->ptr = mime_types[i].mime_type; - vec->len = mime_types[i].mime_type_len; - return; - } - } - - /* Nothing found. Fall back to text/plain */ - vec->ptr = "text/plain"; - vec->len = 10; -} - -#ifndef HAVE_MD5 -typedef struct MD5Context { - uint32_t buf[4]; - uint32_t bits[2]; - unsigned char in[64]; -} MD5_CTX; - -#if __BYTE_ORDER == 1234 -#define byteReverse(buf, len) /* Nothing */ -#else -/* - * Note: this code is harmless on little-endian machines. - */ -static void -byteReverse(unsigned char *buf, unsigned longs) -{ - uint32_t t; - do { - t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | - ((unsigned) buf[1] << 8 | buf[0]); - *(uint32_t *) buf = t; - buf += 4; - } while (--longs); -} -#endif /* __BYTE_ORDER */ - -/* The four core functions - F1 is optimized somewhat */ - -/* #define F1(x, y, z) (x & y | ~x & z) */ -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -/* This is the central step in the MD5 algorithm. */ -#define MD5STEP(f, w, x, y, z, data, s) \ -( w += f(x, y, z) + data, w = w<>(32-s), w += x ) - -/* - * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious - * initialization constants. - */ -static void -MD5Init(MD5_CTX *ctx) -{ - ctx->buf[0] = 0x67452301; - ctx->buf[1] = 0xefcdab89; - ctx->buf[2] = 0x98badcfe; - ctx->buf[3] = 0x10325476; - - ctx->bits[0] = 0; - ctx->bits[1] = 0; -} - -/* - * The core of the MD5 algorithm, this alters an existing MD5 hash to - * reflect the addition of 16 longwords of new data. MD5Update blocks - * the data and converts bytes into longwords for this routine. - */ -static void -MD5Transform(uint32_t buf[4], uint32_t const in[16]) -{ - register uint32_t a, b, c, d; - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; - - MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; -} - -/* - * Update context to reflect the concatenation of another buffer full - * of bytes. - */ -static void -MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) -{ - uint32_t t; - - /* Update bitcount */ - - t = ctx->bits[0]; - if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) - ctx->bits[1]++; /* Carry from low to high */ - ctx->bits[1] += len >> 29; - - t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ - - /* Handle any leading odd-sized chunks */ - - if (t) { - unsigned char *p = (unsigned char *) ctx->in + t; - - t = 64 - t; - if (len < t) { - memcpy(p, buf, len); - return; - } - memcpy(p, buf, t); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32_t *) ctx->in); - buf += t; - len -= t; - } - /* Process data in 64-byte chunks */ - - while (len >= 64) { - memcpy(ctx->in, buf, 64); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32_t *) ctx->in); - buf += 64; - len -= 64; - } - - /* Handle any remaining bytes of data. */ - - memcpy(ctx->in, buf, len); -} - -/* - * Final wrapup - pad to 64-byte boundary with the bit pattern - * 1 0* (64-bit count of bits processed, MSB-first) - */ -static void -MD5Final(unsigned char digest[16], MD5_CTX *ctx) -{ - unsigned count; - unsigned char *p; - - /* Compute number of bytes mod 64 */ - count = (ctx->bits[0] >> 3) & 0x3F; - - /* Set the first char of padding to 0x80. This is safe since there is - always at least one byte free */ - p = ctx->in + count; - *p++ = 0x80; - - /* Bytes of padding needed to make 64 bytes */ - count = 64 - 1 - count; - - /* Pad out to 56 mod 64 */ - if (count < 8) { - /* Two lots of padding: Pad the first block to 64 bytes */ - memset(p, 0, count); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32_t *) ctx->in); - - /* Now fill the next block with 56 bytes */ - memset(ctx->in, 0, 56); - } else { - /* Pad block to 56 bytes */ - memset(p, 0, count - 8); - } - byteReverse(ctx->in, 14); - - /* Append length in bits and transform */ - ((uint32_t *) ctx->in)[14] = ctx->bits[0]; - ((uint32_t *) ctx->in)[15] = ctx->bits[1]; - - MD5Transform(ctx->buf, (uint32_t *) ctx->in); - byteReverse((unsigned char *) ctx->buf, 4); - memcpy(digest, ctx->buf, 16); - memset((char *) ctx, 0, sizeof(ctx)); /* In case it's sensitive */ -} -#endif /* !HAVE_MD5 */ - -/* - * Stringify binary data. Output buffer must be twice as big as input, - * because each byte takes 2 bytes in string representation - */ -static void -bin2str(char *to, const unsigned char *p, size_t len) -{ - static const char *hex = "0123456789abcdef"; - - for (; len--; p++) { - *to++ = hex[p[0] >> 4]; - *to++ = hex[p[0] & 0x0f]; - } - *to = '\0'; -} - -/* - * Return stringified MD5 hash for list of vectors. - * buf must point to 33-bytes long buffer - */ -static void -mg_md5(char *buf, ...) -{ - unsigned char hash[16]; - const char *p; - va_list ap; - MD5_CTX ctx; - - MD5Init(&ctx); - - va_start(ap, buf); - while ((p = va_arg(ap, const char *)) != NULL) - MD5Update(&ctx, (unsigned char *) p, (int) strlen(p)); - va_end(ap); - - MD5Final(hash, &ctx); - bin2str(buf, hash, sizeof(hash)); -} - -/* - * Check the user's password, return 1 if OK - */ -static bool_t -check_password(const char *method, const char *ha1, const char *uri, - const char *nonce, const char *nc, const char *cnonce, - const char *qop, const char *response) -{ - char ha2[32 + 1], expected_response[32 + 1]; - - /* XXX Due to a bug in MSIE, we do not compare the URI */ - /* Also, we do not check for authentication timeout */ - if (/*strcmp(dig->uri, c->ouri) != 0 || */ - strlen(response) != 32 /*|| - now - strtoul(dig->nonce, NULL, 10) > 3600 */) - return (FALSE); - - mg_md5(ha2, method, ":", uri, NULL); - mg_md5(expected_response, ha1, ":", nonce, ":", nc, - ":", cnonce, ":", qop, ":", ha2, NULL); - - return (!mg_strcasecmp(response, expected_response)); -} - -/* - * Use the global passwords file, if specified by auth_gpass option, - * or search for .htpasswd in the requested directory. - */ -static FILE * -open_auth_file(struct mg_connection *conn, const char *path) -{ - struct mg_context *ctx = conn->ctx; - char name[FILENAME_MAX]; - const char *p, *e; - struct mgstat st; - FILE *fp; - - if (ctx->options[OPT_AUTH_GPASSWD] != NULL) { - /* Use global passwords file */ - fp = mg_fopen(ctx->options[OPT_AUTH_GPASSWD], "r"); - if (fp == NULL) - cry(fc(ctx), "fopen(%s): %s", - ctx->options[OPT_AUTH_GPASSWD], strerror(ERRNO)); - } else if (!mg_stat(path, &st) && st.is_directory) { - (void) mg_snprintf(conn, name, sizeof(name), "%s%c%s", - path, DIRSEP, PASSWORDS_FILE_NAME); - fp = mg_fopen(name, "r"); - } else { - /* - * Try to find .htpasswd in requested directory. - * Given the path, create the path to .htpasswd file - * in the same directory. Find the right-most - * directory separator character first. That would be the - * directory name. If directory separator character is not - * found, 'e' will point to 'p'. - */ - for (p = path, e = p + strlen(p) - 1; e > p; e--) - if (IS_DIRSEP_CHAR(*e)) - break; - - /* - * Make up the path by concatenating directory name and - * .htpasswd file name. - */ - (void) mg_snprintf(conn, name, sizeof(name), "%.*s%c%s", - (int) (e - p), p, DIRSEP, PASSWORDS_FILE_NAME); - fp = mg_fopen(name, "r"); - } - - return (fp); -} - -/* - * Parsed Authorization: header - */ -struct ah { - char *user, *uri, *cnonce, *response, *qop, *nc, *nonce; -}; - -static bool_t -parse_auth_header(struct mg_connection *conn, char *buf, size_t buf_size, - struct ah *ah) -{ - char *name, *value, *s; - const char *auth_header; - - if ((auth_header = mg_get_header(conn, "Authorization")) == NULL || - mg_strncasecmp(auth_header, "Digest ", 7) != 0) - return (FALSE); - - /* Make modifiable copy of the auth header */ - (void) mg_strlcpy(buf, auth_header + 7, buf_size); - - s = buf; - (void) memset(ah, 0, sizeof(*ah)); - - /* Gobble initial spaces */ - while (isspace(* (unsigned char *) s)) - s++; - - /* Parse authorization header */ - for (;;) { - name = skip(&s, "="); - value = skip(&s, ", "); - - if (*value == '"') { - value++; - value[strlen(value) - 1] = '\0'; - } else if (*value == '\0') { - break; - } - - if (!strcmp(name, "username")) { - ah->user = value; - } else if (!strcmp(name, "cnonce")) { - ah->cnonce = value; - } else if (!strcmp(name, "response")) { - ah->response = value; - } else if (!strcmp(name, "uri")) { - ah->uri = value; - } else if (!strcmp(name, "qop")) { - ah->qop = value; - } else if (!strcmp(name, "nc")) { - ah->nc = value; - } else if (!strcmp(name, "nonce")) { - ah->nonce = value; - } - } - - /* CGI needs it as REMOTE_USER */ - if (ah->user != NULL) - conn->request_info.remote_user = mg_strdup(ah->user); - - return (TRUE); -} - -/* - * Authorize against the opened passwords file. Return 1 if authorized. - */ -static bool_t -authorize(struct mg_connection *conn, FILE *fp) -{ - struct ah ah; - char line[256], f_user[256], domain[256], ha1[256], - buf[MAX_REQUEST_SIZE]; - - if (!parse_auth_header(conn, buf, sizeof(buf), &ah)) - return (FALSE); - - /* Loop over passwords file */ - while (fgets(line, sizeof(line), fp) != NULL) { - - if (sscanf(line, "%[^:]:%[^:]:%s", f_user, domain, ha1) != 3) - continue; - - if (!strcmp(ah.user, f_user) && - !strcmp(domain, conn->ctx->options[OPT_AUTH_DOMAIN])) - return (check_password( - conn->request_info.request_method, ha1, - ah.uri, ah.nonce, ah.nc, ah.cnonce, - ah.qop, ah.response)); - } - - return (FALSE); -} - -/* - * Return TRUE if request is authorised, FALSE otherwise. - */ -static bool_t -check_authorization(struct mg_connection *conn, const char *path) -{ - FILE *fp; - char fname[FILENAME_MAX]; - struct vec uri_vec, filename_vec; - const char *list; - bool_t authorized; - - fp = NULL; - authorized = TRUE; - - lock_option(conn->ctx, OPT_PROTECT); - list = conn->ctx->options[OPT_PROTECT]; - while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) { - if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) { - (void) mg_snprintf(conn, fname, sizeof(fname), "%.*s", - filename_vec.len, filename_vec.ptr); - if ((fp = mg_fopen(fname, "r")) == NULL) - cry(conn, "%s: cannot open %s: %s", - __func__, fname, strerror(errno)); - break; - } - } - unlock_option(conn->ctx, OPT_PROTECT); - - if (fp == NULL) - fp = open_auth_file(conn, path); - - if (fp != NULL) { - authorized = authorize(conn, fp); - (void) fclose(fp); - } - - return (authorized); -} - -static void -send_authorization_request(struct mg_connection *conn) -{ - conn->request_info.status_code = 401; - (void) mg_printf(conn, - "HTTP/1.1 401 Unauthorized\r\n" - "WWW-Authenticate: Digest qop=\"auth\", " - "realm=\"%s\", nonce=\"%lu\"\r\n\r\n", - conn->ctx->options[OPT_AUTH_DOMAIN], (unsigned long) time(NULL)); -} - -static bool_t -is_authorized_for_put(struct mg_connection *conn) -{ - FILE *fp; - int ret = FALSE; - - if ((fp = mg_fopen(conn->ctx->options[OPT_AUTH_PUT], "r")) != NULL) { - set_close_on_exec(fileno(fp)); - ret = authorize(conn, fp); - (void) fclose(fp); - } - - return (ret); -} - -int -mg_modify_passwords_file(struct mg_context *ctx, const char *fname, - const char *user, const char *pass) -{ - int found; - char line[512], u[512], d[512], ha1[33], tmp[FILENAME_MAX]; - const char *domain; - FILE *fp, *fp2; - - found = 0; - fp = fp2 = NULL; - domain = ctx->options[OPT_AUTH_DOMAIN]; - - /* Regard empty password as no password - remove user record. */ - if (pass[0] == '\0') - pass = NULL; - - (void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname); - - /* Create the file if does not exist */ - if ((fp = mg_fopen(fname, "a+")) != NULL) - (void) fclose(fp); - - /* Open the given file and temporary file */ - if ((fp = mg_fopen(fname, "r")) == NULL) { - cry(fc(ctx), "Cannot open %s: %s", fname, strerror(errno)); - return (0); - } else if ((fp2 = mg_fopen(tmp, "w+")) == NULL) { - cry(fc(ctx), "Cannot open %s: %s", tmp, strerror(errno)); - return (0); - } - - /* Copy the stuff to temporary file */ - while (fgets(line, sizeof(line), fp) != NULL) { - - if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) - continue; - - if (!strcmp(u, user) && !strcmp(d, domain)) { - found++; - if (pass != NULL) { - mg_md5(ha1, user, ":", domain, ":", pass, NULL); - fprintf(fp2, "%s:%s:%s\n", user, domain, ha1); - } - } else { - (void) fprintf(fp2, "%s", line); - } - } - - /* If new user, just add it */ - if (!found && pass != NULL) { - mg_md5(ha1, user, ":", domain, ":", pass, NULL); - (void) fprintf(fp2, "%s:%s:%s\n", user, domain, ha1); - } - - /* Close files */ - (void) fclose(fp); - (void) fclose(fp2); - - /* Put the temp file in place of real file */ - (void) mg_remove(fname); - (void) mg_rename(tmp, fname); - - return (0); -} - -struct de { - struct mg_connection *conn; - char *file_name; - struct mgstat st; -}; - -/* - * This function is called from send_directory() and prints out - * single directory entry. - */ -static void -print_dir_entry(struct de *de) -{ - char size[64], mod[64]; - - if (de->st.is_directory) { - (void) mg_snprintf(de->conn, - size, sizeof(size), "%s", "[DIRECTORY]"); - } else { - /* - * We use (signed) cast below because MSVC 6 compiler cannot - * convert unsigned __int64 to double. Sigh. - */ - if (de->st.size < 1024) - (void) mg_snprintf(de->conn, size, sizeof(size), - "%lu", (unsigned long) de->st.size); - else if (de->st.size < 1024 * 1024) - (void) mg_snprintf(de->conn, size, sizeof(size), - "%.1fk", (double) de->st.size / 1024.0); - else if (de->st.size < 1024 * 1024 * 1024) - (void) mg_snprintf(de->conn, size, sizeof(size), - "%.1fM", (double) de->st.size / 1048576); - else - (void) mg_snprintf(de->conn, size, sizeof(size), - "%.1fG", (double) de->st.size / 1073741824); - } - (void) strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", - localtime(&de->st.mtime)); - de->conn->num_bytes_sent += mg_printf(de->conn, - "%s%s" - " %s  %s\n", - de->conn->request_info.uri, de->file_name, de->file_name, - de->st.is_directory ? "/" : "", mod, size); -} - -/* - * This function is called from send_directory() and used for - * sorting direcotory entries by size, or name, or modification time. - */ -static int -compare_dir_entries(const void *p1, const void *p2) -{ - const struct de *a = (struct de *) p1, *b = (struct de *) p2; - const char *query_string = a->conn->request_info.query_string; - int cmp_result = 0; - - if (query_string == NULL) - query_string = "na"; - - if (a->st.is_directory && !b->st.is_directory) { - return (-1); /* Always put directories on top */ - } else if (!a->st.is_directory && b->st.is_directory) { - return (1); /* Always put directories on top */ - } else if (*query_string == 'n') { - cmp_result = strcmp(a->file_name, b->file_name); - } else if (*query_string == 's') { - cmp_result = a->st.size == b->st.size ? 0 : - a->st.size > b->st.size ? 1 : -1; - } else if (*query_string == 'd') { - cmp_result = a->st.mtime == b->st.mtime ? 0 : - a->st.mtime > b->st.mtime ? 1 : -1; - } - - return (query_string[1] == 'd' ? -cmp_result : cmp_result); -} - -/* - * Send directory contents. - */ -static void -send_directory(struct mg_connection *conn, const char *dir) -{ - struct dirent *dp; - DIR *dirp; - struct de *entries = NULL; - char path[FILENAME_MAX]; - int i, sort_direction, num_entries = 0, arr_size = 128; - - if ((dirp = opendir(dir)) == NULL) { - send_error(conn, 500, "Cannot open directory", - "Error: opendir(%s): %s", path, strerror(ERRNO)); - return; - } - - (void) mg_printf(conn, "%s", - "HTTP/1.1 200 OK\r\n" - "Connection: close\r\n" - "Content-Type: text/html; charset=utf-8\r\n\r\n"); - - sort_direction = conn->request_info.query_string != NULL && - conn->request_info.query_string[1] == 'd' ? 'a' : 'd'; - - while ((dp = readdir(dirp)) != NULL) { - - /* Do not show current dir and passwords file */ - if (!strcmp(dp->d_name, ".") || - !strcmp(dp->d_name, "..") || - !strcmp(dp->d_name, PASSWORDS_FILE_NAME)) - continue; - - if (entries == NULL || num_entries >= arr_size) { - arr_size *= 2; - entries = (struct de *) realloc(entries, - arr_size * sizeof(entries[0])); - } - - if (entries == NULL) { - send_error(conn, 500, "Cannot open directory", - "%s", "Error: cannot allocate memory"); - return; - } - - (void) mg_snprintf(conn, path, sizeof(path), "%s%c%s", - dir, DIRSEP, dp->d_name); - - (void) mg_stat(path, &entries[num_entries].st); - entries[num_entries].conn = conn; - entries[num_entries].file_name = mg_strdup(dp->d_name); - num_entries++; - } - (void) closedir(dirp); - - conn->num_bytes_sent += mg_printf(conn, - "Index of %s" - "" - "

Index of %s

"
-	    ""
-	    ""
-	    ""
-	    "",
-	    conn->request_info.uri, conn->request_info.uri,
-	    sort_direction, sort_direction, sort_direction);
-
-	/* Print first entry - link to a parent directory */
-	conn->num_bytes_sent += mg_printf(conn,
-	    ""
-	    "\n",
-	    conn->request_info.uri, "..", "Parent directory", "-", "-");
-
-	/* Sort and print directory entries */
-	qsort(entries, num_entries, sizeof(entries[0]), compare_dir_entries);
-	for (i = 0; i < num_entries; i++) {
-		print_dir_entry(&entries[i]);
-		free(entries[i].file_name);
-	}
-	free(entries);
-
-	conn->num_bytes_sent += mg_printf(conn, "%s", "
NameModifiedSize

%s %s  %s
"); - conn->request_info.status_code = 200; -} - -/* - * Send len bytes from the opened file to the client. - */ -static void -send_opened_file_stream(struct mg_connection *conn, FILE *fp, uint64_t len) -{ - char buf[BUFSIZ]; - int to_read, num_read, num_written; - - while (len > 0) { - /* Calculate how much to read from the file in the buffer */ - to_read = sizeof(buf); - if ((uint64_t) to_read > len) - to_read = (int) len; - - /* Read from file, exit the loop on error */ - if ((num_read = fread(buf, 1, to_read, fp)) == 0) - break; - - /* Send read bytes to the client, exit the loop on error */ - if ((num_written = mg_write(conn, buf, num_read)) != num_read) - break; - - /* Both read and were successful, adjust counters */ - conn->num_bytes_sent += num_written; - len -= num_written; - } -} - -/* - * Send regular file contents. - */ -static void -send_file(struct mg_connection *conn, const char *path, struct mgstat *stp) -{ - char date[64], lm[64], etag[64], range[64]; - const char *fmt = "%a, %d %b %Y %H:%M:%S %Z", *msg = "OK", *hdr; - time_t curtime = time(NULL); - uint64_t cl, r1, r2; - struct vec mime_vec; - FILE *fp; - int n; - - get_mime_type(conn->ctx, path, &mime_vec); - cl = stp->size; - conn->request_info.status_code = 200; - range[0] = '\0'; - - if ((fp = mg_fopen(path, "rb")) == NULL) { - send_error(conn, 500, http_500_error, - "fopen(%s): %s", path, strerror(ERRNO)); - return; - } - set_close_on_exec(fileno(fp)); - - /* If Range: header specified, act accordingly */ - r1 = r2 = 0; - hdr = mg_get_header(conn, "Range"); - if (hdr != NULL && (n = sscanf(hdr, - "bytes=%" UINT64_FMT "u-%" UINT64_FMT "u", &r1, &r2)) > 0) { - conn->request_info.status_code = 206; - (void) fseeko(fp, (off_t) r1, SEEK_SET); - cl = n == 2 ? r2 - r1 + 1: cl - r1; - (void) mg_snprintf(conn, range, sizeof(range), - "Content-Range: bytes " - "%" UINT64_FMT "u-%" - UINT64_FMT "u/%" UINT64_FMT "u\r\n", - r1, r1 + cl - 1, stp->size); - msg = "Partial Content"; - } - - /* Prepare Etag, Date, Last-Modified headers */ - (void) strftime(date, sizeof(date), fmt, localtime(&curtime)); - (void) strftime(lm, sizeof(lm), fmt, localtime(&stp->mtime)); - (void) mg_snprintf(conn, etag, sizeof(etag), "%lx.%lx", - (unsigned long) stp->mtime, (unsigned long) stp->size); - - (void) mg_printf(conn, - "HTTP/1.1 %d %s\r\n" - "Date: %s\r\n" - "Last-Modified: %s\r\n" - "Etag: \"%s\"\r\n" - "Content-Type: %.*s\r\n" - "Content-Length: %" UINT64_FMT "u\r\n" - "Connection: close\r\n" - "Accept-Ranges: bytes\r\n" - "%s\r\n", - conn->request_info.status_code, msg, date, lm, etag, - mime_vec.len, mime_vec.ptr, cl, range); - - if (strcmp(conn->request_info.request_method, "HEAD") != 0) - send_opened_file_stream(conn, fp, cl); - (void) fclose(fp); -} - -/* - * Parse HTTP headers from the given buffer, advance buffer to the point - * where parsing stopped. - */ -static void -parse_http_headers(char **buf, struct mg_request_info *ri) -{ - int i; - - for (i = 0; i < (int) ARRAY_SIZE(ri->http_headers); i++) { - ri->http_headers[i].name = skip(buf, ": "); - ri->http_headers[i].value = skip(buf, "\r\n"); - if (ri->http_headers[i].name[0] == '\0') - break; - ri->num_headers = i + 1; - } -} - -static bool_t -is_known_http_method(const char *method) -{ - return (!strcmp(method, "GET") || - !strcmp(method, "POST") || - !strcmp(method, "HEAD") || - !strcmp(method, "PUT") || - !strcmp(method, "DELETE")); -} - -/* - * Parse HTTP request, fill in mg_request_info structure. - */ -static bool_t -parse_http_request(char *buf, struct mg_request_info *ri, const struct usa *usa) -{ - char *http_version; - int n, success_code = FALSE; - - ri->request_method = skip(&buf, " "); - ri->uri = skip(&buf, " "); - http_version = skip(&buf, "\r\n"); - - if (is_known_http_method(ri->request_method) && - ri->uri[0] == '/' && - sscanf(http_version, "HTTP/%d.%d%n", - &ri->http_version_major, &ri->http_version_minor, &n) == 2 && - http_version[n] == '\0') { - parse_http_headers(&buf, ri); - ri->remote_port = ntohs(usa->u.sin.sin_port); - (void) memcpy(&ri->remote_ip, &usa->u.sin.sin_addr.s_addr, 4); - ri->remote_ip = ntohl(ri->remote_ip); - success_code = TRUE; - } - - return (success_code); -} - -/* - * Keep reading the input (either opened file descriptor fd, or socket sock, - * or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the - * buffer (which marks the end of HTTP request). Buffer buf may already - * have some data. The length of the data is stored in nread. - * Upon every read operation, increase nread by the number of bytes read. - */ -static int -read_request(FILE *fp, SOCKET sock, SSL *ssl, char *buf, int bufsiz, int *nread) -{ - int n, request_len; - - request_len = 0; - while (*nread < bufsiz && request_len == 0) { - n = pull(fp, sock, ssl, buf + *nread, bufsiz - *nread); - if (n <= 0) { - break; - } else { - *nread += n; - request_len = get_request_len(buf, (size_t) *nread); - } - } - - return (request_len); -} - -/* - * For given directory path, substitute it to valid index file. - * Return 0 if index file has been found, -1 if not found. - * If the file is found, it's stats is returned in stp. - */ -static bool_t -substitute_index_file(struct mg_connection *conn, - char *path, size_t path_len, struct mgstat *stp) -{ - const char *list; - struct mgstat st; - struct vec filename_vec; - size_t n; - bool_t found; - - n = strlen(path); - - /* - * The 'path' given to us points to the directory. Remove all trailing - * directory separator characters from the end of the path, and - * then append single directory separator character. - */ - while (n > 0 && IS_DIRSEP_CHAR(path[n - 1])) - n--; - path[n] = DIRSEP; - - /* - * Traverse index files list. For each entry, append it to the given - * path and see if the file exists. If it exists, break the loop - */ - lock_option(conn->ctx, OPT_INDEX_FILES); - list = conn->ctx->options[OPT_INDEX_FILES]; - found = FALSE; - - while ((list = next_option(list, &filename_vec, NULL)) != NULL) { - - /* Ignore too long entries that may overflow path buffer */ - if (filename_vec.len > path_len - n) - continue; - - /* Prepare full path to the index file */ - (void) mg_strlcpy(path + n + 1, - filename_vec.ptr, filename_vec.len + 1); - - /* Does it exist ? */ - if (mg_stat(path, &st) == 0) { - /* Yes it does, break the loop */ - *stp = st; - found = TRUE; - break; - } - } - unlock_option(conn->ctx, OPT_INDEX_FILES); - - /* If no index file exists, restore directory path */ - if (found == FALSE) - path[n] = '\0'; - - return (found); -} - -static void -remove_callback(struct mg_context *ctx, - const char *uri_regex, int status_code, bool_t is_auth) -{ - struct callback *cb; - int i; - - for (i = 0; i < ctx->num_callbacks; i++) { - cb = ctx->callbacks + i; - if ((uri_regex != NULL && cb->uri_regex != NULL && - ((is_auth && cb->is_auth) || (!is_auth && !cb->is_auth)) && - !strcmp(uri_regex, cb->uri_regex)) || (uri_regex == NULL && - (cb->status_code == 0 || - cb->status_code == status_code))) { - (void) memmove(cb, cb + 1, - (char *) (ctx->callbacks + ctx->num_callbacks) - - (char *) (cb + 1)); - break; - } - } -} - -static void -add_callback(struct mg_context *ctx, const char *uri_regex, int status_code, - mg_callback_t func, bool_t is_auth, void *user_data) -{ - struct callback *cb; - - pthread_mutex_lock(&ctx->bind_mutex); - if (func == NULL) { - remove_callback(ctx, uri_regex, status_code, is_auth); - } else if (ctx->num_callbacks >= (int) ARRAY_SIZE(ctx->callbacks) - 1) { - cry(fc(ctx), "Too many callbacks! Increase MAX_CALLBACKS."); - } else { - cb = &ctx->callbacks[ctx->num_callbacks]; - cb->uri_regex = uri_regex ? mg_strdup(uri_regex) : NULL; - cb->func = func; - cb->is_auth = is_auth; - cb->status_code = status_code; - cb->user_data = user_data; - ctx->num_callbacks++; - DEBUG_TRACE((DEBUG_MGS_PREFIX "%s: uri %s code %d", - __func__, uri_regex ? uri_regex : "NULL", status_code)); - } - pthread_mutex_unlock(&ctx->bind_mutex); -} - -void -mg_set_uri_callback(struct mg_context *ctx, const char *uri_regex, - mg_callback_t func, void *user_data) -{ - assert(uri_regex != NULL); - add_callback(ctx, uri_regex, -1, func, FALSE, user_data); -} - -void -mg_set_error_callback(struct mg_context *ctx, int error_code, - mg_callback_t func, void *user_data) -{ - assert(error_code >= 0 && error_code < 1000); - add_callback(ctx, NULL, error_code, func, FALSE, user_data); -} - -void -mg_set_auth_callback(struct mg_context *ctx, const char *uri_regex, - mg_callback_t func, void *user_data) -{ - assert(uri_regex != NULL); - add_callback(ctx, uri_regex, -1, func, TRUE, user_data); -} - -/* - * Return True if we should reply 304 Not Modified. - */ -static bool_t -is_not_modified(const struct mg_connection *conn, const struct mgstat *stp) -{ - const char *ims = mg_get_header(conn, "If-Modified-Since"); - return (ims != NULL && stp->mtime < date_to_epoch(ims)); -} - -static bool_t -append_chunk(struct mg_request_info *ri, FILE *fp, const char *buf, int len) -{ - bool_t ret_code = TRUE; - - if (fp == NULL) { - /* TODO: check for NULL here */ - ri->post_data = (char *) realloc(ri->post_data, - ri->post_data_len + len); - (void) memcpy(ri->post_data + ri->post_data_len, buf, len); - ri->post_data_len += len; - } else if (push(fp, INVALID_SOCKET, - NULL, buf, (uint64_t) len) != (uint64_t) len) { - ret_code = FALSE; - } - - return (ret_code); -} - -static bool_t -handle_request_body(struct mg_connection *conn, FILE *fp) -{ - struct mg_request_info *ri = &conn->request_info; - const char *expect, *tmp; - uint64_t content_len; - char buf[BUFSIZ]; - int to_read, nread, already_read; - bool_t success_code = FALSE; - - content_len = get_content_length(conn); - expect = mg_get_header(conn, "Expect"); - - if (content_len == UNKNOWN_CONTENT_LENGTH) { - send_error(conn, 411, "Length Required", ""); - } else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) { - send_error(conn, 417, "Expectation Failed", ""); - } else { - if (expect != NULL) - (void) mg_printf(conn, "HTTP/1.1 100 Continue\r\n\r\n"); - - already_read = ri->post_data_len; - assert(already_read >= 0); - - if (content_len <= (uint64_t) already_read) { - ri->post_data_len = (int) content_len; - /* - * If fp is NULL, this is embedded mode, and we do not - * have to do anything: POST data is already there, - * no need to allocate a buffer and copy it in. - * If fp != NULL, we need to write the data. - */ - success_code = fp == NULL || (push(fp, INVALID_SOCKET, - NULL, ri->post_data, content_len) == content_len) ? - TRUE : FALSE; - } else { - - if (fp == NULL) { - conn->free_post_data = TRUE; - tmp = ri->post_data; - /* +1 in case if already_read == 0 */ - ri->post_data = (char*)malloc(already_read + 1); - (void) memcpy(ri->post_data, tmp, already_read); - } else { - (void) push(fp, INVALID_SOCKET, NULL, - ri->post_data, (uint64_t) already_read); - } - - content_len -= already_read; - - while (content_len > 0) { - to_read = sizeof(buf); - if ((uint64_t) to_read > content_len) - to_read = (int) content_len; - nread = pull(NULL, conn->client.sock, - conn->ssl, buf, to_read); - if (nread <= 0) - break; - if (!append_chunk(ri, fp, buf, nread)) - break; - content_len -= nread; - } - success_code = content_len == 0 ? TRUE : FALSE; - } - - /* Each error code path in this function must send an error */ - if (success_code != TRUE) - send_error(conn, 577, http_500_error, - "%s", "Error handling body data"); - } - - return (success_code); -} - -#if !defined(NO_CGI) - -/* - * This structure helps to create an environment for the spawned CGI program. - * Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings, - * last element must be NULL. - * However, on Windows there is a requirement that all these VARIABLE=VALUE\0 - * strings must reside in a contiguous buffer. The end of the buffer is - * marked by two '\0' characters. - * We satisfy both worlds: we create an envp array (which is vars), all - * entries are actually pointers inside buf. - */ -struct cgi_env_block { - struct mg_connection *conn; - char buf[CGI_ENVIRONMENT_SIZE]; /* Environment buffer */ - int len; /* Space taken */ - char *vars[MAX_CGI_ENVIR_VARS]; /* char **envp */ - int nvars; /* Number of variables */ -}; - -/* - * Append VARIABLE=VALUE\0 string to the buffer, and add a respective - * pointer into the vars array. - */ -static char * -addenv(struct cgi_env_block *block, const char *fmt, ...) -{ - int n, space; - char *added; - va_list ap; - - /* Calculate how much space is left in the buffer */ - space = sizeof(block->buf) - block->len - 2; - assert(space >= 0); - - /* Make a pointer to the free space int the buffer */ - added = block->buf + block->len; - - /* Copy VARIABLE=VALUE\0 string into the free space */ - va_start(ap, fmt); - n = mg_vsnprintf(block->conn, added, (size_t) space, fmt, ap); - va_end(ap); - - /* Make sure we do not overflow buffer and the envp array */ - if (n > 0 && n < space && - block->nvars < (int) ARRAY_SIZE(block->vars) - 2) { - /* Append a pointer to the added string into the envp array */ - block->vars[block->nvars++] = block->buf + block->len; - /* Bump up used length counter. Include \0 terminator */ - block->len += n + 1; - } - - return (added); -} - -static void -prepare_cgi_environment(struct mg_connection *conn, const char *prog, - struct cgi_env_block *blk) -{ - const char *s, *script_filename, *root; - struct vec var_vec; - char *p; - int i; - - blk->len = blk->nvars = 0; - blk->conn = conn; - - /* SCRIPT_FILENAME */ - script_filename = prog; - if ((s = strrchr(prog, '/')) != NULL) - script_filename = s + 1; - - lock_option(conn->ctx, OPT_ROOT); - root = conn->ctx->options[OPT_ROOT]; - addenv(blk, "SERVER_NAME=%s", conn->ctx->options[OPT_AUTH_DOMAIN]); - unlock_option(conn->ctx, OPT_ROOT); - - /* Prepare the environment block */ - addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1"); - addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1"); - addenv(blk, "%s", "REDIRECT_STATUS=200"); /* PHP */ - addenv(blk, "SERVER_PORT=%d", ntohs(conn->client.lsa.u.sin.sin_port)); - addenv(blk, "SERVER_ROOT=%s", root); - addenv(blk, "DOCUMENT_ROOT=%s", root); - addenv(blk, "REQUEST_METHOD=%s", conn->request_info.request_method); - addenv(blk, "REMOTE_ADDR=%s", - inet_ntoa(conn->client.rsa.u.sin.sin_addr)); - addenv(blk, "REMOTE_PORT=%d", conn->request_info.remote_port); - addenv(blk, "REQUEST_URI=%s", conn->request_info.uri); - addenv(blk, "SCRIPT_NAME=%s", prog + strlen(root)); - addenv(blk, "SCRIPT_FILENAME=%s", script_filename); /* PHP */ - addenv(blk, "PATH_TRANSLATED=%s", prog); - addenv(blk, "HTTPS=%s", conn->ssl == NULL ? "off" : "on"); - - if ((s = mg_get_header(conn, "Content-Type")) != NULL) - addenv(blk, "CONTENT_TYPE=%s", s); - - if (conn->request_info.query_string != NULL) - addenv(blk, "QUERY_STRING=%s", conn->request_info.query_string); - - if ((s = mg_get_header(conn, "Content-Length")) != NULL) - addenv(blk, "CONTENT_LENGTH=%s", s); - - if ((s = getenv("PATH")) != NULL) - addenv(blk, "PATH=%s", s); - -#if defined(_WIN32) - if ((s = getenv("COMSPEC")) != NULL) - addenv(blk, "COMSPEC=%s", s); - if ((s = getenv("SYSTEMROOT")) != NULL) - addenv(blk, "SYSTEMROOT=%s", s); -#else - if ((s = getenv("LD_LIBRARY_PATH")) != NULL) - addenv(blk, "LD_LIBRARY_PATH=%s", s); -#endif /* _WIN32 */ - - if ((s = getenv("PERLLIB")) != NULL) - addenv(blk, "PERLLIB=%s", s); - - if (conn->request_info.remote_user != NULL) { - addenv(blk, "REMOTE_USER=%s", conn->request_info.remote_user); - addenv(blk, "%s", "AUTH_TYPE=Digest"); - } - - /* Add all headers as HTTP_* variables */ - for (i = 0; i < conn->request_info.num_headers; i++) { - p = addenv(blk, "HTTP_%s=%s", - conn->request_info.http_headers[i].name, - conn->request_info.http_headers[i].value); - - /* Convert variable name into uppercase, and change - to _ */ - for (; *p != '=' && *p != '\0'; p++) { - if (*p == '-') - *p = '_'; - *p = (char) toupper(* (unsigned char *) p); - } - } - - /* Add user-specified variables */ - lock_option(conn->ctx, OPT_CGI_ENV); - s = conn->ctx->options[OPT_CGI_ENV]; - while ((s = next_option(s, &var_vec, NULL)) != NULL) - addenv(blk, "%.*s", var_vec.len, var_vec.ptr); - unlock_option(conn->ctx, OPT_CGI_ENV); - - blk->vars[blk->nvars++] = NULL; - blk->buf[blk->len++] = '\0'; - - assert(blk->nvars < (int) ARRAY_SIZE(blk->vars)); - assert(blk->len > 0); - assert(blk->len < (int) sizeof(blk->buf)); -} - -static void -send_cgi(struct mg_connection *conn, const char *prog) -{ - int headers_len, data_len, i, n; - const char *status; - char buf[MAX_REQUEST_SIZE], *pbuf; - struct mg_request_info ri; - struct cgi_env_block blk; - char dir[FILENAME_MAX], *p; - int fd_stdin[2], fd_stdout[2]; - FILE *in, *out; - pid_t pid; - - prepare_cgi_environment(conn, prog, &blk); - - /* CGI must be executed in its own directory */ - (void) mg_snprintf(conn, dir, sizeof(dir), "%s", prog); - if ((p = strrchr(dir, DIRSEP)) != NULL) - *p++ = '\0'; - - pid = (pid_t) -1; - fd_stdin[0] = fd_stdin[1] = fd_stdout[0] = fd_stdout[1] = -1; - in = out = NULL; - - if (pipe(fd_stdin) != 0 || pipe(fd_stdout) != 0) { - send_error(conn, 500, http_500_error, - "Cannot create CGI pipe: %s", strerror(ERRNO)); - goto done; - } else if ((pid = spawn_process(conn, p, blk.buf, blk.vars, - fd_stdin[0], fd_stdout[1], dir)) == (pid_t) -1) { - goto done; - } else if ((in = fdopen(fd_stdin[1], "wb")) == NULL || - (out = fdopen(fd_stdout[0], "rb")) == NULL) { - send_error(conn, 500, http_500_error, - "fopen: %s", strerror(ERRNO)); - goto done; - } - - setbuf(in, NULL); - setbuf(out, NULL); - - /* - * spawn_process() must close those! - * If we don't mark them as closed, close() attempt before - * return from this function throws an exception on Windows. - * Windows does not like when closed descriptor is closed again. - */ - fd_stdin[0] = fd_stdout[1] = -1; - - /* Send POST data to the CGI process if needed */ - if (!strcmp(conn->request_info.request_method, "POST") && - !handle_request_body(conn, in)) { - goto done; - } - - /* - * Now read CGI reply into a buffer. We need to set correct - * status code, thus we need to see all HTTP headers first. - * Do not send anything back to client, until we buffer in all - * HTTP headers. - */ - data_len = 0; - headers_len = read_request(out, INVALID_SOCKET, NULL, - buf, sizeof(buf), &data_len); - if (headers_len <= 0) { - send_error(conn, 500, http_500_error, - "CGI program sent malformed HTTP headers: [%.*s]", - data_len, buf); - goto done; - } - pbuf = buf; - buf[headers_len - 1] = '\0'; - parse_http_headers(&pbuf, &ri); - - /* Make up and send the status line */ - status = get_header(&ri, "Status"); - conn->request_info.status_code = status == NULL ? 200 : atoi(status); - (void) mg_printf(conn, "HTTP/1.1 %d OK\r\n", - conn->request_info.status_code); - - /* Send headers */ - for (i = 0; i < ri.num_headers; i++) - (void) mg_printf(conn, "%s: %s\r\n", - ri.http_headers[i].name, - ri.http_headers[i].value); - (void) mg_write(conn, "\r\n", 2); - - /* Send chunk of data that may be read after the headers */ - conn->num_bytes_sent += mg_write(conn, - buf + headers_len, data_len - headers_len); - - /* - * Read the rest of CGI output and send to the client. If read from - * CGI returns 0, CGI has finished output. If it returns < 0, - * some read error occured (CGI process terminated unexpectedly?) - * If write to the client fails, the means client has disconnected - * unexpectedly. - * In all such cases, stop data exchange and do cleanup. - */ - do { - n = pull(out, INVALID_SOCKET, NULL, buf, sizeof(buf)); - if (n > 0) - n = mg_write(conn, buf, n); - if (n > 0) - conn->num_bytes_sent += n; - } while (n > 0); - -done: - if (pid != (pid_t) -1) - kill(pid, SIGTERM); - if (fd_stdin[0] != -1) - (void) close(fd_stdin[0]); - if (fd_stdout[1] != -1) - (void) close(fd_stdout[1]); - - if (in != NULL) - (void) fclose(in); - else if (fd_stdin[1] != -1) - (void) close(fd_stdin[1]); - - if (out != NULL) - (void) fclose(out); - else if (fd_stdout[0] != -1) - (void) close(fd_stdout[0]); -} -#endif /* !NO_CGI */ - -/* - * For a given PUT path, create all intermediate subdirectories - * for given path. Return 0 if the path itself is a directory, - * or -1 on error, 1 if OK. - */ -static int -put_dir(const char *path) -{ - char buf[FILENAME_MAX]; - const char *s, *p; - struct mgstat st; - size_t len; - - for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) { - len = p - path; - assert(len < sizeof(buf)); - (void) memcpy(buf, path, len); - buf[len] = '\0'; - - /* Try to create intermediate directory */ - if (mg_stat(buf, &st) == -1 && mg_mkdir(buf, 0755) != 0) - return (-1); - - /* Is path itself a directory ? */ - if (p[1] == '\0') - return (0); - } - - return (1); -} - -static void -put_file(struct mg_connection *conn, const char *path) -{ - struct mgstat st; - FILE *fp; - int rc; - - conn->request_info.status_code = mg_stat(path, &st) == 0 ? 200 : 201; - - if (mg_get_header(conn, "Range")) { - send_error(conn, 501, "Not Implemented", - "%s", "Range support for PUT requests is not implemented"); - } else if ((rc = put_dir(path)) == 0) { - (void) mg_printf(conn, "HTTP/1.1 %d OK\r\n\r\n", - conn->request_info.status_code); - } else if (rc == -1) { - send_error(conn, 500, http_500_error, - "put_dir(%s): %s", path, strerror(ERRNO)); - } else if ((fp = mg_fopen(path, "wb+")) == NULL) { - send_error(conn, 500, http_500_error, - "fopen(%s): %s", path, strerror(ERRNO)); - } else { - set_close_on_exec(fileno(fp)); - if (handle_request_body(conn, fp)) - (void) mg_printf(conn, "HTTP/1.1 %d OK\r\n\r\n", - conn->request_info.status_code); - (void) fclose(fp); - } -} - -#if !defined(NO_SSI) -static void send_ssi_file(struct mg_connection *, const char *, FILE *, int); - -static void -do_ssi_include(struct mg_connection *conn, const char *ssi, char *tag, - int include_level) -{ - char file_name[BUFSIZ], path[FILENAME_MAX], *p; - FILE *fp; - - /* - * sscanf() is safe here, since send_ssi_file() also uses buffer - * of size BUFSIZ to get the tag. So strlen(tag) is always < BUFSIZ. - */ - if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) { - /* File name is relative to the webserver root */ - lock_option(conn->ctx, OPT_ROOT); - (void) mg_snprintf(conn, path, sizeof(path), "%s%c%s", - conn->ctx->options[OPT_ROOT], DIRSEP, file_name); - unlock_option(conn->ctx, OPT_ROOT); - } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1) { - /* - * File name is relative to the webserver working directory - * or it is absolute system path - */ - (void) mg_snprintf(conn, path, sizeof(path), "%s", file_name); - } else if (sscanf(tag, " \"%[^\"]\"", file_name) == 1) { - /* File name is relative to the currect document */ - (void) mg_snprintf(conn, path, sizeof(path), "%s", ssi); - if ((p = strrchr(path, DIRSEP)) != NULL) - p[1] = '\0'; - (void) mg_snprintf(conn, path + strlen(path), - sizeof(path) - strlen(path), "%s", file_name); - } else { - cry(conn, "Bad SSI #include: [%s]", tag); - return; - } - - if ((fp = mg_fopen(path, "rb")) == NULL) { - cry(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s", - tag, path, strerror(ERRNO)); - } else { - set_close_on_exec(fileno(fp)); - if (match_extension(path, - conn->ctx->options[OPT_SSI_EXTENSIONS])) { - send_ssi_file(conn, path, fp, include_level + 1); - } else { - send_opened_file_stream(conn, fp, - UNKNOWN_CONTENT_LENGTH); - } - (void) fclose(fp); - } -} - -static void -do_ssi_exec(struct mg_connection *conn, char *tag) -{ - char cmd[BUFSIZ]; - FILE *fp; - - if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) { - cry(conn, "Bad SSI #exec: [%s]", tag); - } else if ((fp = popen(cmd, "r")) == NULL) { - cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO)); - } else { - send_opened_file_stream(conn, fp, UNKNOWN_CONTENT_LENGTH); - (void) pclose(fp); - } -} - -static void -send_ssi_file(struct mg_connection *conn, const char *path, FILE *fp, - int include_level) -{ - char buf[BUFSIZ]; - int ch, len, in_ssi_tag; - - if (include_level > 10) { - cry(conn, "SSI #include level is too deep (%s)", path); - return; - } - - in_ssi_tag = FALSE; - len = 0; - - while ((ch = fgetc(fp)) != EOF) { - if (in_ssi_tag && ch == '>') { - in_ssi_tag = FALSE; - buf[len++] = ch & 0xff; - buf[len] = '\0'; - assert(len <= (int) sizeof(buf)); - if (len < 6 || memcmp(buf, "