diff options
author | marco <marco@3ad0048d-3df7-0310-abae-a5850022a9f2> | 2016-05-25 19:56:31 +0000 |
---|---|---|
committer | marco <marco@3ad0048d-3df7-0310-abae-a5850022a9f2> | 2016-05-25 19:56:31 +0000 |
commit | 24f58bedad8a59d94912ab64df5212d289c352ee (patch) | |
tree | ef44a3026a3f0cf5564bff822c77f8f9049fff01 | |
parent | bd3edbb5a969eead8f1d54e8095140c6f58615b8 (diff) | |
download | fpc-24f58bedad8a59d94912ab64df5212d289c352ee.tar.gz |
# revisions: 32067
git-svn-id: http://svn.freepascal.org/svn/fpc/branches/fixes_3_0@33795 3ad0048d-3df7-0310-abae-a5850022a9f2
32 files changed, 8068 insertions, 0 deletions
diff --git a/packages/fpmake_add.inc b/packages/fpmake_add.inc index 8e3260357f..e8c6406207 100644 --- a/packages/fpmake_add.inc +++ b/packages/fpmake_add.inc @@ -60,6 +60,7 @@ add_ldap(ADirectory+IncludeTrailingPathDelimiter('ldap')); add_libc(ADirectory+IncludeTrailingPathDelimiter('libc')); add_libcurl(ADirectory+IncludeTrailingPathDelimiter('libcurl')); + add_libmicrohttpd(ADirectory+IncludeTrailingPathDelimiter('libmicrohttpd')); add_libgbafpc(ADirectory+IncludeTrailingPathDelimiter('libgbafpc')); add_libgd(ADirectory+IncludeTrailingPathDelimiter('libgd')); add_libndsfpc(ADirectory+IncludeTrailingPathDelimiter('libndsfpc')); diff --git a/packages/fpmake_proc.inc b/packages/fpmake_proc.inc index 89a699e142..88f34aff2b 100644 --- a/packages/fpmake_proc.inc +++ b/packages/fpmake_proc.inc @@ -403,6 +403,11 @@ begin with Installer do {$include libvlc/fpmake.pp} end; +procedure add_libmicrohttpd(const ADirectory: string); +begin + with Installer do +{$include libmicrohttpd/fpmake.pp} +end; procedure add_libxml(const ADirectory: string); begin diff --git a/packages/libmicrohttpd/Makefile b/packages/libmicrohttpd/Makefile new file mode 100644 index 0000000000..19f55dc6b0 --- /dev/null +++ b/packages/libmicrohttpd/Makefile @@ -0,0 +1,1873 @@ +# +# Don't edit, this file is generated by FPCMake Version 2.0.0 [2015-09-05 rev 31523] +# +default: all +MAKEFILETARGETS=i386-linux i386-go32v2 i386-win32 i386-os2 i386-freebsd i386-beos i386-haiku i386-netbsd i386-solaris i386-qnx i386-netware i386-openbsd i386-wdosx i386-darwin i386-emx i386-watcom i386-netwlibc i386-wince i386-embedded i386-symbian i386-nativent i386-iphonesim i386-android i386-aros m68k-linux m68k-freebsd m68k-netbsd m68k-amiga m68k-atari m68k-openbsd m68k-palmos m68k-embedded powerpc-linux powerpc-netbsd powerpc-amiga powerpc-macos powerpc-darwin powerpc-morphos powerpc-embedded powerpc-wii powerpc-aix sparc-linux sparc-netbsd sparc-solaris sparc-embedded x86_64-linux x86_64-freebsd x86_64-netbsd x86_64-solaris x86_64-openbsd x86_64-darwin x86_64-win64 x86_64-embedded x86_64-iphonesim x86_64-aros x86_64-dragonfly arm-linux arm-palmos arm-darwin arm-wince arm-gba arm-nds arm-embedded arm-symbian arm-android powerpc64-linux powerpc64-darwin powerpc64-embedded powerpc64-aix avr-embedded armeb-linux armeb-embedded mips-linux mipsel-linux mipsel-embedded mipsel-android jvm-java jvm-android i8086-msdos i8086-win16 aarch64-linux aarch64-darwin +BSDs = freebsd netbsd openbsd darwin dragonfly +UNIXs = linux $(BSDs) solaris qnx haiku aix +LIMIT83fs = go32v2 os2 emx watcom msdos win16 +OSNeedsComspecToRunBatch = go32v2 watcom +FORCE: +.PHONY: FORCE +override PATH:=$(patsubst %/,%,$(subst \,/,$(PATH))) +ifneq ($(findstring darwin,$(OSTYPE)),) +inUnix=1 #darwin +SEARCHPATH:=$(filter-out .,$(subst :, ,$(PATH))) +else +ifeq ($(findstring ;,$(PATH)),) +inUnix=1 +SEARCHPATH:=$(filter-out .,$(subst :, ,$(PATH))) +else +SEARCHPATH:=$(subst ;, ,$(PATH)) +endif +endif +SEARCHPATH+=$(patsubst %/,%,$(subst \,/,$(dir $(MAKE)))) +PWD:=$(strip $(wildcard $(addsuffix /pwd.exe,$(SEARCHPATH)))) +ifeq ($(PWD),) +PWD:=$(strip $(wildcard $(addsuffix /pwd,$(SEARCHPATH)))) +ifeq ($(PWD),) +$(error You need the GNU utils package to use this Makefile) +else +PWD:=$(firstword $(PWD)) +SRCEXEEXT= +endif +else +PWD:=$(firstword $(PWD)) +SRCEXEEXT=.exe +endif +ifndef inUnix +ifeq ($(OS),Windows_NT) +inWinNT=1 +else +ifdef OS2_SHELL +inOS2=1 +endif +endif +else +ifneq ($(findstring cygdrive,$(PATH)),) +inCygWin=1 +endif +endif +ifdef inUnix +SRCBATCHEXT=.sh +else +ifdef inOS2 +SRCBATCHEXT=.cmd +else +SRCBATCHEXT=.bat +endif +endif +ifdef COMSPEC +ifneq ($(findstring $(OS_SOURCE),$(OSNeedsComspecToRunBatch)),) +ifndef RUNBATCH +RUNBATCH=$(COMSPEC) /C +endif +endif +endif +ifdef inUnix +PATHSEP=/ +else +PATHSEP:=$(subst /,\,/) +ifdef inCygWin +PATHSEP=/ +endif +endif +ifdef PWD +BASEDIR:=$(subst \,/,$(shell $(PWD))) +ifdef inCygWin +ifneq ($(findstring /cygdrive/,$(BASEDIR)),) +BASENODIR:=$(patsubst /cygdrive%,%,$(BASEDIR)) +BASEDRIVE:=$(firstword $(subst /, ,$(BASENODIR))) +BASEDIR:=$(subst /cygdrive/$(BASEDRIVE)/,$(BASEDRIVE):/,$(BASEDIR)) +endif +endif +else +BASEDIR=. +endif +ifdef inOS2 +ifndef ECHO +ECHO:=$(strip $(wildcard $(addsuffix /gecho$(SRCEXEEXT),$(SEARCHPATH)))) +ifeq ($(ECHO),) +ECHO:=$(strip $(wildcard $(addsuffix /echo$(SRCEXEEXT),$(SEARCHPATH)))) +ifeq ($(ECHO),) +ECHO=echo +else +ECHO:=$(firstword $(ECHO)) +endif +else +ECHO:=$(firstword $(ECHO)) +endif +endif +export ECHO +endif +override DEFAULT_FPCDIR=../.. +ifndef FPC +ifdef PP +FPC=$(PP) +endif +endif +ifndef FPC +FPCPROG:=$(strip $(wildcard $(addsuffix /fpc$(SRCEXEEXT),$(SEARCHPATH)))) +ifneq ($(FPCPROG),) +FPCPROG:=$(firstword $(FPCPROG)) +ifneq ($(CPU_TARGET),) +FPC:=$(shell $(FPCPROG) -P$(CPU_TARGET) -PB) +else +FPC:=$(shell $(FPCPROG) -PB) +endif +ifneq ($(findstring Error,$(FPC)),) +override FPC=$(firstword $(strip $(wildcard $(addsuffix /ppc386$(SRCEXEEXT),$(SEARCHPATH))))) +else +ifeq ($(strip $(wildcard $(FPC))),) +FPC:=$(firstword $(FPCPROG)) +endif +endif +else +override FPC=$(firstword $(strip $(wildcard $(addsuffix /ppc386$(SRCEXEEXT),$(SEARCHPATH))))) +endif +endif +override FPC:=$(subst $(SRCEXEEXT),,$(FPC)) +override FPC:=$(subst \,/,$(FPC))$(SRCEXEEXT) +FOUNDFPC:=$(strip $(wildcard $(FPC))) +ifeq ($(FOUNDFPC),) +FOUNDFPC=$(strip $(wildcard $(addsuffix /$(FPC),$(SEARCHPATH)))) +ifeq ($(FOUNDFPC),) +$(error Compiler $(FPC) not found) +endif +endif +ifndef FPC_COMPILERINFO +FPC_COMPILERINFO:=$(shell $(FPC) -iVSPTPSOTO) +endif +ifndef FPC_VERSION +FPC_VERSION:=$(word 1,$(FPC_COMPILERINFO)) +endif +export FPC FPC_VERSION FPC_COMPILERINFO +unexport CHECKDEPEND ALLDEPENDENCIES +ifndef CPU_TARGET +ifdef CPU_TARGET_DEFAULT +CPU_TARGET=$(CPU_TARGET_DEFAULT) +endif +endif +ifndef OS_TARGET +ifdef OS_TARGET_DEFAULT +OS_TARGET=$(OS_TARGET_DEFAULT) +endif +endif +ifndef CPU_SOURCE +CPU_SOURCE:=$(word 2,$(FPC_COMPILERINFO)) +endif +ifndef CPU_TARGET +CPU_TARGET:=$(word 3,$(FPC_COMPILERINFO)) +endif +ifndef OS_SOURCE +OS_SOURCE:=$(word 4,$(FPC_COMPILERINFO)) +endif +ifndef OS_TARGET +OS_TARGET:=$(word 5,$(FPC_COMPILERINFO)) +endif +FULL_TARGET=$(CPU_TARGET)-$(OS_TARGET) +FULL_SOURCE=$(CPU_SOURCE)-$(OS_SOURCE) +ifeq ($(CPU_TARGET),armeb) +ARCH=arm +override FPCOPT+=-Cb +else +ifeq ($(CPU_TARGET),armel) +ARCH=arm +override FPCOPT+=-CaEABI +else +ARCH=$(CPU_TARGET) +endif +endif +ifeq ($(FULL_TARGET),arm-embedded) +ifeq ($(SUBARCH),) +$(error When compiling for arm-embedded, a sub-architecture (e.g. SUBARCH=armv4t or SUBARCH=armv7m) must be defined) +endif +override FPCOPT+=-Cp$(SUBARCH) +endif +ifeq ($(FULL_TARGET),avr-embedded) +ifeq ($(SUBARCH),) +$(error When compiling for avr-embedded, a sub-architecture (e.g. SUBARCH=avr25 or SUBARCH=avr35) must be defined) +endif +override FPCOPT+=-Cp$(SUBARCH) +endif +ifeq ($(FULL_TARGET),mipsel-embedded) +ifeq ($(SUBARCH),) +$(error When compiling for mipsel-embedded, a sub-architecture (e.g. SUBARCH=pic32mx) must be defined) +endif +override FPCOPT+=-Cp$(SUBARCH) +endif +ifneq ($(findstring $(OS_SOURCE),$(LIMIT83fs)),) +TARGETSUFFIX=$(OS_TARGET) +SOURCESUFFIX=$(OS_SOURCE) +else +ifneq ($(findstring $(OS_TARGET),$(LIMIT83fs)),) +TARGETSUFFIX=$(OS_TARGET) +else +TARGETSUFFIX=$(FULL_TARGET) +endif +SOURCESUFFIX=$(FULL_SOURCE) +endif +ifneq ($(FULL_TARGET),$(FULL_SOURCE)) +CROSSCOMPILE=1 +endif +ifeq ($(findstring makefile,$(MAKECMDGOALS)),) +ifeq ($(findstring $(FULL_TARGET),$(MAKEFILETARGETS)),) +$(error The Makefile doesn't support target $(FULL_TARGET), please run fpcmake first) +endif +endif +ifneq ($(findstring $(OS_TARGET),$(BSDs)),) +BSDhier=1 +endif +ifeq ($(OS_TARGET),linux) +linuxHier=1 +endif +ifndef CROSSCOMPILE +BUILDFULLNATIVE=1 +export BUILDFULLNATIVE +endif +ifdef BUILDFULLNATIVE +BUILDNATIVE=1 +export BUILDNATIVE +endif +export OS_TARGET OS_SOURCE ARCH CPU_TARGET CPU_SOURCE FULL_TARGET FULL_SOURCE TARGETSUFFIX SOURCESUFFIX CROSSCOMPILE +ifdef FPCDIR +override FPCDIR:=$(subst \,/,$(FPCDIR)) +ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl units)),) +override FPCDIR=wrong +endif +else +override FPCDIR=wrong +endif +ifdef DEFAULT_FPCDIR +ifeq ($(FPCDIR),wrong) +override FPCDIR:=$(subst \,/,$(DEFAULT_FPCDIR)) +ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl units)),) +override FPCDIR=wrong +endif +endif +endif +ifeq ($(FPCDIR),wrong) +ifdef inUnix +override FPCDIR=/usr/local/lib/fpc/$(FPC_VERSION) +ifeq ($(wildcard $(FPCDIR)/units),) +override FPCDIR=/usr/lib/fpc/$(FPC_VERSION) +endif +else +override FPCDIR:=$(subst /$(FPC),,$(firstword $(strip $(wildcard $(addsuffix /$(FPC),$(SEARCHPATH)))))) +override FPCDIR:=$(FPCDIR)/.. +ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl units)),) +override FPCDIR:=$(FPCDIR)/.. +ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl units)),) +override FPCDIR:=$(BASEDIR) +ifeq ($(wildcard $(addprefix $(FPCDIR)/,rtl units)),) +override FPCDIR=c:/pp +endif +endif +endif +endif +endif +ifndef CROSSBINDIR +CROSSBINDIR:=$(wildcard $(FPCDIR)/bin/$(TARGETSUFFIX)) +endif +ifneq ($(findstring $(OS_TARGET),darwin iphonesim),) +ifeq ($(OS_SOURCE),darwin) +DARWIN2DARWIN=1 +endif +endif +ifndef BINUTILSPREFIX +ifndef CROSSBINDIR +ifdef CROSSCOMPILE +ifneq ($(OS_TARGET),msdos) +ifndef DARWIN2DARWIN +ifneq ($(CPU_TARGET),jvm) +BINUTILSPREFIX=$(CPU_TARGET)-$(OS_TARGET)- +ifeq ($(OS_TARGET),android) +ifeq ($(CPU_TARGET),arm) +BINUTILSPREFIX=arm-linux-androideabi- +else +ifeq ($(CPU_TARGET),i386) +BINUTILSPREFIX=i686-linux-android- +else +ifeq ($(CPU_TARGET),mipsel) +BINUTILSPREFIX=mipsel-linux-android- +endif +endif +endif +endif +endif +endif +else +BINUTILSPREFIX=$(OS_TARGET)- +endif +endif +endif +endif +UNITSDIR:=$(wildcard $(FPCDIR)/units/$(TARGETSUFFIX)) +ifeq ($(UNITSDIR),) +UNITSDIR:=$(wildcard $(FPCDIR)/units/$(OS_TARGET)) +endif +PACKAGESDIR:=$(wildcard $(FPCDIR) $(FPCDIR)/packages $(FPCDIR)/packages/base $(FPCDIR)/packages/extra) +ifndef FPCFPMAKE +ifdef CROSSCOMPILE +ifeq ($(strip $(wildcard $(addsuffix /compiler/ppc$(SRCEXEEXT),$(FPCDIR)))),) +FPCPROG:=$(strip $(wildcard $(addsuffix /fpc$(SRCEXEEXT),$(SEARCHPATH)))) +ifneq ($(FPCPROG),) +FPCPROG:=$(firstword $(FPCPROG)) +FPCFPMAKE:=$(shell $(FPCPROG) -PB) +ifeq ($(strip $(wildcard $(FPCFPMAKE))),) +FPCFPMAKE:=$(firstword $(FPCPROG)) +endif +else +override FPCFPMAKE=$(firstword $(strip $(wildcard $(addsuffix /ppc386$(SRCEXEEXT),$(SEARCHPATH))))) +endif +else +FPCFPMAKE=$(strip $(wildcard $(addsuffix /compiler/ppc$(SRCEXEEXT),$(FPCDIR)))) +FPMAKE_SKIP_CONFIG=-n +export FPCFPMAKE +export FPMAKE_SKIP_CONFIG +endif +else +FPMAKE_SKIP_CONFIG=-n +FPCFPMAKE=$(FPC) +endif +endif +override PACKAGE_NAME=libmicrohttpd +override PACKAGE_VERSION=3.1.1 +FPMAKE_BIN_CLEAN=$(wildcard ./fpmake$(SRCEXEEXT)) +ifdef OS_TARGET +FPC_TARGETOPT+=--os=$(OS_TARGET) +endif +ifdef CPU_TARGET +FPC_TARGETOPT+=--cpu=$(CPU_TARGET) +endif +LOCALFPMAKE=./fpmake$(SRCEXEEXT) +override INSTALL_FPCPACKAGE=y +ifdef REQUIRE_UNITSDIR +override UNITSDIR+=$(REQUIRE_UNITSDIR) +endif +ifdef REQUIRE_PACKAGESDIR +override PACKAGESDIR+=$(REQUIRE_PACKAGESDIR) +endif +ifdef ZIPINSTALL +ifneq ($(findstring $(OS_TARGET),$(UNIXs)),) +UNIXHier=1 +endif +else +ifneq ($(findstring $(OS_SOURCE),$(UNIXs)),) +UNIXHier=1 +endif +endif +ifndef INSTALL_PREFIX +ifdef PREFIX +INSTALL_PREFIX=$(PREFIX) +endif +endif +ifndef INSTALL_PREFIX +ifdef UNIXHier +INSTALL_PREFIX=/usr/local +else +ifdef INSTALL_FPCPACKAGE +INSTALL_BASEDIR:=/pp +else +INSTALL_BASEDIR:=/$(PACKAGE_NAME) +endif +endif +endif +export INSTALL_PREFIX +ifdef INSTALL_FPCSUBDIR +export INSTALL_FPCSUBDIR +endif +ifndef DIST_DESTDIR +DIST_DESTDIR:=$(BASEDIR) +endif +export DIST_DESTDIR +ifndef COMPILER_UNITTARGETDIR +ifdef PACKAGEDIR_MAIN +COMPILER_UNITTARGETDIR=$(PACKAGEDIR_MAIN)/units/$(TARGETSUFFIX) +else +COMPILER_UNITTARGETDIR=units/$(TARGETSUFFIX) +endif +endif +ifndef COMPILER_TARGETDIR +COMPILER_TARGETDIR=. +endif +ifndef INSTALL_BASEDIR +ifdef UNIXHier +ifdef INSTALL_FPCPACKAGE +INSTALL_BASEDIR:=$(INSTALL_PREFIX)/lib/fpc/$(FPC_VERSION) +else +INSTALL_BASEDIR:=$(INSTALL_PREFIX)/lib/$(PACKAGE_NAME) +endif +else +INSTALL_BASEDIR:=$(INSTALL_PREFIX) +endif +endif +ifndef INSTALL_BINDIR +ifdef UNIXHier +INSTALL_BINDIR:=$(INSTALL_PREFIX)/bin +else +INSTALL_BINDIR:=$(INSTALL_BASEDIR)/bin +ifdef INSTALL_FPCPACKAGE +ifdef CROSSCOMPILE +ifdef CROSSINSTALL +INSTALL_BINDIR:=$(INSTALL_BINDIR)/$(SOURCESUFFIX) +else +INSTALL_BINDIR:=$(INSTALL_BINDIR)/$(TARGETSUFFIX) +endif +else +INSTALL_BINDIR:=$(INSTALL_BINDIR)/$(TARGETSUFFIX) +endif +endif +endif +endif +ifndef INSTALL_UNITDIR +INSTALL_UNITDIR:=$(INSTALL_BASEDIR)/units/$(TARGETSUFFIX) +ifdef INSTALL_FPCPACKAGE +ifdef PACKAGE_NAME +INSTALL_UNITDIR:=$(INSTALL_UNITDIR)/$(PACKAGE_NAME) +endif +endif +endif +ifndef INSTALL_LIBDIR +ifdef UNIXHier +INSTALL_LIBDIR:=$(INSTALL_PREFIX)/lib +else +INSTALL_LIBDIR:=$(INSTALL_UNITDIR) +endif +endif +ifndef INSTALL_SOURCEDIR +ifdef UNIXHier +ifdef BSDhier +SRCPREFIXDIR=share/src +else +ifdef linuxHier +SRCPREFIXDIR=share/src +else +SRCPREFIXDIR=src +endif +endif +ifdef INSTALL_FPCPACKAGE +ifdef INSTALL_FPCSUBDIR +INSTALL_SOURCEDIR:=$(INSTALL_PREFIX)/$(SRCPREFIXDIR)/fpc-$(FPC_VERSION)/$(INSTALL_FPCSUBDIR)/$(PACKAGE_NAME) +else +INSTALL_SOURCEDIR:=$(INSTALL_PREFIX)/$(SRCPREFIXDIR)/fpc-$(FPC_VERSION)/$(PACKAGE_NAME) +endif +else +INSTALL_SOURCEDIR:=$(INSTALL_PREFIX)/$(SRCPREFIXDIR)/$(PACKAGE_NAME)-$(PACKAGE_VERSION) +endif +else +ifdef INSTALL_FPCPACKAGE +ifdef INSTALL_FPCSUBDIR +INSTALL_SOURCEDIR:=$(INSTALL_BASEDIR)/source/$(INSTALL_FPCSUBDIR)/$(PACKAGE_NAME) +else +INSTALL_SOURCEDIR:=$(INSTALL_BASEDIR)/source/$(PACKAGE_NAME) +endif +else +INSTALL_SOURCEDIR:=$(INSTALL_BASEDIR)/source +endif +endif +endif +ifndef INSTALL_DOCDIR +ifdef UNIXHier +ifdef BSDhier +DOCPREFIXDIR=share/doc +else +ifdef linuxHier +DOCPREFIXDIR=share/doc +else +DOCPREFIXDIR=doc +endif +endif +ifdef INSTALL_FPCPACKAGE +INSTALL_DOCDIR:=$(INSTALL_PREFIX)/$(DOCPREFIXDIR)/fpc-$(FPC_VERSION)/$(PACKAGE_NAME) +else +INSTALL_DOCDIR:=$(INSTALL_PREFIX)/$(DOCPREFIXDIR)/$(PACKAGE_NAME)-$(PACKAGE_VERSION) +endif +else +ifdef INSTALL_FPCPACKAGE +INSTALL_DOCDIR:=$(INSTALL_BASEDIR)/doc/$(PACKAGE_NAME) +else +INSTALL_DOCDIR:=$(INSTALL_BASEDIR)/doc +endif +endif +endif +ifndef INSTALL_EXAMPLEDIR +ifdef UNIXHier +ifdef INSTALL_FPCPACKAGE +ifdef BSDhier +INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/share/examples/fpc-$(FPC_VERSION)/$(PACKAGE_NAME) +else +ifdef linuxHier +INSTALL_EXAMPLEDIR:=$(INSTALL_DOCDIR)/examples +else +INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/doc/fpc-$(FPC_VERSION)/examples/$(PACKAGE_NAME) +endif +endif +else +ifdef BSDhier +INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/share/examples/$(PACKAGE_NAME)-$(PACKAGE_VERSION) +else +ifdef linuxHier +INSTALL_EXAMPLEDIR:=$(INSTALL_DOCDIR)/examples/$(PACKAGE_NAME)-$(PACKAGE_VERSION) +else +INSTALL_EXAMPLEDIR:=$(INSTALL_PREFIX)/doc/$(PACKAGE_NAME)-$(PACKAGE_VERSION) +endif +endif +endif +else +ifdef INSTALL_FPCPACKAGE +INSTALL_EXAMPLEDIR:=$(INSTALL_BASEDIR)/examples/$(PACKAGE_NAME) +else +INSTALL_EXAMPLEDIR:=$(INSTALL_BASEDIR)/examples +endif +endif +endif +ifndef INSTALL_DATADIR +INSTALL_DATADIR=$(INSTALL_BASEDIR) +endif +ifndef INSTALL_SHAREDDIR +INSTALL_SHAREDDIR=$(INSTALL_PREFIX)/lib +endif +ifdef CROSSCOMPILE +ifndef CROSSBINDIR +CROSSBINDIR:=$(wildcard $(CROSSTARGETDIR)/bin/$(SOURCESUFFIX)) +ifeq ($(CROSSBINDIR),) +CROSSBINDIR:=$(wildcard $(INSTALL_BASEDIR)/cross/$(TARGETSUFFIX)/bin/$(FULL_SOURCE)) +endif +endif +else +CROSSBINDIR= +endif +BATCHEXT=.bat +LOADEREXT=.as +EXEEXT=.exe +PPLEXT=.ppl +PPUEXT=.ppu +OEXT=.o +ASMEXT=.s +SMARTEXT=.sl +STATICLIBEXT=.a +SHAREDLIBEXT=.so +SHAREDLIBPREFIX=libfp +STATICLIBPREFIX=libp +IMPORTLIBPREFIX=libimp +RSTEXT=.rst +EXEDBGEXT=.dbg +ifeq ($(OS_TARGET),go32v1) +STATICLIBPREFIX= +SHORTSUFFIX=v1 +endif +ifeq ($(OS_TARGET),go32v2) +STATICLIBPREFIX= +SHORTSUFFIX=dos +IMPORTLIBPREFIX= +endif +ifeq ($(OS_TARGET),watcom) +STATICLIBPREFIX= +OEXT=.obj +ASMEXT=.asm +SHAREDLIBEXT=.dll +SHORTSUFFIX=wat +IMPORTLIBPREFIX= +endif +ifneq ($(CPU_TARGET),jvm) +ifeq ($(OS_TARGET),android) +BATCHEXT=.sh +EXEEXT= +HASSHAREDLIB=1 +SHORTSUFFIX=lnx +endif +endif +ifeq ($(OS_TARGET),linux) +BATCHEXT=.sh +EXEEXT= +HASSHAREDLIB=1 +SHORTSUFFIX=lnx +endif +ifeq ($(OS_TARGET),dragonfly) +BATCHEXT=.sh +EXEEXT= +HASSHAREDLIB=1 +SHORTSUFFIX=df +endif +ifeq ($(OS_TARGET),freebsd) +BATCHEXT=.sh +EXEEXT= +HASSHAREDLIB=1 +SHORTSUFFIX=fbs +endif +ifeq ($(OS_TARGET),netbsd) +BATCHEXT=.sh +EXEEXT= +HASSHAREDLIB=1 +SHORTSUFFIX=nbs +endif +ifeq ($(OS_TARGET),openbsd) +BATCHEXT=.sh +EXEEXT= +HASSHAREDLIB=1 +SHORTSUFFIX=obs +endif +ifeq ($(OS_TARGET),win32) +SHAREDLIBEXT=.dll +SHORTSUFFIX=w32 +endif +ifeq ($(OS_TARGET),os2) +BATCHEXT=.cmd +AOUTEXT=.out +STATICLIBPREFIX= +SHAREDLIBEXT=.dll +SHORTSUFFIX=os2 +ECHO=echo +IMPORTLIBPREFIX= +endif +ifeq ($(OS_TARGET),emx) +BATCHEXT=.cmd +AOUTEXT=.out +STATICLIBPREFIX= +SHAREDLIBEXT=.dll +SHORTSUFFIX=emx +ECHO=echo +IMPORTLIBPREFIX= +endif +ifeq ($(OS_TARGET),amiga) +EXEEXT= +SHAREDLIBEXT=.library +SHORTSUFFIX=amg +endif +ifeq ($(OS_TARGET),aros) +EXEEXT= +SHAREDLIBEXT=.library +SHORTSUFFIX=aros +endif +ifeq ($(OS_TARGET),morphos) +EXEEXT= +SHAREDLIBEXT=.library +SHORTSUFFIX=mos +endif +ifeq ($(OS_TARGET),atari) +EXEEXT=.ttp +SHORTSUFFIX=ata +endif +ifeq ($(OS_TARGET),beos) +BATCHEXT=.sh +EXEEXT= +SHORTSUFFIX=be +endif +ifeq ($(OS_TARGET),haiku) +BATCHEXT=.sh +EXEEXT= +SHORTSUFFIX=hai +endif +ifeq ($(OS_TARGET),solaris) +BATCHEXT=.sh +EXEEXT= +SHORTSUFFIX=sun +endif +ifeq ($(OS_TARGET),qnx) +BATCHEXT=.sh +EXEEXT= +SHORTSUFFIX=qnx +endif +ifeq ($(OS_TARGET),netware) +EXEEXT=.nlm +STATICLIBPREFIX= +SHORTSUFFIX=nw +IMPORTLIBPREFIX=imp +endif +ifeq ($(OS_TARGET),netwlibc) +EXEEXT=.nlm +STATICLIBPREFIX= +SHORTSUFFIX=nwl +IMPORTLIBPREFIX=imp +endif +ifeq ($(OS_TARGET),macos) +BATCHEXT= +EXEEXT= +DEBUGSYMEXT=.xcoff +SHORTSUFFIX=mac +IMPORTLIBPREFIX=imp +endif +ifneq ($(findstring $(OS_TARGET),darwin iphonesim),) +BATCHEXT=.sh +EXEEXT= +HASSHAREDLIB=1 +SHORTSUFFIX=dwn +EXEDBGEXT=.dSYM +endif +ifeq ($(OS_TARGET),gba) +EXEEXT=.gba +SHAREDLIBEXT=.so +SHORTSUFFIX=gba +endif +ifeq ($(OS_TARGET),symbian) +SHAREDLIBEXT=.dll +SHORTSUFFIX=symbian +endif +ifeq ($(OS_TARGET),NativeNT) +SHAREDLIBEXT=.dll +SHORTSUFFIX=nativent +endif +ifeq ($(OS_TARGET),wii) +EXEEXT=.dol +SHAREDLIBEXT=.so +SHORTSUFFIX=wii +endif +ifeq ($(OS_TARGET),aix) +BATCHEXT=.sh +EXEEXT= +SHORTSUFFIX=aix +endif +ifeq ($(OS_TARGET),java) +OEXT=.class +ASMEXT=.j +SHAREDLIBEXT=.jar +SHORTSUFFIX=java +endif +ifeq ($(CPU_TARGET),jvm) +ifeq ($(OS_TARGET),android) +OEXT=.class +ASMEXT=.j +SHAREDLIBEXT=.jar +SHORTSUFFIX=android +endif +endif +ifeq ($(OS_TARGET),msdos) +STATICLIBPREFIX= +STATICLIBEXT=.a +SHORTSUFFIX=d16 +endif +ifeq ($(OS_TARGET),embedded) +EXEEXT=.bin +SHORTSUFFIX=emb +endif +ifeq ($(OS_TARGET),win16) +STATICLIBPREFIX= +STATICLIBEXT=.a +SHAREDLIBEXT=.dll +SHORTSUFFIX=w16 +endif +ifneq ($(findstring $(OS_SOURCE),$(LIMIT83fs)),) +FPCMADE=fpcmade.$(SHORTSUFFIX) +ZIPSUFFIX=$(SHORTSUFFIX) +ZIPCROSSPREFIX= +ZIPSOURCESUFFIX=src +ZIPEXAMPLESUFFIX=exm +else +FPCMADE=fpcmade.$(TARGETSUFFIX) +ZIPSOURCESUFFIX=.source +ZIPEXAMPLESUFFIX=.examples +ifdef CROSSCOMPILE +ZIPSUFFIX=.$(SOURCESUFFIX) +ZIPCROSSPREFIX=$(TARGETSUFFIX)- +else +ZIPSUFFIX=.$(TARGETSUFFIX) +ZIPCROSSPREFIX= +endif +endif +ifndef ECHO +ECHO:=$(strip $(wildcard $(addsuffix /gecho$(SRCEXEEXT),$(SEARCHPATH)))) +ifeq ($(ECHO),) +ECHO:=$(strip $(wildcard $(addsuffix /echo$(SRCEXEEXT),$(SEARCHPATH)))) +ifeq ($(ECHO),) +ECHO= __missing_command_ECHO +else +ECHO:=$(firstword $(ECHO)) +endif +else +ECHO:=$(firstword $(ECHO)) +endif +endif +export ECHO +ifndef DATE +DATE:=$(strip $(wildcard $(addsuffix /gdate$(SRCEXEEXT),$(SEARCHPATH)))) +ifeq ($(DATE),) +DATE:=$(strip $(wildcard $(addsuffix /date$(SRCEXEEXT),$(SEARCHPATH)))) +ifeq ($(DATE),) +DATE= __missing_command_DATE +else +DATE:=$(firstword $(DATE)) +endif +else +DATE:=$(firstword $(DATE)) +endif +endif +export DATE +ifndef GINSTALL +GINSTALL:=$(strip $(wildcard $(addsuffix /ginstall$(SRCEXEEXT),$(SEARCHPATH)))) +ifeq ($(GINSTALL),) +GINSTALL:=$(strip $(wildcard $(addsuffix /install$(SRCEXEEXT),$(SEARCHPATH)))) +ifeq ($(GINSTALL),) +GINSTALL= __missing_command_GINSTALL +else +GINSTALL:=$(firstword $(GINSTALL)) +endif +else +GINSTALL:=$(firstword $(GINSTALL)) +endif +endif +export GINSTALL +ifndef CPPROG +CPPROG:=$(strip $(wildcard $(addsuffix /cp$(SRCEXEEXT),$(SEARCHPATH)))) +ifeq ($(CPPROG),) +CPPROG= __missing_command_CPPROG +else +CPPROG:=$(firstword $(CPPROG)) +endif +endif +export CPPROG +ifndef RMPROG +RMPROG:=$(strip $(wildcard $(addsuffix /rm$(SRCEXEEXT),$(SEARCHPATH)))) +ifeq ($(RMPROG),) +RMPROG= __missing_command_RMPROG +else +RMPROG:=$(firstword $(RMPROG)) +endif +endif +export RMPROG +ifndef MVPROG +MVPROG:=$(strip $(wildcard $(addsuffix /mv$(SRCEXEEXT),$(SEARCHPATH)))) +ifeq ($(MVPROG),) +MVPROG= __missing_command_MVPROG +else +MVPROG:=$(firstword $(MVPROG)) +endif +endif +export MVPROG +ifndef MKDIRPROG +MKDIRPROG:=$(strip $(wildcard $(addsuffix /gmkdir$(SRCEXEEXT),$(SEARCHPATH)))) +ifeq ($(MKDIRPROG),) +MKDIRPROG:=$(strip $(wildcard $(addsuffix /mkdir$(SRCEXEEXT),$(SEARCHPATH)))) +ifeq ($(MKDIRPROG),) +MKDIRPROG= __missing_command_MKDIRPROG +else +MKDIRPROG:=$(firstword $(MKDIRPROG)) +endif +else +MKDIRPROG:=$(firstword $(MKDIRPROG)) +endif +endif +export MKDIRPROG +ifndef ECHOREDIR +ifndef inUnix +ECHOREDIR=echo +else +ECHOREDIR=$(ECHO) +endif +endif +ifndef COPY +COPY:=$(CPPROG) -fp +endif +ifndef COPYTREE +COPYTREE:=$(CPPROG) -Rfp +endif +ifndef MKDIRTREE +MKDIRTREE:=$(MKDIRPROG) -p +endif +ifndef MOVE +MOVE:=$(MVPROG) -f +endif +ifndef DEL +DEL:=$(RMPROG) -f +endif +ifndef DELTREE +DELTREE:=$(RMPROG) -rf +endif +ifndef INSTALL +ifdef inUnix +INSTALL:=$(GINSTALL) -c -m 644 +else +INSTALL:=$(COPY) +endif +endif +ifndef INSTALLEXE +ifdef inUnix +INSTALLEXE:=$(GINSTALL) -c -m 755 +else +INSTALLEXE:=$(COPY) +endif +endif +ifndef MKDIR +MKDIR:=$(GINSTALL) -m 755 -d +endif +export ECHOREDIR COPY COPYTREE MOVE DEL DELTREE INSTALL INSTALLEXE MKDIR +ifndef PPUMOVE +PPUMOVE:=$(strip $(wildcard $(addsuffix /ppumove$(SRCEXEEXT),$(SEARCHPATH)))) +ifeq ($(PPUMOVE),) +PPUMOVE= __missing_command_PPUMOVE +else +PPUMOVE:=$(firstword $(PPUMOVE)) +endif +endif +export PPUMOVE +ifndef FPCMAKE +FPCMAKE:=$(strip $(wildcard $(addsuffix /fpcmake$(SRCEXEEXT),$(SEARCHPATH)))) +ifeq ($(FPCMAKE),) +FPCMAKE= __missing_command_FPCMAKE +else +FPCMAKE:=$(firstword $(FPCMAKE)) +endif +endif +export FPCMAKE +ifndef ZIPPROG +ZIPPROG:=$(strip $(wildcard $(addsuffix /zip$(SRCEXEEXT),$(SEARCHPATH)))) +ifeq ($(ZIPPROG),) +ZIPPROG= __missing_command_ZIPPROG +else +ZIPPROG:=$(firstword $(ZIPPROG)) +endif +endif +export ZIPPROG +ifndef TARPROG +TARPROG:=$(strip $(wildcard $(addsuffix /gtar$(SRCEXEEXT),$(SEARCHPATH)))) +ifeq ($(TARPROG),) +TARPROG:=$(strip $(wildcard $(addsuffix /tar$(SRCEXEEXT),$(SEARCHPATH)))) +ifeq ($(TARPROG),) +TARPROG= __missing_command_TARPROG +else +TARPROG:=$(firstword $(TARPROG)) +endif +else +TARPROG:=$(firstword $(TARPROG)) +endif +endif +export TARPROG +ASNAME=$(BINUTILSPREFIX)as +LDNAME=$(BINUTILSPREFIX)ld +ARNAME=$(BINUTILSPREFIX)ar +RCNAME=$(BINUTILSPREFIX)rc +NASMNAME=$(BINUTILSPREFIX)nasm +ifndef ASPROG +ifdef CROSSBINDIR +ASPROG=$(CROSSBINDIR)/$(ASNAME)$(SRCEXEEXT) +else +ASPROG=$(ASNAME) +endif +endif +ifndef LDPROG +ifdef CROSSBINDIR +LDPROG=$(CROSSBINDIR)/$(LDNAME)$(SRCEXEEXT) +else +LDPROG=$(LDNAME) +endif +endif +ifndef RCPROG +ifdef CROSSBINDIR +RCPROG=$(CROSSBINDIR)/$(RCNAME)$(SRCEXEEXT) +else +RCPROG=$(RCNAME) +endif +endif +ifndef ARPROG +ifdef CROSSBINDIR +ARPROG=$(CROSSBINDIR)/$(ARNAME)$(SRCEXEEXT) +else +ARPROG=$(ARNAME) +endif +endif +ifndef NASMPROG +ifdef CROSSBINDIR +NASMPROG=$(CROSSBINDIR)/$(NASMNAME)$(SRCEXEEXT) +else +NASMPROG=$(NASMNAME) +endif +endif +AS=$(ASPROG) +LD=$(LDPROG) +RC=$(RCPROG) +AR=$(ARPROG) +NASM=$(NASMPROG) +ifdef inUnix +PPAS=./ppas$(SRCBATCHEXT) +else +PPAS=ppas$(SRCBATCHEXT) +endif +ifdef inUnix +LDCONFIG=ldconfig +else +LDCONFIG= +endif +ifdef DATE +DATESTR:=$(shell $(DATE) +%Y%m%d) +else +DATESTR= +endif +ZIPOPT=-9 +ZIPEXT=.zip +ifeq ($(USETAR),bz2) +TAROPT=vj +TAREXT=.tar.bz2 +else +TAROPT=vz +TAREXT=.tar.gz +endif +override REQUIRE_PACKAGES=rtl +ifeq ($(FULL_TARGET),i386-linux) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-go32v2) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-win32) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-os2) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-freebsd) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-beos) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-haiku) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-netbsd) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-solaris) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-qnx) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-netware) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-openbsd) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-wdosx) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-darwin) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-emx) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-watcom) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-netwlibc) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-wince) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-embedded) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-symbian) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-nativent) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-iphonesim) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-android) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i386-aros) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),m68k-linux) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),m68k-freebsd) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),m68k-netbsd) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),m68k-amiga) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),m68k-atari) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),m68k-openbsd) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),m68k-palmos) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),m68k-embedded) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),powerpc-linux) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),powerpc-netbsd) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),powerpc-amiga) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),powerpc-macos) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),powerpc-darwin) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),powerpc-morphos) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),powerpc-embedded) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),powerpc-wii) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),powerpc-aix) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),sparc-linux) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),sparc-netbsd) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),sparc-solaris) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),sparc-embedded) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),x86_64-linux) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),x86_64-freebsd) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),x86_64-netbsd) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),x86_64-solaris) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),x86_64-openbsd) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),x86_64-darwin) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),x86_64-win64) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),x86_64-embedded) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),x86_64-iphonesim) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),x86_64-aros) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),x86_64-dragonfly) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),arm-linux) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),arm-palmos) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),arm-darwin) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),arm-wince) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),arm-gba) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),arm-nds) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),arm-embedded) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),arm-symbian) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),arm-android) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),powerpc64-linux) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),powerpc64-darwin) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),powerpc64-embedded) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),powerpc64-aix) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),avr-embedded) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),armeb-linux) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),armeb-embedded) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),mips-linux) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),mipsel-linux) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),mipsel-embedded) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),mipsel-android) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),jvm-java) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),jvm-android) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i8086-msdos) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),i8086-win16) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),aarch64-linux) +REQUIRE_PACKAGES_RTL=1 +endif +ifeq ($(FULL_TARGET),aarch64-darwin) +REQUIRE_PACKAGES_RTL=1 +endif +ifdef REQUIRE_PACKAGES_RTL +PACKAGEDIR_RTL:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /rtl/Makefile.fpc,$(PACKAGESDIR)))))) +ifneq ($(PACKAGEDIR_RTL),) +ifneq ($(wildcard $(PACKAGEDIR_RTL)/units/$(TARGETSUFFIX)),) +UNITDIR_RTL=$(PACKAGEDIR_RTL)/units/$(TARGETSUFFIX) +else +UNITDIR_RTL=$(PACKAGEDIR_RTL) +endif +ifneq ($(wildcard $(PACKAGEDIR_RTL)/units/$(SOURCESUFFIX)),) +UNITDIR_FPMAKE_RTL=$(PACKAGEDIR_RTL)/units/$(SOURCESUFFIX) +else +ifneq ($(wildcard $(PACKAGEDIR_RTL)/units_bs/$(SOURCESUFFIX)),) +UNITDIR_FPMAKE_RTL=$(PACKAGEDIR_RTL)/units_bs/$(SOURCESUFFIX) +else +UNITDIR_FPMAKE_RTL=$(PACKAGEDIR_RTL) +endif +endif +ifdef CHECKDEPEND +$(PACKAGEDIR_RTL)/$(OS_TARGET)/$(FPCMADE): + $(MAKE) -C $(PACKAGEDIR_RTL)/$(OS_TARGET) $(FPCMADE) +override ALLDEPENDENCIES+=$(PACKAGEDIR_RTL)/$(OS_TARGET)/$(FPCMADE) +endif +else +PACKAGEDIR_RTL= +UNITDIR_RTL:=$(subst /Package.fpc,,$(strip $(wildcard $(addsuffix /rtl/Package.fpc,$(UNITSDIR))))) +ifneq ($(UNITDIR_RTL),) +UNITDIR_RTL:=$(firstword $(UNITDIR_RTL)) +else +UNITDIR_RTL= +endif +endif +ifdef UNITDIR_RTL +override COMPILER_UNITDIR+=$(UNITDIR_RTL) +endif +ifdef UNITDIR_FPMAKE_RTL +override COMPILER_FPMAKE_UNITDIR+=$(UNITDIR_FPMAKE_RTL) +endif +endif +ifndef NOCPUDEF +override FPCOPTDEF=$(ARCH) +endif +ifneq ($(OS_TARGET),$(OS_SOURCE)) +override FPCOPT+=-T$(OS_TARGET) +endif +ifneq ($(CPU_TARGET),$(CPU_SOURCE)) +override FPCOPT+=-P$(ARCH) +endif +ifeq ($(OS_SOURCE),openbsd) +override FPCOPT+=-FD$(NEW_BINUTILS_PATH) +override FPCMAKEOPT+=-FD$(NEW_BINUTILS_PATH) +override FPMAKE_BUILD_OPT+=-FD$(NEW_BINUTILS_PATH) +endif +ifndef CROSSBOOTSTRAP +ifneq ($(BINUTILSPREFIX),) +override FPCOPT+=-XP$(BINUTILSPREFIX) +endif +ifneq ($(BINUTILSPREFIX),) +override FPCOPT+=-Xr$(RLINKPATH) +endif +endif +ifndef CROSSCOMPILE +ifneq ($(BINUTILSPREFIX),) +override FPCMAKEOPT+=-XP$(BINUTILSPREFIX) +override FPMAKE_BUILD_OPT+=-XP$(BINUTILSPREFIX) +endif +endif +ifdef UNITDIR +override FPCOPT+=$(addprefix -Fu,$(UNITDIR)) +endif +ifdef LIBDIR +override FPCOPT+=$(addprefix -Fl,$(LIBDIR)) +endif +ifdef OBJDIR +override FPCOPT+=$(addprefix -Fo,$(OBJDIR)) +endif +ifdef INCDIR +override FPCOPT+=$(addprefix -Fi,$(INCDIR)) +endif +ifdef LINKSMART +override FPCOPT+=-XX +endif +ifdef CREATESMART +override FPCOPT+=-CX +endif +ifdef DEBUG +override FPCOPT+=-gl +override FPCOPTDEF+=DEBUG +endif +ifdef RELEASE +ifneq ($(findstring 2.0.,$(FPC_VERSION)),) +ifeq ($(CPU_TARGET),i386) +FPCCPUOPT:=-OG2p3 +endif +ifeq ($(CPU_TARGET),powerpc) +FPCCPUOPT:=-O1r +endif +else +FPCCPUOPT:=-O2 +endif +override FPCOPT+=-Ur -Xs $(FPCCPUOPT) -n +override FPCOPTDEF+=RELEASE +endif +ifdef STRIP +override FPCOPT+=-Xs +endif +ifdef OPTIMIZE +override FPCOPT+=-O2 +endif +ifdef VERBOSE +override FPCOPT+=-vwni +endif +ifdef COMPILER_OPTIONS +override FPCOPT+=$(COMPILER_OPTIONS) +endif +ifdef COMPILER_UNITDIR +override FPCOPT+=$(addprefix -Fu,$(COMPILER_UNITDIR)) +endif +ifdef COMPILER_LIBRARYDIR +override FPCOPT+=$(addprefix -Fl,$(COMPILER_LIBRARYDIR)) +endif +ifdef COMPILER_OBJECTDIR +override FPCOPT+=$(addprefix -Fo,$(COMPILER_OBJECTDIR)) +endif +ifdef COMPILER_INCLUDEDIR +override FPCOPT+=$(addprefix -Fi,$(COMPILER_INCLUDEDIR)) +endif +ifdef CROSSBINDIR +override FPCOPT+=-FD$(CROSSBINDIR) +endif +ifdef COMPILER_TARGETDIR +override FPCOPT+=-FE$(COMPILER_TARGETDIR) +ifeq ($(COMPILER_TARGETDIR),.) +override TARGETDIRPREFIX= +else +override TARGETDIRPREFIX=$(COMPILER_TARGETDIR)/ +endif +endif +ifdef COMPILER_UNITTARGETDIR +override FPCOPT+=-FU$(COMPILER_UNITTARGETDIR) +ifeq ($(COMPILER_UNITTARGETDIR),.) +override UNITTARGETDIRPREFIX= +else +override UNITTARGETDIRPREFIX=$(COMPILER_UNITTARGETDIR)/ +endif +else +ifdef COMPILER_TARGETDIR +override COMPILER_UNITTARGETDIR=$(COMPILER_TARGETDIR) +override UNITTARGETDIRPREFIX=$(TARGETDIRPREFIX) +endif +endif +ifdef CREATESHARED +override FPCOPT+=-Cg +endif +ifneq ($(findstring $(OS_TARGET),dragonfly freebsd openbsd netbsd linux solaris),) +ifneq ($(findstring $(CPU_TARGET),x86_64 mips mipsel),) +override FPCOPT+=-Cg +endif +endif +ifdef LINKSHARED +endif +ifdef OPT +override FPCOPT+=$(OPT) +endif +ifdef FPMAKEBUILDOPT +override FPMAKE_BUILD_OPT+=$(FPMAKEBUILDOPT) +endif +ifdef FPCOPTDEF +override FPCOPT+=$(addprefix -d,$(FPCOPTDEF)) +endif +ifdef CFGFILE +override FPCOPT+=@$(CFGFILE) +endif +ifdef USEENV +override FPCEXTCMD:=$(FPCOPT) +override FPCOPT:=!FPCEXTCMD +export FPCEXTCMD +endif +override AFULL_TARGET=$(CPU_TARGET)-$(OS_TARGET) +override AFULL_SOURCE=$(CPU_SOURCE)-$(OS_SOURCE) +ifneq ($(AFULL_TARGET),$(AFULL_SOURCE)) +override ACROSSCOMPILE=1 +endif +ifdef ACROSSCOMPILE +override FPCOPT+=$(CROSSOPT) +endif +override COMPILER:=$(strip $(FPC) $(FPCOPT)) +ifneq (,$(findstring -sh ,$(COMPILER))) +UseEXECPPAS=1 +endif +ifneq (,$(findstring -s ,$(COMPILER))) +ifeq ($(FULL_SOURCE),$(FULL_TARGET)) +UseEXECPPAS=1 +endif +endif +ifneq ($(UseEXECPPAS),1) +EXECPPAS= +else +ifdef RUNBATCH +EXECPPAS:=@$(RUNBATCH) $(PPAS) +else +EXECPPAS:=@$(PPAS) +endif +endif +ifdef TARGET_RSTS +override RSTFILES=$(addsuffix $(RSTEXT),$(TARGET_RSTS)) +override CLEANRSTFILES+=$(RSTFILES) +endif +.PHONY: fpc_install fpc_sourceinstall fpc_exampleinstall +ifdef INSTALL_UNITS +override INSTALLPPUFILES+=$(addsuffix $(PPUEXT),$(INSTALL_UNITS)) +endif +ifdef INSTALL_BUILDUNIT +override INSTALLPPUFILES:=$(filter-out $(INSTALL_BUILDUNIT)$(PPUEXT),$(INSTALLPPUFILES)) +endif +ifdef INSTALLPPUFILES +override INSTALLPPULINKFILES:=$(subst $(PPUEXT),$(OEXT),$(INSTALLPPUFILES)) $(addprefix $(STATICLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(INSTALLPPUFILES))) $(addprefix $(IMPORTLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(INSTALLPPUFILES))) +ifneq ($(UNITTARGETDIRPREFIX),) +override INSTALLPPUFILES:=$(addprefix $(UNITTARGETDIRPREFIX),$(notdir $(INSTALLPPUFILES))) +override INSTALLPPULINKFILES:=$(wildcard $(addprefix $(UNITTARGETDIRPREFIX),$(notdir $(INSTALLPPULINKFILES)))) +endif +override INSTALL_CREATEPACKAGEFPC=1 +endif +ifdef INSTALLEXEFILES +ifneq ($(TARGETDIRPREFIX),) +override INSTALLEXEFILES:=$(addprefix $(TARGETDIRPREFIX),$(notdir $(INSTALLEXEFILES))) +endif +endif +fpc_install: all $(INSTALLTARGET) +ifdef INSTALLEXEFILES + $(MKDIR) $(INSTALL_BINDIR) + $(INSTALLEXE) $(INSTALLEXEFILES) $(INSTALL_BINDIR) +endif +ifdef INSTALL_CREATEPACKAGEFPC +ifdef FPCMAKE +ifdef PACKAGE_VERSION +ifneq ($(wildcard Makefile.fpc),) + $(FPCMAKE) -p -T$(CPU_TARGET)-$(OS_TARGET) Makefile.fpc + $(MKDIR) $(INSTALL_UNITDIR) + $(INSTALL) Package.fpc $(INSTALL_UNITDIR) +endif +endif +endif +endif +ifdef INSTALLPPUFILES + $(MKDIR) $(INSTALL_UNITDIR) + $(INSTALL) $(INSTALLPPUFILES) $(INSTALL_UNITDIR) +ifneq ($(INSTALLPPULINKFILES),) + $(INSTALL) $(INSTALLPPULINKFILES) $(INSTALL_UNITDIR) +endif +ifneq ($(wildcard $(LIB_FULLNAME)),) + $(MKDIR) $(INSTALL_LIBDIR) + $(INSTALL) $(LIB_FULLNAME) $(INSTALL_LIBDIR) +ifdef inUnix + ln -sf $(LIB_FULLNAME) $(INSTALL_LIBDIR)/$(LIB_NAME) +endif +endif +endif +ifdef INSTALL_FILES + $(MKDIR) $(INSTALL_DATADIR) + $(INSTALL) $(INSTALL_FILES) $(INSTALL_DATADIR) +endif +fpc_sourceinstall: distclean + $(MKDIR) $(INSTALL_SOURCEDIR) + $(COPYTREE) $(BASEDIR)/* $(INSTALL_SOURCEDIR) +fpc_exampleinstall: $(EXAMPLEINSTALLTARGET) $(addsuffix _distclean,$(TARGET_EXAMPLEDIRS)) +ifdef HASEXAMPLES + $(MKDIR) $(INSTALL_EXAMPLEDIR) +endif +ifdef EXAMPLESOURCEFILES + $(COPY) $(EXAMPLESOURCEFILES) $(INSTALL_EXAMPLEDIR) +endif +ifdef TARGET_EXAMPLEDIRS + $(COPYTREE) $(addsuffix /*,$(TARGET_EXAMPLEDIRS)) $(INSTALL_EXAMPLEDIR) +endif +.PHONY: fpc_distinstall +fpc_distinstall: install exampleinstall +.PHONY: fpc_zipinstall fpc_zipsourceinstall fpc_zipexampleinstall +ifndef PACKDIR +ifndef inUnix +PACKDIR=$(BASEDIR)/../fpc-pack +else +PACKDIR=/tmp/fpc-pack +endif +endif +ifndef ZIPNAME +ifdef DIST_ZIPNAME +ZIPNAME=$(DIST_ZIPNAME) +else +ZIPNAME=$(PACKAGE_NAME) +endif +endif +ifndef FULLZIPNAME +FULLZIPNAME=$(ZIPCROSSPREFIX)$(ZIPPREFIX)$(ZIPNAME)$(ZIPSUFFIX) +endif +ifndef ZIPTARGET +ifdef DIST_ZIPTARGET +ZIPTARGET=DIST_ZIPTARGET +else +ZIPTARGET=install +endif +endif +ifndef USEZIP +ifdef inUnix +USETAR=1 +endif +endif +ifndef inUnix +USEZIPWRAPPER=1 +endif +ifdef USEZIPWRAPPER +ZIPPATHSEP=$(PATHSEP) +ZIPWRAPPER=$(subst /,$(PATHSEP),$(DIST_DESTDIR)/fpczip$(SRCBATCHEXT)) +else +ZIPPATHSEP=/ +endif +ZIPCMD_CDPACK:=cd $(subst /,$(ZIPPATHSEP),$(PACKDIR)) +ZIPCMD_CDBASE:=cd $(subst /,$(ZIPPATHSEP),$(BASEDIR)) +ifdef USETAR +ZIPDESTFILE:=$(DIST_DESTDIR)/$(FULLZIPNAME)$(TAREXT) +ZIPCMD_ZIP:=$(TARPROG) c$(TAROPT)f $(ZIPDESTFILE) * +else +ZIPDESTFILE:=$(DIST_DESTDIR)/$(FULLZIPNAME)$(ZIPEXT) +ZIPCMD_ZIP:=$(subst /,$(ZIPPATHSEP),$(ZIPPROG)) -Dr $(ZIPOPT) $(ZIPDESTFILE) * +endif +fpc_zipinstall: + $(MAKE) $(ZIPTARGET) INSTALL_PREFIX=$(PACKDIR) ZIPINSTALL=1 + $(MKDIR) $(DIST_DESTDIR) + $(DEL) $(ZIPDESTFILE) +ifdef USEZIPWRAPPER +ifneq ($(ECHOREDIR),echo) + $(ECHOREDIR) -e "$(subst \,\\,$(ZIPCMD_CDPACK))" > $(ZIPWRAPPER) + $(ECHOREDIR) -e "$(subst \,\\,$(ZIPCMD_ZIP))" >> $(ZIPWRAPPER) + $(ECHOREDIR) -e "$(subst \,\\,$(ZIPCMD_CDBASE))" >> $(ZIPWRAPPER) +else + echo $(ZIPCMD_CDPACK) > $(ZIPWRAPPER) + echo $(ZIPCMD_ZIP) >> $(ZIPWRAPPER) + echo $(ZIPCMD_CDBASE) >> $(ZIPWRAPPER) +endif +ifdef inUnix + /bin/sh $(ZIPWRAPPER) +else +ifdef RUNBATCH + $(RUNBATCH) $(ZIPWRAPPER) +else + $(ZIPWRAPPER) +endif +endif + $(DEL) $(ZIPWRAPPER) +else + $(ZIPCMD_CDPACK) ; $(ZIPCMD_ZIP) ; $(ZIPCMD_CDBASE) +endif + $(DELTREE) $(PACKDIR) +fpc_zipsourceinstall: + $(MAKE) fpc_zipinstall ZIPTARGET=sourceinstall ZIPSUFFIX=$(ZIPSOURCESUFFIX) +fpc_zipexampleinstall: +ifdef HASEXAMPLES + $(MAKE) fpc_zipinstall ZIPTARGET=exampleinstall ZIPSUFFIX=$(ZIPEXAMPLESUFFIX) +endif +fpc_zipdistinstall: + $(MAKE) fpc_zipinstall ZIPTARGET=distinstall +.PHONY: fpc_clean fpc_cleanall fpc_distclean +ifdef EXEFILES +override CLEANEXEFILES:=$(addprefix $(TARGETDIRPREFIX),$(CLEANEXEFILES)) +override CLEANEXEDBGFILES:=$(addprefix $(TARGETDIRPREFIX),$(CLEANEXEDBGFILES)) +endif +ifdef CLEAN_PROGRAMS +override CLEANEXEFILES+=$(addprefix $(TARGETDIRPREFIX),$(addsuffix $(EXEEXT), $(CLEAN_PROGRAMS))) +override CLEANEXEDBGFILES+=$(addprefix $(TARGETDIRPREFIX),$(addsuffix $(EXEDBGEXT), $(CLEAN_PROGRAMS))) +endif +ifdef CLEAN_UNITS +override CLEANPPUFILES+=$(addsuffix $(PPUEXT),$(CLEAN_UNITS)) +endif +ifdef CLEANPPUFILES +override CLEANPPULINKFILES:=$(subst $(PPUEXT),$(OEXT),$(CLEANPPUFILES)) $(addprefix $(STATICLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(CLEANPPUFILES))) $(addprefix $(IMPORTLIBPREFIX),$(subst $(PPUEXT),$(STATICLIBEXT),$(CLEANPPUFILES))) +ifdef DEBUGSYMEXT +override CLEANPPULINKFILES+=$(subst $(PPUEXT),$(DEBUGSYMEXT),$(CLEANPPUFILES)) +endif +override CLEANPPUFILES:=$(addprefix $(UNITTARGETDIRPREFIX),$(CLEANPPUFILES)) +override CLEANPPULINKFILES:=$(wildcard $(addprefix $(UNITTARGETDIRPREFIX),$(CLEANPPULINKFILES))) +endif +fpc_clean: $(CLEANTARGET) +ifdef CLEANEXEFILES + -$(DEL) $(CLEANEXEFILES) +endif +ifdef CLEANEXEDBGFILES + -$(DELTREE) $(CLEANEXEDBGFILES) +endif +ifdef CLEANPPUFILES + -$(DEL) $(CLEANPPUFILES) +endif +ifneq ($(CLEANPPULINKFILES),) + -$(DEL) $(CLEANPPULINKFILES) +endif +ifdef CLEANRSTFILES + -$(DEL) $(addprefix $(UNITTARGETDIRPREFIX),$(CLEANRSTFILES)) +endif +ifdef CLEAN_FILES + -$(DEL) $(CLEAN_FILES) +endif +ifdef LIB_NAME + -$(DEL) $(LIB_NAME) $(LIB_FULLNAME) +endif + -$(DEL) $(FPCMADE) Package.fpc $(PPAS) script.res link.res $(FPCEXTFILE) $(REDIRFILE) + -$(DEL) *$(ASMEXT) *_ppas$(BATCHEXT) +fpc_cleanall: $(CLEANTARGET) +ifdef CLEANEXEFILES + -$(DEL) $(CLEANEXEFILES) +endif +ifdef COMPILER_UNITTARGETDIR +ifdef CLEANPPUFILES + -$(DEL) $(CLEANPPUFILES) +endif +ifneq ($(CLEANPPULINKFILES),) + -$(DEL) $(CLEANPPULINKFILES) +endif +ifdef CLEANRSTFILES + -$(DEL) $(addprefix $(UNITTARGETDIRPREFIX),$(CLEANRSTFILES)) +endif +endif +ifdef CLEAN_FILES + -$(DEL) $(CLEAN_FILES) +endif + -$(DELTREE) units + -$(DEL) *$(OEXT) *$(PPUEXT) *$(RSTEXT) *$(ASMEXT) *$(STATICLIBEXT) *$(SHAREDLIBEXT) *$(PPLEXT) +ifneq ($(PPUEXT),.ppu) + -$(DEL) *.o *.ppu *.a +endif + -$(DELTREE) *$(SMARTEXT) + -$(DEL) fpcmade.* Package.fpc $(PPAS) script.res link.res $(FPCEXTFILE) $(REDIRFILE) + -$(DEL) *_ppas$(BATCHEXT) +ifdef AOUTEXT + -$(DEL) *$(AOUTEXT) +endif +ifdef DEBUGSYMEXT + -$(DEL) *$(DEBUGSYMEXT) +endif +ifdef LOCALFPMAKEBIN + -$(DEL) $(LOCALFPMAKEBIN) + -$(DEL) $(FPMAKEBINOBJ) +endif +fpc_distclean: cleanall +.PHONY: fpc_baseinfo +override INFORULES+=fpc_baseinfo +fpc_baseinfo: + @$(ECHO) + @$(ECHO) == Package info == + @$(ECHO) Package Name..... $(PACKAGE_NAME) + @$(ECHO) Package Version.. $(PACKAGE_VERSION) + @$(ECHO) + @$(ECHO) == Configuration info == + @$(ECHO) + @$(ECHO) FPC.......... $(FPC) + @$(ECHO) FPC Version.. $(FPC_VERSION) + @$(ECHO) Source CPU... $(CPU_SOURCE) + @$(ECHO) Target CPU... $(CPU_TARGET) + @$(ECHO) Source OS.... $(OS_SOURCE) + @$(ECHO) Target OS.... $(OS_TARGET) + @$(ECHO) Full Source.. $(FULL_SOURCE) + @$(ECHO) Full Target.. $(FULL_TARGET) + @$(ECHO) SourceSuffix. $(SOURCESUFFIX) + @$(ECHO) TargetSuffix. $(TARGETSUFFIX) + @$(ECHO) FPC fpmake... $(FPCFPMAKE) + @$(ECHO) + @$(ECHO) == Directory info == + @$(ECHO) + @$(ECHO) Required pkgs... $(REQUIRE_PACKAGES) + @$(ECHO) + @$(ECHO) Basedir......... $(BASEDIR) + @$(ECHO) FPCDir.......... $(FPCDIR) + @$(ECHO) CrossBinDir..... $(CROSSBINDIR) + @$(ECHO) UnitsDir........ $(UNITSDIR) + @$(ECHO) PackagesDir..... $(PACKAGESDIR) + @$(ECHO) + @$(ECHO) GCC library..... $(GCCLIBDIR) + @$(ECHO) Other library... $(OTHERLIBDIR) + @$(ECHO) + @$(ECHO) == Tools info == + @$(ECHO) + @$(ECHO) As........ $(AS) + @$(ECHO) Ld........ $(LD) + @$(ECHO) Ar........ $(AR) + @$(ECHO) Rc........ $(RC) + @$(ECHO) + @$(ECHO) Mv........ $(MVPROG) + @$(ECHO) Cp........ $(CPPROG) + @$(ECHO) Rm........ $(RMPROG) + @$(ECHO) GInstall.. $(GINSTALL) + @$(ECHO) Echo...... $(ECHO) + @$(ECHO) Shell..... $(SHELL) + @$(ECHO) Date...... $(DATE) + @$(ECHO) FPCMake... $(FPCMAKE) + @$(ECHO) PPUMove... $(PPUMOVE) + @$(ECHO) Zip....... $(ZIPPROG) + @$(ECHO) + @$(ECHO) == Object info == + @$(ECHO) + @$(ECHO) Target Loaders........ $(TARGET_LOADERS) + @$(ECHO) Target Units.......... $(TARGET_UNITS) + @$(ECHO) Target Implicit Units. $(TARGET_IMPLICITUNITS) + @$(ECHO) Target Programs....... $(TARGET_PROGRAMS) + @$(ECHO) Target Dirs........... $(TARGET_DIRS) + @$(ECHO) Target Examples....... $(TARGET_EXAMPLES) + @$(ECHO) Target ExampleDirs.... $(TARGET_EXAMPLEDIRS) + @$(ECHO) + @$(ECHO) Clean Units......... $(CLEAN_UNITS) + @$(ECHO) Clean Files......... $(CLEAN_FILES) + @$(ECHO) + @$(ECHO) Install Units....... $(INSTALL_UNITS) + @$(ECHO) Install Files....... $(INSTALL_FILES) + @$(ECHO) + @$(ECHO) == Install info == + @$(ECHO) + @$(ECHO) DateStr.............. $(DATESTR) + @$(ECHO) ZipName.............. $(ZIPNAME) + @$(ECHO) ZipPrefix............ $(ZIPPREFIX) + @$(ECHO) ZipCrossPrefix....... $(ZIPCROSSPREFIX) + @$(ECHO) ZipSuffix............ $(ZIPSUFFIX) + @$(ECHO) FullZipName.......... $(FULLZIPNAME) + @$(ECHO) Install FPC Package.. $(INSTALL_FPCPACKAGE) + @$(ECHO) + @$(ECHO) Install base dir..... $(INSTALL_BASEDIR) + @$(ECHO) Install binary dir... $(INSTALL_BINDIR) + @$(ECHO) Install library dir.. $(INSTALL_LIBDIR) + @$(ECHO) Install units dir.... $(INSTALL_UNITDIR) + @$(ECHO) Install source dir... $(INSTALL_SOURCEDIR) + @$(ECHO) Install doc dir...... $(INSTALL_DOCDIR) + @$(ECHO) Install example dir.. $(INSTALL_EXAMPLEDIR) + @$(ECHO) Install data dir..... $(INSTALL_DATADIR) + @$(ECHO) + @$(ECHO) Dist destination dir. $(DIST_DESTDIR) + @$(ECHO) Dist zip name........ $(DIST_ZIPNAME) + @$(ECHO) +.PHONY: fpc_info +fpc_info: $(INFORULES) +.PHONY: fpc_makefile fpc_makefiles fpc_makefile_sub1 fpc_makefile_sub2 \ + fpc_makefile_dirs +fpc_makefile: + $(FPCMAKE) -w -T$(OS_TARGET) Makefile.fpc +fpc_makefile_sub1: +ifdef TARGET_DIRS + $(FPCMAKE) -w -T$(OS_TARGET) $(addsuffix /Makefile.fpc,$(TARGET_DIRS)) +endif +ifdef TARGET_EXAMPLEDIRS + $(FPCMAKE) -w -T$(OS_TARGET) $(addsuffix /Makefile.fpc,$(TARGET_EXAMPLEDIRS)) +endif +fpc_makefile_sub2: $(addsuffix _makefile_dirs,$(TARGET_DIRS) $(TARGET_EXAMPLEDIRS)) +fpc_makefile_dirs: fpc_makefile_sub1 fpc_makefile_sub2 +fpc_makefiles: fpc_makefile fpc_makefile_dirs +units: +examples: +shared: +sourceinstall: fpc_sourceinstall +exampleinstall: fpc_exampleinstall +zipexampleinstall: fpc_zipexampleinstall +info: fpc_info +makefiles: fpc_makefiles +.PHONY: units examples shared sourceinstall exampleinstall zipexampleinstall info makefiles +ifneq ($(wildcard fpcmake.loc),) +include fpcmake.loc +endif +override FPCOPT:=$(filter-out -FU%,$(FPCOPT)) +override FPCOPT:=$(filter-out -FE%,$(FPCOPT)) +override FPCOPT:=$(filter-out $(addprefix -Fu,$(COMPILER_UNITDIR)),$(FPCOPT))# Compose general fpmake-parameters +ifdef FPMAKEOPT +FPMAKE_OPT+=$(FPMAKEOPT) +endif +FPMAKE_OPT+=--localunitdir=../.. +FPMAKE_OPT+=--globalunitdir=.. +FPMAKE_OPT+=$(FPC_TARGETOPT) +FPMAKE_OPT+=$(addprefix -o ,$(FPCOPT)) +FPMAKE_OPT+=--compiler=$(FPC) +FPMAKE_OPT+=-bu +.NOTPARALLEL: +fpmake$(SRCEXEEXT): fpmake.pp + $(FPCFPMAKE) fpmake.pp $(FPMAKE_SKIP_CONFIG) $(addprefix -Fu,$(COMPILER_FPMAKE_UNITDIR)) $(FPCMAKEOPT) $(OPT) +all: fpmake$(SRCEXEEXT) + $(LOCALFPMAKE) compile $(FPMAKE_OPT) +smart: fpmake$(SRCEXEEXT) + $(LOCALFPMAKE) compile $(FPMAKE_OPT) -o -XX -o -CX +release: fpmake$(SRCEXEEXT) + $(LOCALFPMAKE) compile $(FPMAKE_OPT) -o -dRELEASE +debug: fpmake$(SRCEXEEXT) + $(LOCALFPMAKE) compile $(FPMAKE_OPT) -o -dDEBUG +ifeq ($(FPMAKE_BIN_CLEAN),) +clean: +else +clean: + $(FPMAKE_BIN_CLEAN) clean $(FPMAKE_OPT) +endif +ifeq ($(FPMAKE_BIN_CLEAN),) +distclean: $(addsuffix _distclean,$(TARGET_DIRS)) fpc_cleanall +else +distclean: +ifdef inUnix + { $(FPMAKE_BIN_CLEAN) distclean $(FPMAKE_OPT); if [ $$? != "0" ]; then { echo Something wrong with fpmake exectable. Remove the executable and call make recursively to recover.; $(DEL) $(FPMAKE_BIN_CLEAN); $(MAKE) fpc_cleanall; }; fi; } +else + $(FPMAKE_BIN_CLEAN) distclean $(FPMAKE_OPT) +endif + -$(DEL) $(LOCALFPMAKE) +endif +cleanall: distclean +install: fpmake$(SRCEXEEXT) +ifdef UNIXHier + $(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_PREFIX) --baseinstalldir=$(INSTALL_LIBDIR)/fpc/$(FPC_VERSION) --unitinstalldir=$(INSTALL_UNITDIR) +else + $(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_BASEDIR) --baseinstalldir=$(INSTALL_BASEDIR) --unitinstalldir=$(INSTALL_UNITDIR) +endif +distinstall: fpmake$(SRCEXEEXT) +ifdef UNIXHier + $(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_PREFIX) --baseinstalldir=$(INSTALL_LIBDIR)/fpc/$(FPC_VERSION) --unitinstalldir=$(INSTALL_UNITDIR) -ie -fsp 0 +else + $(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_BASEDIR) --baseinstalldir=$(INSTALL_BASEDIR) --unitinstalldir=$(INSTALL_UNITDIR) -ie -fsp 0 +endif +zipinstall: fpmake$(SRCEXEEXT) + $(LOCALFPMAKE) zipinstall $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) +zipdistinstall: fpmake$(SRCEXEEXT) + $(LOCALFPMAKE) zipinstall $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) -ie -fsp 0 +zipsourceinstall: fpmake$(SRCEXEEXT) +ifdef UNIXHier + $(LOCALFPMAKE) archive $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) --prefix=share/src/fpc-\$$\(PACKAGEVERSION\)/$(INSTALL_FPCSUBDIR)/\$$\(PACKAGEDIRECTORY\) +else + $(LOCALFPMAKE) archive $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) --prefix=source\\$(INSTALL_FPCSUBDIR)\\\$$\(PACKAGEDIRECTORY\) +endif diff --git a/packages/libmicrohttpd/Makefile.fpc b/packages/libmicrohttpd/Makefile.fpc new file mode 100644 index 0000000000..bd1f51ae09 --- /dev/null +++ b/packages/libmicrohttpd/Makefile.fpc @@ -0,0 +1,102 @@ +# +# Makefile.fpc for running fpmake +# + +[package] +name=libmicrohttpd +version=3.1.1 + +[require] +packages=rtl + +[install] +fpcpackage=y + +[default] +fpcdir=../.. + +[prerules] +FPMAKE_BIN_CLEAN=$(wildcard ./fpmake$(SRCEXEEXT)) +ifdef OS_TARGET +FPC_TARGETOPT+=--os=$(OS_TARGET) +endif +ifdef CPU_TARGET +FPC_TARGETOPT+=--cpu=$(CPU_TARGET) +endif +LOCALFPMAKE=./fpmake$(SRCEXEEXT) + +[rules] +# Do not pass the Makefile's unit and binary target locations. Fpmake uses it's own. +override FPCOPT:=$(filter-out -FU%,$(FPCOPT)) +override FPCOPT:=$(filter-out -FE%,$(FPCOPT)) +# Do not pass the package-unitdirectories. Fpmake adds those and this way they don't apear in the .fpm +override FPCOPT:=$(filter-out $(addprefix -Fu,$(COMPILER_UNITDIR)),$(FPCOPT))# Compose general fpmake-parameters +# Compose general fpmake-parameters +ifdef FPMAKEOPT +FPMAKE_OPT+=$(FPMAKEOPT) +endif +FPMAKE_OPT+=--localunitdir=../.. +FPMAKE_OPT+=--globalunitdir=.. +FPMAKE_OPT+=$(FPC_TARGETOPT) +FPMAKE_OPT+=$(addprefix -o ,$(FPCOPT)) +FPMAKE_OPT+=--compiler=$(FPC) +FPMAKE_OPT+=-bu +.NOTPARALLEL: + +fpmake$(SRCEXEEXT): fpmake.pp + $(FPCFPMAKE) fpmake.pp $(FPMAKE_SKIP_CONFIG) $(addprefix -Fu,$(COMPILER_FPMAKE_UNITDIR)) $(FPCMAKEOPT) $(OPT) +all: fpmake$(SRCEXEEXT) + $(LOCALFPMAKE) compile $(FPMAKE_OPT) +smart: fpmake$(SRCEXEEXT) + $(LOCALFPMAKE) compile $(FPMAKE_OPT) -o -XX -o -CX +release: fpmake$(SRCEXEEXT) + $(LOCALFPMAKE) compile $(FPMAKE_OPT) -o -dRELEASE +debug: fpmake$(SRCEXEEXT) + $(LOCALFPMAKE) compile $(FPMAKE_OPT) -o -dDEBUG +# If no fpmake exists and (dist)clean is called, do not try to build fpmake, it will +# most often fail because the dependencies are cleared. +# In case of a clean, simply do nothing +ifeq ($(FPMAKE_BIN_CLEAN),) +clean: +else +clean: + $(FPMAKE_BIN_CLEAN) clean $(FPMAKE_OPT) +endif +# In case of a distclean, perform an 'old'-style distclean. This to avoid problems +# when the package is compiled using fpcmake prior to running this clean using fpmake +ifeq ($(FPMAKE_BIN_CLEAN),) +distclean: $(addsuffix _distclean,$(TARGET_DIRS)) fpc_cleanall +else +distclean: +ifdef inUnix + { $(FPMAKE_BIN_CLEAN) distclean $(FPMAKE_OPT); if [ $$? != "0" ]; then { echo Something wrong with fpmake exectable. Remove the executable and call make recursively to recover.; $(DEL) $(FPMAKE_BIN_CLEAN); $(MAKE) fpc_cleanall; }; fi; } +else + $(FPMAKE_BIN_CLEAN) distclean $(FPMAKE_OPT) +endif + -$(DEL) $(LOCALFPMAKE) +endif +cleanall: distclean +install: fpmake$(SRCEXEEXT) +ifdef UNIXHier + $(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_PREFIX) --baseinstalldir=$(INSTALL_LIBDIR)/fpc/$(FPC_VERSION) --unitinstalldir=$(INSTALL_UNITDIR) +else + $(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_BASEDIR) --baseinstalldir=$(INSTALL_BASEDIR) --unitinstalldir=$(INSTALL_UNITDIR) +endif +# distinstall also installs the example-sources and omits the location of the source- +# files from the fpunits.cfg files. +distinstall: fpmake$(SRCEXEEXT) +ifdef UNIXHier + $(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_PREFIX) --baseinstalldir=$(INSTALL_LIBDIR)/fpc/$(FPC_VERSION) --unitinstalldir=$(INSTALL_UNITDIR) -ie -fsp 0 +else + $(LOCALFPMAKE) install $(FPMAKE_OPT) --prefix=$(INSTALL_BASEDIR) --baseinstalldir=$(INSTALL_BASEDIR) --unitinstalldir=$(INSTALL_UNITDIR) -ie -fsp 0 +endif +zipinstall: fpmake$(SRCEXEEXT) + $(LOCALFPMAKE) zipinstall $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) +zipdistinstall: fpmake$(SRCEXEEXT) + $(LOCALFPMAKE) zipinstall $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) -ie -fsp 0 +zipsourceinstall: fpmake$(SRCEXEEXT) +ifdef UNIXHier + $(LOCALFPMAKE) archive $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) --prefix=share/src/fpc-\$$\(PACKAGEVERSION\)/$(INSTALL_FPCSUBDIR)/\$$\(PACKAGEDIRECTORY\) +else + $(LOCALFPMAKE) archive $(FPMAKE_OPT) --zipprefix=$(DIST_DESTDIR)/$(ZIPPREFIX) --prefix=source\\$(INSTALL_FPCSUBDIR)\\\$$\(PACKAGEDIRECTORY\) +endif diff --git a/packages/libmicrohttpd/examples/basicauthentication.pp b/packages/libmicrohttpd/examples/basicauthentication.pp new file mode 100644 index 0000000000..2ab0b7446f --- /dev/null +++ b/packages/libmicrohttpd/examples/basicauthentication.pp @@ -0,0 +1,70 @@ +(* Feel free to use this example code in any way + you see fit (Public Domain) *) + +// Original example: https://gnunet.org/svn/libmicrohttpd/doc/examples/basicauthentication.c + +program basicauthentication; + +{$mode objfpc}{$H+} + +uses + libmicrohttpd, SysUtils; + +const + PORT = 8888; + + function AnswerToConnection(ACls: Pointer; AConnection: PMHD_Connection; + AUrl: Pcchar; AMethod: Pcchar; AVersion: Pcchar; AUploadData: Pcchar; + AUploadDataSize: Psize_t; AConCls: PPointer): cint; cdecl; + var + VPage: Pcchar; + VUser: Pcchar; + VPass: Pcchar; + VReturn: cint; + VFail: Boolean; + VResponse: PMHD_Response; + begin + if StrComp(AMethod, 'GET') <> 0 then + Exit(MHD_NO); + if not Assigned(AConCls^) then + begin + AConCls^ := AConnection; + Exit(MHD_YES); + end; + VPass := nil; + VUser := MHD_basic_auth_get_username_password(AConnection, @VPass); + VFail := (VUser = nil) or (StrComp(VUser, 'root') <> 0) or + (StrComp(VPass, 'pa$$w0rd') <> 0); + if VUser <> nil then + VUser := nil; + if VPass <> nil then + VPass := nil; + if VFail then + begin + VPage := '<html><body>Go away.</body></html>'; + VResponse := MHD_create_response_from_buffer(Length(VPage), + Pointer(VPage), MHD_RESPMEM_PERSISTENT); + VReturn := MHD_queue_basic_auth_fail_response(AConnection, + 'my realm', VResponse); + end + else + begin + VPage := '<html><body>A secret.</body></html>'; + VResponse := MHD_create_response_from_buffer(Length(VPage), + Pointer(VPage), MHD_RESPMEM_PERSISTENT); + VReturn := MHD_queue_response(AConnection, MHD_HTTP_OK, VResponse); + end; + MHD_destroy_response(VResponse); + Result := VReturn; + end; + +var + VDaemon: PMHD_Daemon; +begin + VDaemon := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, nil, + nil, @AnswerToConnection, nil, MHD_OPTION_END); + if not Assigned(VDaemon) then + Halt(1); + ReadLn; + MHD_stop_daemon(VDaemon); +end. diff --git a/packages/libmicrohttpd/examples/benchmark.pp b/packages/libmicrohttpd/examples/benchmark.pp new file mode 100644 index 0000000000..1cbe6e8928 --- /dev/null +++ b/packages/libmicrohttpd/examples/benchmark.pp @@ -0,0 +1,131 @@ +(* + This file is part of libmicrohttpd + Copyright (C) 2007, 2013 Christian Grothoff (and other contributing authors) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*) +(** + * @file benchmark.pp (Original: benchmark.c) + * @brief minimal code to benchmark MHD GET performance + * @author Christian Grothoff / Silvio Clécio + *) + +program benchmark; + +{$mode objfpc}{$H+} +{$MACRO ON} +{$IF DEFINED(CPU_COUNT) and (CPU_COUNT + 0) < 2} + {$UNDEF CPU_COUNT} +{$ENDIF} +{$IF NOT DEFINED(CPU_COUNT)} + {$DEFINE CPU_COUNT := 2} +{$ENDIF} + +uses +{$IFDEF MSWINDOWS} + WinSock2, +{$ELSE} + BaseUnix, Unix, +{$ENDIF} + cmem, sysutils, cutils, libmicrohttpd; + +const + PAGE: Pcchar = '<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>'; + SMALL = 1024 * 128; + NUMBER_OF_THREADS = CPU_COUNT; + +var + small_deltas: array[0..SMALL] of cuint; + response: PMHD_Response; + + procedure completed_callback(cls: Pointer; connection: PMHD_Connection; + con_cls: PPointer; toe: MHD_RequestTerminationCode); cdecl; + var + tv: ptimeval; + tve: timeval; + delta: cuint64; + begin + tv := con_cls^; + if nil = tv then + Exit; + fpgettimeofday(@tve, nil); + delta := 0; + if tve.tv_usec >= tv^.tv_usec then + delta += (tve.tv_sec - tv^.tv_sec) * 1000000 + + (tve.tv_usec - tv^.tv_usec) + else + delta += (tve.tv_sec - tv^.tv_sec) * 1000000 - + tv^.tv_usec + tve.tv_usec; + if delta < SMALL then + Inc(small_deltas[delta]) + else + WriteLn(stdout, Format('D: %u 1', [delta])); + Free(tv); + end; + + function uri_logger_cb(cls: Pointer; uri: Pcchar): Pointer; cdecl; + var + tv: ptimeval; + begin + tv := Malloc(SizeOf(timeval)); + if nil <> tv then + fpgettimeofday(tv, nil); + Result := tv; + end; + + function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar; + method: Pcchar; version: Pcchar; upload_data: Pcchar; + upload_data_size: Psize_t; ptr: PPointer): cint; cdecl; + begin + if 0 <> strcomp(method, 'GET') then + Exit(MHD_NO); + Result := MHD_queue_response(connection, MHD_HTTP_OK, response); + end; + +var + d: PMHD_Daemon; + i: cuint; +begin + if argc <> 2 then + begin + WriteLn(argv[0] + ' PORT'); + Halt(1); + end; + response := MHD_create_response_from_buffer(Length(PAGE), Pointer(PAGE), + MHD_RESPMEM_PERSISTENT); +{$IF 0} + MHD_add_response_header (response, MHD_HTTP_HEADER_CONNECTION, 'close'); +{$ENDIF} + d := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY or MHD_SUPPRESS_DATE_NO_CLOCK +{$IFDEF EPOLL_SUPPORT} + or MHD_USE_EPOLL_LINUX_ONLY or MHD_USE_EPOLL_TURBO +{$ENDIF}, + StrToInt(argv[1]), nil, nil, @ahc_echo, nil, + MHD_OPTION_CONNECTION_TIMEOUT, 120, + MHD_OPTION_THREAD_POOL_SIZE, NUMBER_OF_THREADS, + MHD_OPTION_URI_LOG_CALLBACK, @uri_logger_cb, nil, + MHD_OPTION_NOTIFY_COMPLETED, @completed_callback, nil, + MHD_OPTION_CONNECTION_LIMIT, 1000, + MHD_OPTION_END); + if d = nil then + Halt(1); + ReadLn; + MHD_stop_daemon(d); + MHD_destroy_response(response); + for i := 0 to SMALL do + if 0 <> small_deltas[i] then + WriteLn(stdout, Format('D: %d %u', [i, small_deltas[i]])); +end. + diff --git a/packages/libmicrohttpd/examples/benchmark_https.pp b/packages/libmicrohttpd/examples/benchmark_https.pp new file mode 100644 index 0000000000..32496255c0 --- /dev/null +++ b/packages/libmicrohttpd/examples/benchmark_https.pp @@ -0,0 +1,182 @@ +(* + This file is part of libmicrohttpd + Copyright (C) 2007, 2013 Christian Grothoff (and other contributing authors) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*) +(** + * @file benchmark_https.pp (Original: benchmark_https.c) + * @brief minimal code to benchmark MHD GET performance with HTTPS + * @author Christian Grothoff / Silvio Clécio + *) + +program benchmark_https; + +{$mode objfpc}{$H+} +{$MACRO ON} +{$IF DEFINED(CPU_COUNT) and (CPU_COUNT + 0) < 2} + {$UNDEF CPU_COUNT} +{$ENDIF} +{$IF NOT DEFINED(CPU_COUNT)} + {$DEFINE CPU_COUNT := 2} +{$ENDIF} + +uses +{$IFDEF MSWINDOWS} + WinSock2, +{$ELSE} + BaseUnix, Unix, +{$ENDIF} + cmem, sysutils, cutils, libmicrohttpd; + +const + PAGE: Pcchar = '<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>'; + SMALL = 1024 * 128; + NUMBER_OF_THREADS = CPU_COUNT; + +var + small_deltas: array[0..SMALL] of cuint; + response: PMHD_Response; + + procedure completed_callback(cls: Pointer; connection: PMHD_Connection; + con_cls: PPointer; toe: MHD_RequestTerminationCode); cdecl; + var + tv: ptimeval; + tve: timeval; + delta: cuint64; + begin + tv := con_cls^; + if nil = tv then + Exit; + fpgettimeofday(@tve, nil); + delta := 0; + if tve.tv_usec >= tv^.tv_usec then + delta += (tve.tv_sec - tv^.tv_sec) * 1000000 + + (tve.tv_usec - tv^.tv_usec) + else + delta += (tve.tv_sec - tv^.tv_sec) * 1000000 - + tv^.tv_usec + tve.tv_usec; + if delta < SMALL then + Inc(small_deltas[delta]) + else + WriteLn(stdout, Format('D: %u 1', [delta])); + Free(tv); + end; + + function uri_logger_cb(cls: Pointer; uri: Pcchar): Pointer; cdecl; + var + tv: ptimeval; + begin + tv := Malloc(SizeOf(timeval)); + if nil <> tv then + fpgettimeofday(tv, nil); + Result := tv; + end; + + function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar; + method: Pcchar; version: Pcchar; upload_data: Pcchar; + upload_data_size: Psize_t; ptr: PPointer): cint; cdecl; + begin + if 0 <> strcomp(method, 'GET') then + Exit(MHD_NO); + Result := MHD_queue_response(connection, MHD_HTTP_OK, response); + end; + +const + srv_signed_key_pem: array[0..1674] of AnsiChar = + '-----BEGIN RSA PRIVATE KEY-----'#10+ + 'MIIEowIBAAKCAQEAvfTdv+3fgvVTKRnP/HVNG81cr8TrUP/iiyuve/THMzvFXhCW'#10+ + '+K03KwEku55QvnUndwBfU/ROzLlv+5hotgiDRNFT3HxurmhouySBrJNJv7qWp8IL'#10+ + 'q4sw32vo0fbMu5BZF49bUXK9L3kW2PdhTtSQPWHEzNrCxO+YgCilKHkY3vQNfdJ0'#10+ + '20Q5EAAEseD1YtWCIpRvJzYlZMpjYB1ubTl24kwrgOKUJYKqM4jmF4DVQp4oOK/6'#10+ + 'QYGGh1QmHRPAy3CBII6sbb+sZT9cAqU6GYQVB35lm4XAgibXV6KgmpVxVQQ69U6x'#10+ + 'yoOl204xuekZOaG9RUPId74Rtmwfi1TLbBzo2wIDAQABAoIBADu09WSICNq5cMe4'#10+ + '+NKCLlgAT1NiQpLls1gKRbDhKiHU9j8QWNvWWkJWrCya4QdUfLCfeddCMeiQmv3K'#10+ + 'lJMvDs+5OjJSHFoOsGiuW2Ias7IjnIojaJalfBml6frhJ84G27IXmdz6gzOiTIer'#10+ + 'DjeAgcwBaKH5WwIay2TxIaScl7AwHBauQkrLcyb4hTmZuQh6ArVIN6+pzoVuORXM'#10+ + 'bpeNWl2l/HSN3VtUN6aCAKbN/X3o0GavCCMn5Fa85uJFsab4ss/uP+2PusU71+zP'#10+ + 'sBm6p/2IbGvF5k3VPDA7X5YX61sukRjRBihY8xSnNYx1UcoOsX6AiPnbhifD8+xQ'#10+ + 'Tlf8oJUCgYEA0BTfzqNpr9Wxw5/QXaSdw7S/0eP5a0C/nwURvmfSzuTD4equzbEN'#10+ + 'd+dI/s2JMxrdj/I4uoAfUXRGaabevQIjFzC9uyE3LaOyR2zhuvAzX+vVcs6bSXeU'#10+ + 'pKpCAcN+3Z3evMaX2f+z/nfSUAl2i4J2R+/LQAWJW4KwRky/m+cxpfUCgYEA6bN1'#10+ + 'b73bMgM8wpNt6+fcmS+5n0iZihygQ2U2DEud8nZJL4Nrm1dwTnfZfJBnkGj6+0Q0'#10+ + 'cOwj2KS0/wcEdJBP0jucU4v60VMhp75AQeHqidIde0bTViSRo3HWKXHBIFGYoU3T'#10+ + 'LyPyKndbqsOObnsFXHn56Nwhr2HLf6nw4taGQY8CgYBoSW36FLCNbd6QGvLFXBGt'#10+ + '2lMhEM8az/K58kJ4WXSwOLtr6MD/WjNT2tkcy0puEJLm6BFCd6A6pLn9jaKou/92'#10+ + 'SfltZjJPb3GUlp9zn5tAAeSSi7YMViBrfuFiHObij5LorefBXISLjuYbMwL03MgH'#10+ + 'Ocl2JtA2ywMp2KFXs8GQWQKBgFyIVv5ogQrbZ0pvj31xr9HjqK6d01VxIi+tOmpB'#10+ + '4ocnOLEcaxX12BzprW55ytfOCVpF1jHD/imAhb3YrHXu0fwe6DXYXfZV4SSG2vB7'#10+ + 'IB9z14KBN5qLHjNGFpMQXHSMek+b/ftTU0ZnPh9uEM5D3YqRLVd7GcdUhHvG8P8Q'#10+ + 'C9aXAoGBAJtID6h8wOGMP0XYX5YYnhlC7dOLfk8UYrzlp3xhqVkzKthTQTj6wx9R'#10+ + 'GtC4k7U1ki8oJsfcIlBNXd768fqDVWjYju5rzShMpo8OCTS6ipAblKjCxPPVhIpv'#10+ + 'tWPlbSn1qj6wylstJ5/3Z+ZW5H4wIKp5jmLiioDhcP0L/Ex3Zx8O'#10+ + '-----END RSA PRIVATE KEY-----'#10; + + srv_signed_cert_pem: array[0..1138] of AnsiChar = + '-----BEGIN CERTIFICATE-----'#10+ + 'MIIDGzCCAgWgAwIBAgIES0KCvTALBgkqhkiG9w0BAQUwFzEVMBMGA1UEAxMMdGVz'#10+ + 'dF9jYV9jZXJ0MB4XDTEwMDEwNTAwMDcyNVoXDTQ1MDMxMjAwMDcyNVowFzEVMBMG'#10+ + 'A1UEAxMMdGVzdF9jYV9jZXJ0MIIBHzALBgkqhkiG9w0BAQEDggEOADCCAQkCggEA'#10+ + 'vfTdv+3fgvVTKRnP/HVNG81cr8TrUP/iiyuve/THMzvFXhCW+K03KwEku55QvnUn'#10+ + 'dwBfU/ROzLlv+5hotgiDRNFT3HxurmhouySBrJNJv7qWp8ILq4sw32vo0fbMu5BZ'#10+ + 'F49bUXK9L3kW2PdhTtSQPWHEzNrCxO+YgCilKHkY3vQNfdJ020Q5EAAEseD1YtWC'#10+ + 'IpRvJzYlZMpjYB1ubTl24kwrgOKUJYKqM4jmF4DVQp4oOK/6QYGGh1QmHRPAy3CB'#10+ + 'II6sbb+sZT9cAqU6GYQVB35lm4XAgibXV6KgmpVxVQQ69U6xyoOl204xuekZOaG9'#10+ + 'RUPId74Rtmwfi1TLbBzo2wIDAQABo3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQM'#10+ + 'MAoGCCsGAQUFBwMBMA8GA1UdDwEB/wQFAwMHIAAwHQYDVR0OBBYEFOFi4ilKOP1d'#10+ + 'XHlWCMwmVKr7mgy8MB8GA1UdIwQYMBaAFP2olB4s2T/xuoQ5pT2RKojFwZo2MAsG'#10+ + 'CSqGSIb3DQEBBQOCAQEAHVWPxazupbOkG7Did+dY9z2z6RjTzYvurTtEKQgzM2Vz'#10+ + 'GQBA+3pZ3c5mS97fPIs9hZXfnQeelMeZ2XP1a+9vp35bJjZBBhVH+pqxjCgiUflg'#10+ + 'A3Zqy0XwwVCgQLE2HyaU3DLUD/aeIFK5gJaOSdNTXZLv43K8kl4cqDbMeRpVTbkt'#10+ + 'YmG4AyEOYRNKGTqMEJXJoxD5E3rBUNrVI/XyTjYrulxbNPcMWEHKNeeqWpKDYTFo'#10+ + 'Bb01PCthGXiq/4A2RLAFosadzRa8SBpoSjPPfZ0b2w4MJpReHqKbR5+T2t6hzml6'#10+ + '4ToyOKPDmamiTuN5KzLN3cw7DQlvWMvqSOChPLnA3Q=='#10+ + '-----END CERTIFICATE-----'#10; + + +var + d: PMHD_Daemon; + i: cuint; +begin + if argc <> 2 then + begin + WriteLn(argv[0] + ' PORT'); + Halt(1); + end; + response := MHD_create_response_from_buffer(Length(PAGE), Pointer(PAGE), + MHD_RESPMEM_PERSISTENT); + d := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY or MHD_USE_SSL +{$IFDEF EPOLL_SUPPORT} + or MHD_USE_EPOLL_LINUX_ONLY or MHD_USE_EPOLL_TURBO +{$ENDIF}, + StrToInt(argv[1]), nil, nil, @ahc_echo, nil, + MHD_OPTION_CONNECTION_TIMEOUT, 120, + MHD_OPTION_THREAD_POOL_SIZE, NUMBER_OF_THREADS, + MHD_OPTION_URI_LOG_CALLBACK, @uri_logger_cb, nil, + MHD_OPTION_NOTIFY_COMPLETED, @completed_callback, nil, + MHD_OPTION_CONNECTION_LIMIT, 1000, + MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem, + MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem, + MHD_OPTION_END); + if d = nil then + Halt(1); + ReadLn; + MHD_stop_daemon(d); + MHD_destroy_response(response); + for i := 0 to SMALL do + if 0 <> small_deltas[i] then + WriteLn(stdout, Format('D: %d %u', [i, small_deltas[i]])); +end. + diff --git a/packages/libmicrohttpd/examples/chunked_example.pp b/packages/libmicrohttpd/examples/chunked_example.pp new file mode 100644 index 0000000000..338c5272f1 --- /dev/null +++ b/packages/libmicrohttpd/examples/chunked_example.pp @@ -0,0 +1,81 @@ +(* + This file is part of libmicrohttpd + Copyright (C) 2015 Christian Grothoff (and other contributing authors) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*) +(** + * @file chunked_example.pp (original: chunked_example.c) + * @brief example for generating chunked encoding with libmicrohttpd + * @author Christian Grothoff / Silvio Clécio / Gilson Nunes + *) + +program chunked_example; + +{$mode objfpc}{$H+} + +uses + sysutils, libmicrohttpd; + + function callback(cls: Pointer; pos: cuint64; buf: Pcchar; + max: size_t): ssize_t; cdecl; + begin + Result := MHD_CONTENT_READER_END_OF_STREAM; + end; + + function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar; + method: Pcchar; version: Pcchar; upload_data: Pcchar; + upload_data_size: Psize_t; ptr: PPointer): cint; cdecl; + const + aptr: cint = 0; + var + response: PMHD_Response; + ret: cint; + begin + if 0 <> strcomp(method, 'GET') then + Exit(MHD_NO); + if @aptr <> ptr^ then + begin + ptr^ := @aptr; + Exit(MHD_YES); + end; + ptr^ := nil; + response := MHD_create_response_from_callback(UInt64(MHD_SIZE_UNKNOWN), 1024, + @callback, nil, nil); + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + Result := ret; + end; + +var + d: PMHD_Daemon; +begin + if argc <> 2 then + begin + WriteLn(argv[0], ' PORT'); + Halt(1); + end; + d := MHD_start_daemon(// MHD_USE_SELECT_INTERNALLY or MHD_USE_DEBUG or MHD_USE_POLL, + MHD_USE_SELECT_INTERNALLY or MHD_USE_DEBUG, + // MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG or MHD_USE_POLL, + // MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG, + StrToInt(argv[1]), nil, nil, @ahc_echo, nil, + MHD_OPTION_CONNECTION_TIMEOUT, 120, MHD_OPTION_END); + if d = nil then + Halt(1); + ReadLn; + MHD_stop_daemon(d); +end. + diff --git a/packages/libmicrohttpd/examples/cutils.pas b/packages/libmicrohttpd/examples/cutils.pas new file mode 100644 index 0000000000..ab80880afc --- /dev/null +++ b/packages/libmicrohttpd/examples/cutils.pas @@ -0,0 +1,225 @@ +unit cutils; + +{$mode objfpc}{$H+} + +interface + +uses +{$IFDEF MSWINDOWS} + Windows, WinSock2, +{$ELSE} + BaseUnix, +{$ENDIF} + ctypes; + +const + LIB_NAME = {$IFDEF MSWINDOWS}'msvcrt'{$ELSE}'c'{$ENDIF}; +{$IFDEF UNIX} + UINT16_MAX = 65535; +{$ENDIF} + SEEK_SET = 0; + SEEK_CUR = 1; + SEEK_END = 2; +{$IFDEF MSWINDOWS} + DELTA_EPOCH_IN_MICROSECS: culonglong = 11644473600000000; +{$ENDIF} + +type +{$IFDEF UNIX} + __off_t = longint; +{$ENDIF} + Pcchar = PAnsiChar; + Ppcchar = ^Pcchar; + FILEptr = ^File; + seek_mode = longint; + open_mode = (fopenread, fopenwrite, fappendwrite); + +{$IFDEF MSWINDOWS} +function fpgettimeofday(tv: PTimeVal; tz: PTimeZone): cint; +procedure _tzset; cdecl; external LIB_NAME name '_tzset'; +function _timezone: cint; cdecl; external LIB_NAME name '_timezone'; +function _daylight: clong; cdecl; external LIB_NAME name '__daylight'; +{$ENDIF} + +{$IFDEF UNIX} +function sscanf(s: Pcchar; format: Pcchar): cint; cdecl; varargs; external LIB_NAME name 'sscanf'; +function lseek(fd: cint; offset: __off_t; whence: cint): __off_t; cdecl; external LIB_NAME name 'lseek'; +function isprint(p: Char): cint; cdecl; external LIB_NAME name 'isprint'; +function strdup(para1: Pcchar): Pcchar; cdecl; external LIB_NAME name 'strdup'; +function strchr(para1: Pcchar; para2: cint): Pcchar; cdecl; external LIB_NAME name 'strchr'; +function strstr(haystack: Pcchar; needle: Pcchar): Pcchar; cdecl; external LIB_NAME name 'strstr'; +function sprintf(s: Pcchar; format: Pcchar): cint; cdecl; varargs; external LIB_NAME name 'sprintf'; +function asprintf(resultp: Ppcchar; format: Pcchar): cint; cdecl; varargs; external LIB_NAME name 'asprintf'; +function errno: PInteger; cdecl; external LIB_NAME name '__errno_location'; +{$ENDIF} +function memset(s: pointer; c: longint; n: size_t): pointer; cdecl; external LIB_NAME name 'memset'; +function snprintf(str: Pcchar; size: size_t; format: Pcchar): cint; cdecl; varargs; external LIB_NAME Name {$IFDEF MSWINDOWS}'_snprintf'{$ELSE}'snprintf'{$ENDIF}; +function rand: cint; cdecl; external LIB_NAME name 'rand'; +function strerror(errnum: cint): Pchar; cdecl; external LIB_NAME name 'strerror'; +function strncat(a, b: Pcchar; sz: size_t): Pchar; cdecl; external LIB_NAME name 'strncat'; +function strcpy(a, b: Pcchar): Pchar; cdecl; external LIB_NAME name 'strcpy'; +function strncmp(a, b: Pcchar; sz: size_t): cint; cdecl; external LIB_NAME name 'strncmp'; + +function fopen(filename: PAnsiChar; mode: open_mode): FILEptr; +procedure fclose(fp: FILEptr); +function fseek(fp: FILEptr; recPos: longint; mode: seek_mode): longint; +function fread(buf: pointer; recSize: longint; recCount: longint; fp: FILEptr): longint; +function fwrite(buf: pointer; recSize: longint; recCount: longint; fp: FILEptr): longint; +function ftell(fp: FILEptr): LongInt; +function feof(fp: FILEptr): LongInt; + +implementation + +{$IFDEF MSWINDOWS} +function fpgettimeofday(tv: PTimeVal; tz: PTimeZone): cint; +const + tzflag: cint = 0; +var + ft: FILETIME; + tmpres: QWord = 0; +begin + if nil <> tv then + begin + GetSystemTimeAsFileTime(@ft); + tmpres := tmpres or ft.dwHighDateTime; + tmpres := tmpres shl 32; + tmpres := tmpres or ft.dwLowDateTime; + tmpres := tmpres div 10; + tmpres -= DELTA_EPOCH_IN_MICROSECS; + tv^.tv_sec := clong(tmpres div culong(1000000)); + tv^.tv_usec := clong(tmpres mod culong(1000000)); + end; + if nil <> tz then + begin + if tzflag <> 1 then + begin + _tzset; + Inc(tzflag); + end; + tz^.tz_minuteswest := _timezone div 60; + tz^.tz_dsttime := _daylight; + end; + Result := 0; +end; +{$ENDIF} + +function fopen(filename: PAnsiChar; mode: open_mode): FILEptr; +var + fp: FILEptr; + OldFileMode: Byte; +begin + fp := nil; + OldFileMode := FileMode; + GetMem(fp, SizeOf(File)); + Assign(fp^, StrPas(filename)); +{$PUSH}{$I-} + case mode of + fopenread: + begin + FileMode := 0; + Reset(fp^, 1); + end; + fopenwrite: + begin + FileMode := 1; + ReWrite(fp^, 1); + end; + fappendwrite: + begin + FileMode := 2; + Reset(fp^, 1); + if IOResult = 2 then + ReWrite(fp^, 1); + Seek(fp^, FileSize(fp^)); + end; + end; + FileMode := OldFileMode; +{$POP} + if IOResult <> 0 then + begin + FreeMem(fp, SizeOf(File)); + fp := nil; + end; + fopen := fp; +end; + +procedure fclose(fp : FILEptr); +begin + if Assigned(fp) then + begin +{$PUSH}{$I-} + Close(fp^); +{$POP} + if IOresult = 0 then + FreeMem(fp, SizeOf(File)); + end; +end; + +function fread(buf: Pointer; recSize: LongInt; recCount: LongInt; + fp : FILEptr): LongInt; +var + totalSize, readcount : LongInt; +begin + if Assigned(buf) then + begin + totalSize := recCount * LongInt(recSize); +{$PUSH}{$I-}{$HINTS OFF} + BlockRead(fp^, buf^, totalSize, readcount); + if readcount <> totalSize then + fread := readcount div recSize + else + fread := recCount; +{$POP} + end + else + fread := 0; +end; + +function fwrite(buf: Pointer; recSize: LongInt; recCount: LongInt; + fp: FILEptr) : LongInt; +var + totalSize, written: LongInt; +begin + if Assigned(buf) then + begin + totalSize := recCount * LongInt(recSize); +{$PUSH}{$I-}{$HINTS OFF} + BlockWrite(fp^, buf^, totalSize, written); + if written <> totalSize then + fwrite := written div recSize + else + fwrite := recCount; +{$POP} + end + else + fwrite := 0; +end; + +function fseek(fp: FILEptr; recPos: LongInt; mode: seek_mode): LongInt; +begin +{$PUSH}{$I-} + case mode of + SEEK_SET: Seek(fp^, recPos); + SEEK_CUR: Seek(fp^, FilePos(fp^) + recPos); + SEEK_END: Seek(fp^, FileSize(fp^) - 1 - recPos); + end; +{$POP} + fseek := IOResult; +end; + +function ftell(fp: FILEptr): LongInt; +begin + ftell := FilePos(fp^); +end; + +function feof(fp: FILEptr): LongInt; +begin + feof := 0; + if Assigned(fp) then + if eof(fp^) then + feof := 1 + else + feof := 0; +end; + +end. diff --git a/packages/libmicrohttpd/examples/demo.pp b/packages/libmicrohttpd/examples/demo.pp new file mode 100644 index 0000000000..f327473297 --- /dev/null +++ b/packages/libmicrohttpd/examples/demo.pp @@ -0,0 +1,811 @@ +(* + This file is part of libmicrohttpd + Copyright (C) 2013 Christian Grothoff (and other contributing authors) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*) + +(** + * @file demo.pp (Original: demo.c) + * @brief complex demonstration site: create directory index, offer + * upload via form and HTTP POST, download with mime type detection + * and error reporting (403, etc.) --- and all of this with + * high-performance settings (large buffers, thread pool). + * If you want to benchmark MHD, this code should be used to + * run tests against. Note that the number of threads may need + * to be adjusted depending on the number of available cores. + * @author Christian Grothoff + *) + +program demo; + +{$mode objfpc}{$H+} +{$MACRO ON} +{$IF DEFINED(CPU_COUNT) AND (CPU_COUNT + 0) < 2} + {$UNDEF CPU_COUNT} +{$ENDIF} +{$IF NOT DEFINED(CPU_COUNT)} + {$DEFINE CPU_COUNT := 2} +{$ENDIF} + +uses + sysutils, pthreads, ctypes, BaseUnix, cmem, cutils, libmicrohttpd; + +type +{$i magic.inc} + +const + + (** + * Number of threads to run in the thread pool. Should (roughly) match + * the number of cores on your system. + *) + NUMBER_OF_THREADS = CPU_COUNT; + + (** + * How many bytes of a file do we give to libmagic to determine the mime type? + * 16k might be a bit excessive, but ought not hurt performance much anyway, + * and should definitively be on the safe side. + *) + MAGIC_HEADER_SIZE = 16 * 1024; + + (** + * Page returned for file-not-found. + *) + FILE_NOT_FOUND_PAGE: Pcchar = '<html><head><title>File not found</title></head><body>File not found</body></html>'; + + (** + * Page returned for internal errors. + *) + INTERNAL_ERROR_PAGE: Pcchar = '<html><head><title>Internal error</title></head><body>Internal error</body></html>'; + + + (** + * Page returned for refused requests. + *) + REQUEST_REFUSED_PAGE: Pcchar = '<html><head><title>Request refused</title></head><body>Request refused (file exists?)</body></html>'; + + (** + * Head of index page. + *) + INDEX_PAGE_HEADER = '<html>'#10'<head><title>Welcome</title></head>'#10'<body>'#10+ + '<h1>Upload</h1>'#10+ + '<form method="POST" enctype="multipart/form-data" action="/">'#10+ + '<dl><dt>Content type:</dt><dd>'+ + '<input type="radio" name="category" value="books">Book</input>'+ + '<input type="radio" name="category" value="images">Image</input>'+ + '<input type="radio" name="category" value="music">Music</input>'+ + '<input type="radio" name="category" value="software">Software</input>'+ + '<input type="radio" name="category" value="videos">Videos</input>'#10+ + '<input type="radio" name="category" value="other" checked>Other</input></dd>'+ + '<dt>Language:</dt><dd>'+ + '<input type="radio" name="language" value="no-lang" checked>none</input>'+ + '<input type="radio" name="language" value="en">English</input>'+ + '<input type="radio" name="language" value="de">German</input>'+ + '<input type="radio" name="language" value="fr">French</input>'+ + '<input type="radio" name="language" value="es">Spanish</input></dd>'#10+ + '<dt>File:</dt><dd>'+ + '<input type="file" name="upload"/></dd></dl>'+ + '<input type="submit" value="Send!"/>'#10+ + '</form>'#10+ + '<h1>Download</h1>'#10+ + '<ol>'#10; + + (** + * Footer of index page. + *) + INDEX_PAGE_FOOTER = '</ol>'#10'</body>'#10'</html>'; + + (** + * NULL-terminated array of supported upload categories. Should match HTML + * in the form. + *) + categories: array[0..6] of Pcchar = ( + 'books', + 'images', + 'music', + 'software', + 'videos', + 'other', + nil + ); + +type + + (** + * Specification of a supported language. + *) + Language = packed record + (** + * Directory name for the language. + *) + dirname: Pcchar; + + (** + * Long name for humans. + *) + longname: Pcchar; + end; + PLanguage = ^Language; + +const + (** + * NULL-terminated array of supported upload categories. Should match HTML + * in the form. + *) + languages: array[0..5] of Language = ( + (dirname: 'no-lang'; longname: 'No language specified'), + (dirname: 'en'; longname: 'English'), + (dirname: 'de'; longname: 'German'), + (dirname: 'fr'; longname: 'French'), + (dirname: 'es'; longname: 'Spanish'), + (dirname: nil; longname: nil) + ); + +var + (** + * Response returned if the requested file does not exist (or is not accessible). + *) + file_not_found_response: PMHD_Response; + + (** + * Response returned for internal errors. + *) + internal_error_response: PMHD_Response; + + (** + * Response returned for '/' (GET) to list the contents of the directory and allow upload. + *) + cached_directory_response: PMHD_Response; + + (** + * Response returned for refused uploads. + *) + request_refused_response: PMHD_Response; + + (** + * Mutex used when we update the cached directory response object. + *) + mutex: pthread_mutex_t; + + (** + * Global handle to MAGIC data. + *) + magic: magic_t; + + (** + * Mark the given response as HTML for the brower. + * + * @param response response to mark + *) + procedure mark_as_html(response: PMHD_Response); + begin + MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, 'text/html'); + end; + + (** + * Replace the existing 'cached_directory_response' with the + * given response. + * + * @param response new directory response + *) + procedure update_cached_response(response: PMHD_Response); + begin + pthread_mutex_lock(@mutex); + if nil <> cached_directory_response then + MHD_destroy_response(cached_directory_response); + cached_directory_response := response; + pthread_mutex_unlock(@mutex); + end; + +type + (** + * Context keeping the data for the response we're building. + *) + ResponseDataContext = packed record + (** + * Response data string. + *) + buf: Pcchar; + + (** + * Number of bytes allocated for 'buf'. + *) + buf_len: size_t; + + (** + * Current position where we append to 'buf'. Must be smaller or equal to 'buf_len'. + *) + off: size_t; + end; + PResponseDataContext = ^ResponseDataContext; + + (** + * Create a listing of the files in 'dirname' in HTML. + * + * @param rdc where to store the list of files + * @param dirname name of the directory to list + * @return MHD_YES on success, MHD_NO on error + *) + function list_directory(rdc: PResponseDataContext; dirname: Pcchar): cint; cdecl; + var + fullname: array[0..PATH_MAX] of AnsiChar; + sbuf: stat; + dir: pDir; + de: pDirent; + r: Pointer; + begin + dir := FpOpendir(dirname); + if nil = dir then + Exit(MHD_NO); + while True do + begin + de := FpReaddir(dir^); + if de = nil then + Break; + if '.' = de^.d_name[0] then + Continue; + if SizeOf(fullname) <= size_t( + snprintf(fullname, SizeOf(fullname), '%s/%s', dirname, de^.d_name)) then + Continue; (* ugh, file too long? how can this be!? *) + if 0 <> FpStat(PAnsiChar(fullname), sbuf) then + Continue; (* ugh, failed to 'stat' *) + if not fpS_ISREG(sbuf.st_mode) then + Continue; (* not a regular file, skip *) + if rdc^.off + 1024 > rdc^.buf_len then + begin + if (2 * rdc^.buf_len + 1024) < rdc^.buf_len then + Break; (* more than SIZE_T _index_ size? Too big for us *) + rdc^.buf_len := 2 * rdc^.buf_len + 1024; + r := ReAlloc(rdc^.buf, rdc^.buf_len); + if nil = r then + Break; (* out of memory *) + rdc^.buf := r; + end; + rdc^.off += snprintf(@rdc^.buf[rdc^.off], rdc^.buf_len - rdc^.off, + '<li><a href="/%s">%s</a></li>'#10, fullname, de^.d_name); + end; + FpClosedir(dir^); + Result := MHD_YES; + end; + + (** + * Re-scan our local directory and re-build the index. + *) + procedure update_directory; + const + initial_allocation: size_t = 32 * 1024; (* initial size for response buffer *) + var + response: PMHD_Response; + rdc: ResponseDataContext; + language_idx: cuint; + category_idx: cuint; + language: PLanguage; + category: Pcchar; + dir_name: array[0..128] of AnsiChar; + sbuf: stat; + begin + rdc.buf_len := initial_allocation; + rdc.buf := Malloc(rdc.buf_len); + if nil = rdc.buf then + begin + update_cached_response(nil); + Exit; + end; + rdc.off := snprintf(rdc.buf, rdc.buf_len, '%s', INDEX_PAGE_HEADER); + language_idx := 0; + while True do + begin + try + if languages[language_idx].dirname = nil then + Break; + language := @languages[language_idx]; + if 0 <> FpStat(language^.dirname, sbuf) then + Continue; (* empty *) + (* we ensured always +1k room, filenames are ~256 bytes, + so there is always still enough space for the header + without need for an additional reallocation check. *) + rdc.off += snprintf(@rdc.buf[rdc.off], rdc.buf_len - rdc.off, + '<h2>%s</h2>'#10, language^.longname); + category_idx := 0; + while True do + begin + try + if categories[category_idx] = nil then + Break; + category := categories[category_idx]; + snprintf(dir_name, sizeof(dir_name), '%s/%s', language^.dirname, category); + if 0 <> FpStat(PAnsiChar(dir_name), sbuf) then + Continue; (* empty *) + (* we ensured always +1k room, filenames are ~256 bytes, + so there is always still enough space for the header + without need for an additional reallocation check. *) + rdc.off += snprintf(@rdc.buf[rdc.off], rdc.buf_len - rdc.off, + '<h3>%s</h3>'#10, category); + if MHD_NO = list_directory(@rdc, dir_name) then + begin + Free(rdc.buf); + update_cached_response(nil); + Exit; + end; + finally + Inc(category_idx); + end; + end; + finally + Inc(language_idx); + end; + end; + (* we ensured always +1k room, filenames are ~256 bytes, + so there is always still enough space for the footer + without need for a final reallocation check. *) + rdc.off += snprintf(@rdc.buf[rdc.off], rdc.buf_len - rdc.off, '%s', + INDEX_PAGE_FOOTER); + initial_allocation := rdc.buf_len; (* remember for next time *) + response := MHD_create_response_from_buffer(rdc.off, rdc.buf, + MHD_RESPMEM_MUST_FREE); + mark_as_html(response); +{$IFDEF FORCE_CLOSE} + MHD_add_response_header (response, MHD_HTTP_HEADER_CONNECTION, 'close'); +{$ENDIF} + update_cached_response(response); + end; + +type + (** + * Context we keep for an upload. + *) + UploadContext = packed record + (** + * Handle where we write the uploaded file to. + *) + fd: cint; + + (** + * Name of the file on disk (used to remove on errors). + *) + filename: Pcchar; + + (** + * Language for the upload. + *) + language: Pcchar; + + (** + * Category for the upload. + *) + category: Pcchar; + + (** + * Post processor we're using to process the upload. + *) + pp: PMHD_PostProcessor; + + (** + * Handle to connection that we're processing the upload for. + *) + connection: PMHD_Connection; + + (** + * Response to generate, NULL to use directory. + *) + response: PMHD_Response; + end; + PUploadContext = ^UploadContext; + + (** + * Append the 'size' bytes from 'data' to '*ret', adding + * 0-termination. If '*ret' is NULL, allocate an empty string first. + * + * @param ret string to update, NULL or 0-terminated + * @param data data to append + * @param size number of bytes in 'data' + * @return MHD_NO on allocation failure, MHD_YES on success + *) + function do_append(ret: Ppcchar; data: Pcchar; size: size_t): cint; cdecl; + var + buf: Pcchar; + old_len: size_t; + begin + if nil = ret^ then + old_len := 0 + else + old_len := strlen(ret^); + buf := Malloc(old_len + size + 1); + if nil = buf then + Exit(MHD_NO); + Move(ret^^, buf, old_len); + if nil <> ret^ then + Free(ret^); + Move(data^, buf[old_len], size); + buf[old_len + size] := #0; + ret^ := buf; + Result := MHD_YES; + end; + + (** + * Iterator over key-value pairs where the value + * maybe made available in increments and/or may + * not be zero-terminated. Used for processing + * POST data. + * + * @param cls user-specified closure + * @param kind type of the value, always MHD_POSTDATA_KIND when called from MHD + * @param key 0-terminated key for the value + * @param filename name of the uploaded file, NULL if not known + * @param content_type mime-type of the data, NULL if not known + * @param transfer_encoding encoding of the data, NULL if not known + * @param data pointer to size bytes of data at the + * specified offset + * @param off offset of data in the overall value + * @param size number of bytes in data available + * @return MHD_YES to continue iterating, + * MHD_NO to abort the iteration + *) + function process_upload_data(cls: Pointer; kind: MHD_ValueKind; key: Pcchar; + filename: Pcchar; content_type: Pcchar; transfer_encoding: Pcchar; + data: Pcchar; off: cuint64; size: size_t): cint; cdecl; + var + uc: PUploadContext; + i: cint; + fn: array[0..PATH_MAX] of AnsiChar; + begin + uc := cls; + if 0 = strcomp(key, 'category') then + Exit(do_append(@uc^.category, data, size)); + if 0 = strcomp(key, 'language') then + Exit(do_append(@uc^.language, data, size)); + if 0 <> strcomp(key, 'upload') then + begin + WriteLn(stderr, Format('Ignoring unexpected form value `%s''', [key])); + Exit(MHD_YES); (* ignore *) + end; + if nil = filename then + begin + WriteLn(stderr, 'No filename, aborting upload'); + Exit(MHD_NO); (* no filename, error *) + end; + if (nil = uc^.category) or (nil = uc^.language) then + begin + WriteLn(stderr, Format('Missing form data for upload `%s''', [filename])); + uc^.response := request_refused_response; + Exit(MHD_NO); + end; + if -1 = uc^.fd then + begin + if (nil <> strstr(filename, '..')) or (nil <> strchr(filename, Ord('/'))) or + (nil <> strchr(filename, Ord('\'))) then + begin + uc^.response := request_refused_response; + Exit(MHD_NO); + end; + (* create directories -- if they don't exist already *) +{$IFDEF MSWINDOWS} + FpMkdir(uc^.language); +{$ELSE} + FpMkdir(uc^.language, S_IRWXU); +{$ENDIF} + snprintf(fn, SizeOf(fn), '%s/%s', uc^.language, uc^.category); +{$IFDEF MSWINDOWS} + FpMkdir(fn); +{$ELSE} + FpMkdir(PAnsiChar(fn), S_IRWXU); +{$ENDIF} + (* open file *) + snprintf(fn, sizeof(fn), '%s/%s/%s', uc^.language, uc^.category, filename); + for i := strlen(fn) - 1 downto 0 do + if isprint(fn[i]) = 1 then + fn[i] := '_'; + uc^.fd := FpOpen(PAnsiChar(fn), O_CREAT or O_EXCL +{$IFDEF O_LARGEFILE} + or O_LARGEFILE +{$ENDIF} + or O_WRONLY, S_IRUSR or S_IWUSR); + if -1 = uc^.fd then + begin + WriteLn(stderr, Format('Error opening file `%s'' for upload: %s', + [fn, strerror(errno^)])); + uc^.response := request_refused_response; + Exit(MHD_NO); + end; + uc^.filename := strdup(fn); + end; + if (0 <> size) and (size <> size_t(FpWrite(uc^.fd, data, size))) then + begin + (* write failed; likely: disk full *) + WriteLn(stderr, Format('Error writing to file `%s'': %s', [uc^.filename, + strerror(errno^)])); + uc^.response := internal_error_response; + FpClose(uc^.fd); + uc^.fd := -1; + if nil <> uc^.filename then + begin + FpUnlink(uc^.filename); + Free(uc^.filename); + uc^.filename := nil; + end; + Exit(MHD_NO); + end; + Exit(MHD_YES); + end; + + (** + * Function called whenever a request was completed. + * Used to clean up 'struct UploadContext' objects. + * + * @param cls client-defined closure, NULL + * @param connection connection handle + * @param con_cls value as set by the last call to + * the MHD_AccessHandlerCallback, points to NULL if this was + * not an upload + * @param toe reason for request termination + *) + procedure response_completed_callback(cls: Pointer; connection: PMHD_Connection; + con_cls: PPointer; toe: MHD_RequestTerminationCode); cdecl; + var + uc: PUploadContext; + begin + uc := con_cls^; + if nil = uc then + Exit; (* this request wasn't an upload request *) + if nil <> uc^.pp then + begin + MHD_destroy_post_processor(uc^.pp); + uc^.pp := nil; + end; + if -1 <> uc^.fd then + begin + FpClose(uc^.fd); + if nil <> uc^.filename then + begin + WriteLn(stderr, Format( + 'Upload of file `%s'' failed (incomplete or aborted), removing file.', + [uc^.filename])); + FpUnlink(uc^.filename); + end; + end; + if nil <> uc^.filename then + Free(uc^.filename); + Free(uc); + end; + + (** + * Return the current directory listing. + * + * @param connection connection to return the directory for + * @return MHD_YES on success, MHD_NO on error + *) + function return_directory_response(connection: PMHD_Connection): cint; + var + ret: cint; + begin + pthread_mutex_lock(@mutex); + if nil = cached_directory_response then + ret := MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, + internal_error_response) + else + ret := MHD_queue_response(connection, MHD_HTTP_OK, + cached_directory_response); + pthread_mutex_unlock(@mutex); + Result := ret; + end; + + (** + * Main callback from MHD, used to generate the page. + * + * @param cls NULL + * @param connection connection handle + * @param url requested URL + * @param method GET, PUT, POST, etc. + * @param version HTTP version + * @param upload_data data from upload (PUT/POST) + * @param upload_data_size number of bytes in "upload_data" + * @param ptr our context + * @return MHD_YES on success, MHD_NO to drop connection + *) + function generate_page(cls: Pointer; connection: PMHD_Connection; url: Pcchar; + method: Pcchar; version: Pcchar; upload_data: Pcchar; + upload_data_size: Psize_t; ptr: PPointer): cint; cdecl; + var + response: PMHD_Response; + ret: cint; + fd: cint; + buf: stat; + (* should be file download *) + file_data: array[0..MAGIC_HEADER_SIZE] of AnsiChar; + got: ssize_t ; + mime: Pcchar; + uc: PUploadContext; + begin + if 0 <> strcomp(url, '/') then + begin + if (0 <> strcomp(method, MHD_HTTP_METHOD_GET)) and + (0 <> strcomp(method, MHD_HTTP_METHOD_HEAD)) then + Exit(MHD_NO); (* unexpected method (we're not polite...) *) + if (0 = FpStat(@url[1], buf)) and (nil = strstr(@url[1], '..')) and + ('/' <> url[1]) then + fd := FpOpen(@url[1], O_RDONLY) + else + fd := -1; + if -1 = fd then + Exit(MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, + file_not_found_response)); + (* read beginning of the file to determine mime type *) + got := FpRead(fd, file_data, SizeOf(file_data)); + if -1 <> got then + mime := magic_buffer(magic, Pcchar(file_data), got) + else + mime := nil; + lseek(fd, 0, SEEK_SET); + response := MHD_create_response_from_fd(buf.st_size, fd); + if nil = response then + begin + (* internal error (i.e. out of memory) *) + FpClose(fd); + Exit(MHD_NO); + end; + (* add mime type if we had one *) + if nil <> mime then + MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, mime); + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + Exit(ret); + end; + if 0 = strcomp(method, MHD_HTTP_METHOD_POST) then + begin + (* upload! *) + uc := ptr^; + if nil = uc then + begin + uc := Malloc(SizeOf(UploadContext)); + if nil = uc then + Exit(MHD_NO); (* out of memory, close connection *) + memset(uc, 0, SizeOf(UploadContext)); + uc^.fd := -1; + uc^.connection := connection; + uc^.pp := MHD_create_post_processor(connection, 64 * 1024 (* buffer size *), + @process_upload_data, uc); + if nil = uc^.pp then + begin + (* out of memory, close connection *) + Free(uc); + Exit(MHD_NO); + end; + ptr^ := uc; + Exit(MHD_YES); + end; + if 0 <> upload_data_size^ then + begin + if nil = uc^.response then + MHD_post_process(uc^.pp, upload_data, upload_data_size^); + upload_data_size^ := 0; + Exit(MHD_YES); + end; + (* end of upload, finish it! *) + MHD_destroy_post_processor(uc^.pp); + uc^.pp := nil; + if -1 <> uc^.fd then + begin + FpClose(uc^.fd); + uc^.fd := -1; + end; + if nil <> uc^.response then + Exit(MHD_queue_response(connection, MHD_HTTP_FORBIDDEN, uc^.response)) + else + begin + update_directory; + Exit(return_directory_response(connection)); + end; + end; + if (0 = strcomp(method, MHD_HTTP_METHOD_GET)) or + (0 = strcomp(method, MHD_HTTP_METHOD_HEAD)) then + Exit(return_directory_response(connection)); + (* unexpected request, refuse *) + Result := MHD_queue_response(connection, MHD_HTTP_FORBIDDEN, + request_refused_response); + end; + + (** + * Function called if we get a SIGPIPE. Does nothing. + * + * @param sig will be SIGPIPE (ignored) + *) + procedure catcher(signal: longint; info: psiginfo; context: psigcontext); cdecl; + begin + (* do nothing *) + end; + + (** + * setup handlers to ignore SIGPIPE. + *) + procedure ignore_sigpipe; + var + oldsig: sigactionrec; + sig: sigactionrec; + begin + sig.sa_handler := @catcher; + FpsigEmptySet(sig.sa_mask); + {$IFDEF SA_INTERRUPT} + sig.sa_flags := SA_INTERRUPT; (* SunOS *) + {$ELSE} + sig.sa_flags := SA_RESTART; + {$ENDIF} + if 0 <> FPSigaction(SIGPIPE, @sig, @oldsig) then + WriteLn(stderr, Format('Failed to install SIGPIPE handler: %s', + [strerror(errno^)])); + end; + + (** + * Entry point to demo. Note: this HTTP server will make all + * files in the current directory and its subdirectories available + * to anyone. Press ENTER to stop the server once it has started. + * + * @param argc number of arguments in argv + * @param argv first and only argument should be the port number + * @return 0 on success + *) +var + d: PMHD_Daemon; + port: cuint; +begin + if (argc <> 2) or (1 <> sscanf(argv[1], '%u', @port)) or + (UINT16_MAX < port) then + begin + WriteLn(stderr, argv[0], ' PORT'); + Halt(1); + end; + ignore_sigpipe; + magic := magic_open(MAGIC_MIME_TYPE); + magic_load(magic, nil); + pthread_mutex_init(@mutex, nil); + file_not_found_response := MHD_create_response_from_buffer( + strlen(FILE_NOT_FOUND_PAGE), FILE_NOT_FOUND_PAGE, + MHD_RESPMEM_PERSISTENT); + mark_as_html(file_not_found_response); + request_refused_response := MHD_create_response_from_buffer( + strlen(REQUEST_REFUSED_PAGE), REQUEST_REFUSED_PAGE, + MHD_RESPMEM_PERSISTENT); + mark_as_html(request_refused_response); + internal_error_response := MHD_create_response_from_buffer( + strlen(INTERNAL_ERROR_PAGE), INTERNAL_ERROR_PAGE, + MHD_RESPMEM_PERSISTENT); + mark_as_html(internal_error_response); + update_directory; + d := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY or MHD_USE_DEBUG +{$IFDEF EPOLL_SUPPORT} + or MHD_USE_EPOLL_LINUX_ONLY +{$ENDIF}, + port, nil, nil, @generate_page, nil, + MHD_OPTION_CONNECTION_MEMORY_LIMIT, size_t(256 * 1024), +{$IFDEF PRODUCTION} + MHD_OPTION_PER_IP_CONNECTION_LIMIT, cuint(64), +{$ENDIF} + MHD_OPTION_CONNECTION_TIMEOUT, cuint(120 (* seconds *)), + MHD_OPTION_THREAD_POOL_SIZE, cuint(NUMBER_OF_THREADS), + MHD_OPTION_NOTIFY_COMPLETED, @response_completed_callback, nil, + MHD_OPTION_END); + if nil = d then + Halt(1); + WriteLn(stderr, 'HTTP server running. Press ENTER to stop the server'); + ReadLn; + MHD_stop_daemon(d); + MHD_destroy_response(file_not_found_response); + MHD_destroy_response(request_refused_response); + MHD_destroy_response(internal_error_response); + update_cached_response(nil); + pthread_mutex_destroy(@mutex); + magic_close(magic); +end. + diff --git a/packages/libmicrohttpd/examples/demo_https.pp b/packages/libmicrohttpd/examples/demo_https.pp new file mode 100644 index 0000000000..c4fd089903 --- /dev/null +++ b/packages/libmicrohttpd/examples/demo_https.pp @@ -0,0 +1,865 @@ +(* + This file is part of libmicrohttpd + Copyright (C) 2013 Christian Grothoff (and other contributing authors) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*) + +(** + * @file demo_https.pp (Original: demo_https.c) + * @brief complex demonstration site: create directory index, offer + * upload via form and HTTP POST, download with mime type detection + * and error reporting (403, etc.) --- and all of this with + * high-performance settings (large buffers, thread pool). + * If you want to benchmark MHD, this code should be used to + * run tests against. Note that the number of threads may need + * to be adjusted depending on the number of available cores. + * Logic is identical to demo.pp, just adds HTTPS support. + * @author Christian Grothoff + *) + +program demo_https; + +{$mode objfpc}{$H+} +{$MACRO ON} +{$IF DEFINED(CPU_COUNT) AND (CPU_COUNT + 0) < 2} + {$UNDEF CPU_COUNT} +{$ENDIF} +{$IF NOT DEFINED(CPU_COUNT)} + {$DEFINE CPU_COUNT := 2} +{$ENDIF} + +uses + sysutils, pthreads, ctypes, BaseUnix, cmem, cutils, libmicrohttpd; + +type +{$i magic.inc} + +const + + (** + * Number of threads to run in the thread pool. Should (roughly) match + * the number of cores on your system. + *) + NUMBER_OF_THREADS = CPU_COUNT; + + (** + * How many bytes of a file do we give to libmagic to determine the mime type? + * 16k might be a bit excessive, but ought not hurt performance much anyway, + * and should definitively be on the safe side. + *) + MAGIC_HEADER_SIZE = 16 * 1024; + + (** + * Page returned for file-not-found. + *) + FILE_NOT_FOUND_PAGE: Pcchar = '<html><head><title>File not found</title></head><body>File not found</body></html>'; + + (** + * Page returned for internal errors. + *) + INTERNAL_ERROR_PAGE: Pcchar = '<html><head><title>Internal error</title></head><body>Internal error</body></html>'; + + + (** + * Page returned for refused requests. + *) + REQUEST_REFUSED_PAGE: Pcchar = '<html><head><title>Request refused</title></head><body>Request refused (file exists?)</body></html>'; + + (** + * Head of index page. + *) + INDEX_PAGE_HEADER = '<html>'#10'<head><title>Welcome</title></head>'#10'<body>'#10+ + '<h1>Upload</h1>'#10+ + '<form method="POST" enctype="multipart/form-data" action="/">'#10+ + '<dl><dt>Content type:</dt><dd>'+ + '<input type="radio" name="category" value="books">Book</input>'+ + '<input type="radio" name="category" value="images">Image</input>'+ + '<input type="radio" name="category" value="music">Music</input>'+ + '<input type="radio" name="category" value="software">Software</input>'+ + '<input type="radio" name="category" value="videos">Videos</input>'#10+ + '<input type="radio" name="category" value="other" checked>Other</input></dd>'+ + '<dt>Language:</dt><dd>'+ + '<input type="radio" name="language" value="no-lang" checked>none</input>'+ + '<input type="radio" name="language" value="en">English</input>'+ + '<input type="radio" name="language" value="de">German</input>'+ + '<input type="radio" name="language" value="fr">French</input>'+ + '<input type="radio" name="language" value="es">Spanish</input></dd>'#10+ + '<dt>File:</dt><dd>'+ + '<input type="file" name="upload"/></dd></dl>'+ + '<input type="submit" value="Send!"/>'#10+ + '</form>'#10+ + '<h1>Download</h1>'#10+ + '<ol>'#10; + + (** + * Footer of index page. + *) + INDEX_PAGE_FOOTER = '</ol>'#10'</body>'#10'</html>'; + + (** + * NULL-terminated array of supported upload categories. Should match HTML + * in the form. + *) + categories: array[0..6] of Pcchar = ( + 'books', + 'images', + 'music', + 'software', + 'videos', + 'other', + nil + ); + +type + + (** + * Specification of a supported language. + *) + Language = packed record + (** + * Directory name for the language. + *) + dirname: Pcchar; + + (** + * Long name for humans. + *) + longname: Pcchar; + end; + PLanguage = ^Language; + +const + (** + * NULL-terminated array of supported upload categories. Should match HTML + * in the form. + *) + languages: array[0..5] of Language = ( + (dirname: 'no-lang'; longname: 'No language specified'), + (dirname: 'en'; longname: 'English'), + (dirname: 'de'; longname: 'German'), + (dirname: 'fr'; longname: 'French'), + (dirname: 'es'; longname: 'Spanish'), + (dirname: nil; longname: nil) + ); + +var + (** + * Response returned if the requested file does not exist (or is not accessible). + *) + file_not_found_response: PMHD_Response; + + (** + * Response returned for internal errors. + *) + internal_error_response: PMHD_Response; + + (** + * Response returned for '/' (GET) to list the contents of the directory and allow upload. + *) + cached_directory_response: PMHD_Response; + + (** + * Response returned for refused uploads. + *) + request_refused_response: PMHD_Response; + + (** + * Mutex used when we update the cached directory response object. + *) + mutex: pthread_mutex_t; + + (** + * Global handle to MAGIC data. + *) + magic: magic_t; + + (** + * Mark the given response as HTML for the brower. + * + * @param response response to mark + *) + procedure mark_as_html(response: PMHD_Response); + begin + MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, 'text/html'); + end; + + (** + * Replace the existing 'cached_directory_response' with the + * given response. + * + * @param response new directory response + *) + procedure update_cached_response(response: PMHD_Response); + begin + pthread_mutex_lock(@mutex); + if nil <> cached_directory_response then + MHD_destroy_response(cached_directory_response); + cached_directory_response := response; + pthread_mutex_unlock(@mutex); + end; + +type + (** + * Context keeping the data for the response we're building. + *) + ResponseDataContext = packed record + (** + * Response data string. + *) + buf: Pcchar; + + (** + * Number of bytes allocated for 'buf'. + *) + buf_len: size_t; + + (** + * Current position where we append to 'buf'. Must be smaller or equal to 'buf_len'. + *) + off: size_t; + end; + PResponseDataContext = ^ResponseDataContext; + + (** + * Create a listing of the files in 'dirname' in HTML. + * + * @param rdc where to store the list of files + * @param dirname name of the directory to list + * @return MHD_YES on success, MHD_NO on error + *) + function list_directory(rdc: PResponseDataContext; dirname: Pcchar): cint; cdecl; + var + fullname: array[0..PATH_MAX] of AnsiChar; + sbuf: stat; + dir: pDir; + de: pDirent; + r: Pointer; + begin + dir := FpOpendir(dirname); + if nil = dir then + Exit(MHD_NO); + while True do + begin + de := FpReaddir(dir^); + if de = nil then + Break; + if '.' = de^.d_name[0] then + Continue; + if SizeOf(fullname) <= size_t( + snprintf(fullname, SizeOf(fullname), '%s/%s', dirname, de^.d_name)) then + Continue; (* ugh, file too long? how can this be!? *) + if 0 <> FpStat(PAnsiChar(fullname), sbuf) then + Continue; (* ugh, failed to 'stat' *) + if not fpS_ISREG(sbuf.st_mode) then + Continue; (* not a regular file, skip *) + if rdc^.off + 1024 > rdc^.buf_len then + begin + if (2 * rdc^.buf_len + 1024) < rdc^.buf_len then + Break; (* more than SIZE_T _index_ size? Too big for us *) + rdc^.buf_len := 2 * rdc^.buf_len + 1024; + r := ReAlloc(rdc^.buf, rdc^.buf_len); + if nil = r then + Break; (* out of memory *) + rdc^.buf := r; + end; + rdc^.off += snprintf(@rdc^.buf[rdc^.off], rdc^.buf_len - rdc^.off, + '<li><a href="/%s">%s</a></li>'#10, fullname, de^.d_name); + end; + FpClosedir(dir^); + Result := MHD_YES; + end; + + (** + * Re-scan our local directory and re-build the index. + *) + procedure update_directory; + const + initial_allocation: size_t = 32 * 1024; (* initial size for response buffer *) + var + response: PMHD_Response; + rdc: ResponseDataContext; + language_idx: cuint; + category_idx: cuint; + language: PLanguage; + category: Pcchar; + dir_name: array[0..128] of AnsiChar; + sbuf: stat; + begin + rdc.buf_len := initial_allocation; + rdc.buf := Malloc(rdc.buf_len); + if nil = rdc.buf then + begin + update_cached_response(nil); + Exit; + end; + rdc.off := snprintf(rdc.buf, rdc.buf_len, '%s', INDEX_PAGE_HEADER); + language_idx := 0; + while True do + begin + try + if languages[language_idx].dirname = nil then + Break; + language := @languages[language_idx]; + if 0 <> FpStat(language^.dirname, sbuf) then + Continue; (* empty *) + (* we ensured always +1k room, filenames are ~256 bytes, + so there is always still enough space for the header + without need for an additional reallocation check. *) + rdc.off += snprintf(@rdc.buf[rdc.off], rdc.buf_len - rdc.off, + '<h2>%s</h2>'#10, language^.longname); + category_idx := 0; + while True do + begin + try + if categories[category_idx] = nil then + Break; + category := categories[category_idx]; + snprintf(dir_name, sizeof(dir_name), '%s/%s', language^.dirname, category); + if 0 <> FpStat(PAnsiChar(dir_name), sbuf) then + Continue; (* empty *) + (* we ensured always +1k room, filenames are ~256 bytes, + so there is always still enough space for the header + without need for an additional reallocation check. *) + rdc.off += snprintf(@rdc.buf[rdc.off], rdc.buf_len - rdc.off, + '<h3>%s</h3>'#10, category); + if MHD_NO = list_directory(@rdc, dir_name) then + begin + Free(rdc.buf); + update_cached_response(nil); + Exit; + end; + finally + Inc(category_idx); + end; + end; + finally + Inc(language_idx); + end; + end; + (* we ensured always +1k room, filenames are ~256 bytes, + so there is always still enough space for the footer + without need for a final reallocation check. *) + rdc.off += snprintf(@rdc.buf[rdc.off], rdc.buf_len - rdc.off, '%s', + INDEX_PAGE_FOOTER); + initial_allocation := rdc.buf_len; (* remember for next time *) + response := MHD_create_response_from_buffer(rdc.off, rdc.buf, + MHD_RESPMEM_MUST_FREE); + mark_as_html(response); +{$IFDEF FORCE_CLOSE} + MHD_add_response_header (response, MHD_HTTP_HEADER_CONNECTION, 'close'); +{$ENDIF} + update_cached_response(response); + end; + +type + (** + * Context we keep for an upload. + *) + UploadContext = packed record + (** + * Handle where we write the uploaded file to. + *) + fd: cint; + + (** + * Name of the file on disk (used to remove on errors). + *) + filename: Pcchar; + + (** + * Language for the upload. + *) + language: Pcchar; + + (** + * Category for the upload. + *) + category: Pcchar; + + (** + * Post processor we're using to process the upload. + *) + pp: PMHD_PostProcessor; + + (** + * Handle to connection that we're processing the upload for. + *) + connection: PMHD_Connection; + + (** + * Response to generate, NULL to use directory. + *) + response: PMHD_Response; + end; + PUploadContext = ^UploadContext; + + (** + * Append the 'size' bytes from 'data' to '*ret', adding + * 0-termination. If '*ret' is NULL, allocate an empty string first. + * + * @param ret string to update, NULL or 0-terminated + * @param data data to append + * @param size number of bytes in 'data' + * @return MHD_NO on allocation failure, MHD_YES on success + *) + function do_append(ret: Ppcchar; data: Pcchar; size: size_t): cint; cdecl; + var + buf: Pcchar; + old_len: size_t; + begin + if nil = ret^ then + old_len := 0 + else + old_len := strlen(ret^); + buf := Malloc(old_len + size + 1); + if nil = buf then + Exit(MHD_NO); + Move(ret^^, buf, old_len); + if nil <> ret^ then + Free(ret^); + Move(data^, buf[old_len], size); + buf[old_len + size] := #0; + ret^ := buf; + Result := MHD_YES; + end; + + (** + * Iterator over key-value pairs where the value + * maybe made available in increments and/or may + * not be zero-terminated. Used for processing + * POST data. + * + * @param cls user-specified closure + * @param kind type of the value, always MHD_POSTDATA_KIND when called from MHD + * @param key 0-terminated key for the value + * @param filename name of the uploaded file, NULL if not known + * @param content_type mime-type of the data, NULL if not known + * @param transfer_encoding encoding of the data, NULL if not known + * @param data pointer to size bytes of data at the + * specified offset + * @param off offset of data in the overall value + * @param size number of bytes in data available + * @return MHD_YES to continue iterating, + * MHD_NO to abort the iteration + *) + function process_upload_data(cls: Pointer; kind: MHD_ValueKind; key: Pcchar; + filename: Pcchar; content_type: Pcchar; transfer_encoding: Pcchar; + data: Pcchar; off: cuint64; size: size_t): cint; cdecl; + var + uc: PUploadContext; + i: cint; + fn: array[0..PATH_MAX] of AnsiChar; + begin + uc := cls; + if 0 = strcomp(key, 'category') then + Exit(do_append(@uc^.category, data, size)); + if 0 = strcomp(key, 'language') then + Exit(do_append(@uc^.language, data, size)); + if 0 <> strcomp(key, 'upload') then + begin + WriteLn(stderr, Format('Ignoring unexpected form value `%s''', [key])); + Exit(MHD_YES); (* ignore *) + end; + if nil = filename then + begin + WriteLn(stderr, 'No filename, aborting upload'); + Exit(MHD_NO); (* no filename, error *) + end; + if (nil = uc^.category) or (nil = uc^.language) then + begin + WriteLn(stderr, Format('Missing form data for upload `%s''', [filename])); + uc^.response := request_refused_response; + Exit(MHD_NO); + end; + if -1 = uc^.fd then + begin + if (nil <> strstr(filename, '..')) or (nil <> strchr(filename, Ord('/'))) or + (nil <> strchr(filename, Ord('\'))) then + begin + uc^.response := request_refused_response; + Exit(MHD_NO); + end; + (* create directories -- if they don't exist already *) +{$IFDEF MSWINDOWS} + FpMkdir(uc^.language); +{$ELSE} + FpMkdir(uc^.language, S_IRWXU); +{$ENDIF} + snprintf(fn, SizeOf(fn), '%s/%s', uc^.language, uc^.category); +{$IFDEF MSWINDOWS} + FpMkdir(fn); +{$ELSE} + FpMkdir(PAnsiChar(fn), S_IRWXU); +{$ENDIF} + (* open file *) + snprintf(fn, sizeof(fn), '%s/%s/%s', uc^.language, uc^.category, filename); + for i := strlen(fn) - 1 downto 0 do + if isprint(fn[i]) = 1 then + fn[i] := '_'; + uc^.fd := FpOpen(PAnsiChar(fn), O_CREAT or O_EXCL +{$IFDEF O_LARGEFILE} + or O_LARGEFILE +{$ENDIF} + or O_WRONLY, S_IRUSR or S_IWUSR); + if -1 = uc^.fd then + begin + WriteLn(stderr, Format('Error opening file `%s'' for upload: %s', + [fn, strerror(errno^)])); + uc^.response := request_refused_response; + Exit(MHD_NO); + end; + uc^.filename := strdup(fn); + end; + if (0 <> size) and (size <> size_t(FpWrite(uc^.fd, data, size))) then + begin + (* write failed; likely: disk full *) + WriteLn(stderr, Format('Error writing to file `%s'': %s', [uc^.filename, + strerror(errno^)])); + uc^.response := internal_error_response; + FpClose(uc^.fd); + uc^.fd := -1; + if nil <> uc^.filename then + begin + FpUnlink(uc^.filename); + Free(uc^.filename); + uc^.filename := nil; + end; + Exit(MHD_NO); + end; + Exit(MHD_YES); + end; + + (** + * Function called whenever a request was completed. + * Used to clean up 'struct UploadContext' objects. + * + * @param cls client-defined closure, NULL + * @param connection connection handle + * @param con_cls value as set by the last call to + * the MHD_AccessHandlerCallback, points to NULL if this was + * not an upload + * @param toe reason for request termination + *) + procedure response_completed_callback(cls: Pointer; connection: PMHD_Connection; + con_cls: PPointer; toe: MHD_RequestTerminationCode); cdecl; + var + uc: PUploadContext; + begin + uc := con_cls^; + if nil = uc then + Exit; (* this request wasn't an upload request *) + if nil <> uc^.pp then + begin + MHD_destroy_post_processor(uc^.pp); + uc^.pp := nil; + end; + if -1 <> uc^.fd then + begin + FpClose(uc^.fd); + if nil <> uc^.filename then + begin + WriteLn(stderr, Format( + 'Upload of file `%s'' failed (incomplete or aborted), removing file.', + [uc^.filename])); + FpUnlink(uc^.filename); + end; + end; + if nil <> uc^.filename then + Free(uc^.filename); + Free(uc); + end; + + (** + * Return the current directory listing. + * + * @param connection connection to return the directory for + * @return MHD_YES on success, MHD_NO on error + *) + function return_directory_response(connection: PMHD_Connection): cint; + var + ret: cint; + begin + pthread_mutex_lock(@mutex); + if nil = cached_directory_response then + ret := MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, + internal_error_response) + else + ret := MHD_queue_response(connection, MHD_HTTP_OK, + cached_directory_response); + pthread_mutex_unlock(@mutex); + Result := ret; + end; + + (** + * Main callback from MHD, used to generate the page. + * + * @param cls NULL + * @param connection connection handle + * @param url requested URL + * @param method GET, PUT, POST, etc. + * @param version HTTP version + * @param upload_data data from upload (PUT/POST) + * @param upload_data_size number of bytes in "upload_data" + * @param ptr our context + * @return MHD_YES on success, MHD_NO to drop connection + *) + function generate_page(cls: Pointer; connection: PMHD_Connection; url: Pcchar; + method: Pcchar; version: Pcchar; upload_data: Pcchar; + upload_data_size: Psize_t; ptr: PPointer): cint; cdecl; + var + response: PMHD_Response; + ret: cint; + fd: cint; + buf: stat; + (* should be file download *) + file_data: array[0..MAGIC_HEADER_SIZE] of AnsiChar; + got: ssize_t ; + mime: Pcchar; + uc: PUploadContext; + begin + if 0 <> strcomp(url, '/') then + begin + if 0 <> strcomp(method, MHD_HTTP_METHOD_GET) then + Exit(MHD_NO); (* unexpected method (we're not polite...) *) + if (0 = FpStat(@url[1], buf)) and (nil = strstr(@url[1], '..')) and + ('/' <> url[1]) then + fd := FpOpen(@url[1], O_RDONLY) + else + fd := -1; + if -1 = fd then + Exit(MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, + file_not_found_response)); + (* read beginning of the file to determine mime type *) + got := FpRead(fd, file_data, SizeOf(file_data)); + if -1 <> got then + mime := magic_buffer(magic, Pcchar(file_data), got) + else + mime := nil; + lseek(fd, 0, SEEK_SET); + response := MHD_create_response_from_fd(buf.st_size, fd); + if nil = response then + begin + (* internal error (i.e. out of memory) *) + FpClose(fd); + Exit(MHD_NO); + end; + (* add mime type if we had one *) + if nil <> mime then + MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, mime); + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + Exit(ret); + end; + if 0 = strcomp(method, MHD_HTTP_METHOD_POST) then + begin + (* upload! *) + uc := ptr^; + if nil = uc then + begin + uc := Malloc(SizeOf(UploadContext)); + if nil = uc then + Exit(MHD_NO); (* out of memory, close connection *) + memset(uc, 0, SizeOf(UploadContext)); + uc^.fd := -1; + uc^.connection := connection; + uc^.pp := MHD_create_post_processor(connection, 64 * 1024 (* buffer size *), + @process_upload_data, uc); + if nil = uc^.pp then + begin + (* out of memory, close connection *) + Free(uc); + Exit(MHD_NO); + end; + ptr^ := uc; + Exit(MHD_YES); + end; + if 0 <> upload_data_size^ then + begin + if nil = uc^.response then + MHD_post_process(uc^.pp, upload_data, upload_data_size^); + upload_data_size^ := 0; + Exit(MHD_YES); + end; + (* end of upload, finish it! *) + MHD_destroy_post_processor(uc^.pp); + uc^.pp := nil; + if -1 <> uc^.fd then + begin + FpClose(uc^.fd); + uc^.fd := -1; + end; + if nil <> uc^.response then + Exit(MHD_queue_response(connection, MHD_HTTP_FORBIDDEN, uc^.response)) + else + begin + update_directory; + Exit(return_directory_response(connection)); + end; + end; + if 0 = strcomp(method, MHD_HTTP_METHOD_GET) then + Exit(return_directory_response(connection)); + (* unexpected request, refuse *) + Result := MHD_queue_response(connection, MHD_HTTP_FORBIDDEN, + request_refused_response); + end; + + (** + * Function called if we get a SIGPIPE. Does nothing. + * + * @param sig will be SIGPIPE (ignored) + *) + procedure catcher(signal: longint; info: psiginfo; context: psigcontext); cdecl; + begin + (* do nothing *) + end; + + (** + * setup handlers to ignore SIGPIPE. + *) + procedure ignore_sigpipe; + var + oldsig: sigactionrec; + sig: sigactionrec; + begin + sig.sa_handler := @catcher; + FpsigEmptySet(sig.sa_mask); + {$IFDEF SA_INTERRUPT} + sig.sa_flags := SA_INTERRUPT; (* SunOS *) + {$ELSE} + sig.sa_flags := SA_RESTART; + {$ENDIF} + if 0 <> FPSigaction(SIGPIPE, @sig, @oldsig) then + WriteLn(stderr, Format('Failed to install SIGPIPE handler: %s', + [strerror(errno^)])); + end; + +const + (* test server key *) + srv_signed_key_pem: array[0..1674] of AnsiChar = + '-----BEGIN RSA PRIVATE KEY-----'#10+ + 'MIIEowIBAAKCAQEAvfTdv+3fgvVTKRnP/HVNG81cr8TrUP/iiyuve/THMzvFXhCW'#10+ + '+K03KwEku55QvnUndwBfU/ROzLlv+5hotgiDRNFT3HxurmhouySBrJNJv7qWp8IL'#10+ + 'q4sw32vo0fbMu5BZF49bUXK9L3kW2PdhTtSQPWHEzNrCxO+YgCilKHkY3vQNfdJ0'#10+ + '20Q5EAAEseD1YtWCIpRvJzYlZMpjYB1ubTl24kwrgOKUJYKqM4jmF4DVQp4oOK/6'#10+ + 'QYGGh1QmHRPAy3CBII6sbb+sZT9cAqU6GYQVB35lm4XAgibXV6KgmpVxVQQ69U6x'#10+ + 'yoOl204xuekZOaG9RUPId74Rtmwfi1TLbBzo2wIDAQABAoIBADu09WSICNq5cMe4'#10+ + '+NKCLlgAT1NiQpLls1gKRbDhKiHU9j8QWNvWWkJWrCya4QdUfLCfeddCMeiQmv3K'#10+ + 'lJMvDs+5OjJSHFoOsGiuW2Ias7IjnIojaJalfBml6frhJ84G27IXmdz6gzOiTIer'#10+ + 'DjeAgcwBaKH5WwIay2TxIaScl7AwHBauQkrLcyb4hTmZuQh6ArVIN6+pzoVuORXM'#10+ + 'bpeNWl2l/HSN3VtUN6aCAKbN/X3o0GavCCMn5Fa85uJFsab4ss/uP+2PusU71+zP'#10+ + 'sBm6p/2IbGvF5k3VPDA7X5YX61sukRjRBihY8xSnNYx1UcoOsX6AiPnbhifD8+xQ'#10+ + 'Tlf8oJUCgYEA0BTfzqNpr9Wxw5/QXaSdw7S/0eP5a0C/nwURvmfSzuTD4equzbEN'#10+ + 'd+dI/s2JMxrdj/I4uoAfUXRGaabevQIjFzC9uyE3LaOyR2zhuvAzX+vVcs6bSXeU'#10+ + 'pKpCAcN+3Z3evMaX2f+z/nfSUAl2i4J2R+/LQAWJW4KwRky/m+cxpfUCgYEA6bN1'#10+ + 'b73bMgM8wpNt6+fcmS+5n0iZihygQ2U2DEud8nZJL4Nrm1dwTnfZfJBnkGj6+0Q0'#10+ + 'cOwj2KS0/wcEdJBP0jucU4v60VMhp75AQeHqidIde0bTViSRo3HWKXHBIFGYoU3T'#10+ + 'LyPyKndbqsOObnsFXHn56Nwhr2HLf6nw4taGQY8CgYBoSW36FLCNbd6QGvLFXBGt'#10+ + '2lMhEM8az/K58kJ4WXSwOLtr6MD/WjNT2tkcy0puEJLm6BFCd6A6pLn9jaKou/92'#10+ + 'SfltZjJPb3GUlp9zn5tAAeSSi7YMViBrfuFiHObij5LorefBXISLjuYbMwL03MgH'#10+ + 'Ocl2JtA2ywMp2KFXs8GQWQKBgFyIVv5ogQrbZ0pvj31xr9HjqK6d01VxIi+tOmpB'#10+ + '4ocnOLEcaxX12BzprW55ytfOCVpF1jHD/imAhb3YrHXu0fwe6DXYXfZV4SSG2vB7'#10+ + 'IB9z14KBN5qLHjNGFpMQXHSMek+b/ftTU0ZnPh9uEM5D3YqRLVd7GcdUhHvG8P8Q'#10+ + 'C9aXAoGBAJtID6h8wOGMP0XYX5YYnhlC7dOLfk8UYrzlp3xhqVkzKthTQTj6wx9R'#10+ + 'GtC4k7U1ki8oJsfcIlBNXd768fqDVWjYju5rzShMpo8OCTS6ipAblKjCxPPVhIpv'#10+ + 'tWPlbSn1qj6wylstJ5/3Z+ZW5H4wIKp5jmLiioDhcP0L/Ex3Zx8O'#10+ + '-----END RSA PRIVATE KEY-----'#10; + + (* test server CA signed certificates *) + srv_signed_cert_pem: array[0..1138] of AnsiChar = + '-----BEGIN CERTIFICATE-----'#10+ + 'MIIDGzCCAgWgAwIBAgIES0KCvTALBgkqhkiG9w0BAQUwFzEVMBMGA1UEAxMMdGVz'#10+ + 'dF9jYV9jZXJ0MB4XDTEwMDEwNTAwMDcyNVoXDTQ1MDMxMjAwMDcyNVowFzEVMBMG'#10+ + 'A1UEAxMMdGVzdF9jYV9jZXJ0MIIBHzALBgkqhkiG9w0BAQEDggEOADCCAQkCggEA'#10+ + 'vfTdv+3fgvVTKRnP/HVNG81cr8TrUP/iiyuve/THMzvFXhCW+K03KwEku55QvnUn'#10+ + 'dwBfU/ROzLlv+5hotgiDRNFT3HxurmhouySBrJNJv7qWp8ILq4sw32vo0fbMu5BZ'#10+ + 'F49bUXK9L3kW2PdhTtSQPWHEzNrCxO+YgCilKHkY3vQNfdJ020Q5EAAEseD1YtWC'#10+ + 'IpRvJzYlZMpjYB1ubTl24kwrgOKUJYKqM4jmF4DVQp4oOK/6QYGGh1QmHRPAy3CB'#10+ + 'II6sbb+sZT9cAqU6GYQVB35lm4XAgibXV6KgmpVxVQQ69U6xyoOl204xuekZOaG9'#10+ + 'RUPId74Rtmwfi1TLbBzo2wIDAQABo3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQM'#10+ + 'MAoGCCsGAQUFBwMBMA8GA1UdDwEB/wQFAwMHIAAwHQYDVR0OBBYEFOFi4ilKOP1d'#10+ + 'XHlWCMwmVKr7mgy8MB8GA1UdIwQYMBaAFP2olB4s2T/xuoQ5pT2RKojFwZo2MAsG'#10+ + 'CSqGSIb3DQEBBQOCAQEAHVWPxazupbOkG7Did+dY9z2z6RjTzYvurTtEKQgzM2Vz'#10+ + 'GQBA+3pZ3c5mS97fPIs9hZXfnQeelMeZ2XP1a+9vp35bJjZBBhVH+pqxjCgiUflg'#10+ + 'A3Zqy0XwwVCgQLE2HyaU3DLUD/aeIFK5gJaOSdNTXZLv43K8kl4cqDbMeRpVTbkt'#10+ + 'YmG4AyEOYRNKGTqMEJXJoxD5E3rBUNrVI/XyTjYrulxbNPcMWEHKNeeqWpKDYTFo'#10+ + 'Bb01PCthGXiq/4A2RLAFosadzRa8SBpoSjPPfZ0b2w4MJpReHqKbR5+T2t6hzml6'#10+ + '4ToyOKPDmamiTuN5KzLN3cw7DQlvWMvqSOChPLnA3Q=='#10+ + '-----END CERTIFICATE-----'#10; + + (** + * Entry point to demo. Note: this HTTP server will make all + * files in the current directory and its subdirectories available + * to anyone. Press ENTER to stop the server once it has started. + * + * @param argc number of arguments in argv + * @param argv first and only argument should be the port number + * @return 0 on success + *) +var + d: PMHD_Daemon; + port: cuint; +begin + if (argc <> 2) or (1 <> sscanf(argv[1], '%u', @port)) or + (UINT16_MAX < port) then + begin + WriteLn(stderr, argv[0], ' PORT'); + Halt(1); + end; + ignore_sigpipe; + magic := magic_open(MAGIC_MIME_TYPE); + magic_load(magic, nil); + pthread_mutex_init(@mutex, nil); + file_not_found_response := MHD_create_response_from_buffer( + strlen(FILE_NOT_FOUND_PAGE), FILE_NOT_FOUND_PAGE, + MHD_RESPMEM_PERSISTENT); + mark_as_html(file_not_found_response); + request_refused_response := MHD_create_response_from_buffer( + strlen(REQUEST_REFUSED_PAGE), REQUEST_REFUSED_PAGE, + MHD_RESPMEM_PERSISTENT); + mark_as_html(request_refused_response); + internal_error_response := MHD_create_response_from_buffer( + strlen(INTERNAL_ERROR_PAGE), INTERNAL_ERROR_PAGE, + MHD_RESPMEM_PERSISTENT); + mark_as_html(internal_error_response); + update_directory; + d := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY or MHD_USE_DEBUG or MHD_USE_SSL +{$IFDEF EPOLL_SUPPORT} + or MHD_USE_EPOLL_LINUX_ONLY +{$ENDIF}, + port, nil, nil, @generate_page, nil, + MHD_OPTION_CONNECTION_MEMORY_LIMIT, size_t(256 * 1024), +{$IFDEF PRODUCTION} + MHD_OPTION_PER_IP_CONNECTION_LIMIT, cuint(64), +{$ENDIF} + MHD_OPTION_CONNECTION_TIMEOUT, cuint(120 (* seconds *)), + MHD_OPTION_THREAD_POOL_SIZE, cuint(NUMBER_OF_THREADS), + MHD_OPTION_NOTIFY_COMPLETED, @response_completed_callback, nil, + MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem, + MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem, + MHD_OPTION_END); + if nil = d then + Halt(1); + WriteLn(stderr, 'HTTP server running. Press ENTER to stop the server'); + ReadLn; + MHD_stop_daemon(d); + MHD_destroy_response(file_not_found_response); + MHD_destroy_response(request_refused_response); + MHD_destroy_response(internal_error_response); + update_cached_response(nil); + pthread_mutex_destroy(@mutex); + magic_close(magic); +end. + diff --git a/packages/libmicrohttpd/examples/digest_auth_example.pp b/packages/libmicrohttpd/examples/digest_auth_example.pp new file mode 100644 index 0000000000..cb92d36852 --- /dev/null +++ b/packages/libmicrohttpd/examples/digest_auth_example.pp @@ -0,0 +1,127 @@ +(* + This file is part of libmicrohttpd + Copyright (C) 2010 Christian Grothoff (and other contributing authors) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*) +(** + * @file digest_auth_example.pp (Original: digest_auth_example.c) + * @brief minimal example for how to use digest auth with libmicrohttpd + * @author Amr Ali / Silvio Clécio + *) + +program digest_auth_example; + +{$mode objfpc}{$H+} + +uses + sysutils, BaseUnix, cmem, cutils, libmicrohttpd; + +const + PAGE: Pcchar = '<html><head><title>libmicrohttpd demo</title></head><body>Access granted</body></html>'; + DENIED: Pcchar = '<html><head><title>libmicrohttpd demo</title></head><body>Access denied</body></html>'; + MY_OPAQUE_STR = '11733b200778ce33060f31c9af70a870ba96ddd4'; + + function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar; + method: Pcchar; version: Pcchar; upload_data: Pcchar; + upload_data_size: Psize_t; ptr: PPointer): cint; cdecl; + const + password: Pcchar = 'testpass'; + realm: Pcchar = 'test@example.com'; + var + response: PMHD_Response; + username: Pcchar; + ret: cint; + signal_stale: cint; + begin + username := MHD_digest_auth_get_username(connection); + if username = nil then + begin + response := MHD_create_response_from_buffer(strlen(DENIED), DENIED, + MHD_RESPMEM_PERSISTENT); + ret := MHD_queue_auth_fail_response(connection, realm, MY_OPAQUE_STR, + response, MHD_NO); + MHD_destroy_response(response); + Exit(ret); + end; + ret := MHD_digest_auth_check(connection, realm, username, password, 300); + Free(username); + if (ret = MHD_INVALID_NONCE) or (ret = MHD_NO) then + begin + response := MHD_create_response_from_buffer(strlen(DENIED), DENIED, + MHD_RESPMEM_PERSISTENT); + if nil = response then + Exit(MHD_NO); + if ret = MHD_INVALID_NONCE then + signal_stale := MHD_YES + else + signal_stale := MHD_NO; + ret := MHD_queue_auth_fail_response(connection, realm, MY_OPAQUE_STR, + response, signal_stale); + MHD_destroy_response(response); + Exit(ret); + end; + response := MHD_create_response_from_buffer(strlen(PAGE), PAGE, + MHD_RESPMEM_PERSISTENT); + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + Result := ret; + end; + +var + fd: cint; + rnd: array[0..7] of AnsiChar; + len: ssize_t; + off: size_t; + d: PMHD_Daemon; +begin + if argc <> 2 then + begin + WriteLn(argv[0], ' PORT'); + Halt(1); + end; + fd := FpOpen('/dev/urandom', O_RDONLY); + if -1 = fd then + begin + WriteLn(stderr, Format('Failed to open `%s'': %s', [ + '/dev/urandom', strerror(errno^)])); + Halt(1); + end; + off := 0; + while off < 8 do + begin + len := FpRead(fd, rnd, 8); + if len = -1 then + begin + WriteLn(stderr, Format('Failed to read `%s'': %s', [ + '/dev/urandom', strerror(errno^)])); + FpClose(fd); + Halt(1); + end; + off += len; + end; + FpClose(fd); + d := MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG, + StrToInt(argv[1]), nil, nil, @ahc_echo, PAGE, + MHD_OPTION_DIGEST_AUTH_RANDOM, SizeOf(rnd), rnd, + MHD_OPTION_NONCE_NC_SIZE, 300, + MHD_OPTION_CONNECTION_TIMEOUT, cuint(120), + MHD_OPTION_END); + if d = nil then + Halt(1); + ReadLn; + MHD_stop_daemon (d); +end. + diff --git a/packages/libmicrohttpd/examples/dual_stack_example.pp b/packages/libmicrohttpd/examples/dual_stack_example.pp new file mode 100644 index 0000000000..a55635c6e0 --- /dev/null +++ b/packages/libmicrohttpd/examples/dual_stack_example.pp @@ -0,0 +1,78 @@ +(* + This file is part of libmicrohttpd + Copyright (C) 2007, 2012 Christian Grothoff (and other contributing authors) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*) +(** + * @file dual_stack_example.pp (Original: dual_stack_example.c) + * @brief how to use MHD with both IPv4 and IPv6 support (dual-stack) + * @author Christian Grothoff / Silvio Clécio + *) + +// To test it, just execute: $ curl -g -6 "http://[::1]:8888/" + +program dual_stack_example; + +{$mode objfpc}{$H+} + +uses + sysutils, libmicrohttpd; + +const + PAGE: Pcchar = '<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>'; + + function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar; + method: Pcchar; version: Pcchar; upload_data: Pcchar; + upload_data_size: Psize_t; ptr: PPointer): cint; cdecl; + const + aptr: cint = 0; + var + me: Pcchar; + response: PMHD_Response; + ret: cint; + begin + me := cls; + if 0 <> strcomp(method, 'GET') then + Exit(MHD_NO); (* unexpected method *) + if @aptr <> ptr^ then + begin + (* do never respond on first call *) + ptr^ := @aptr; + Exit(MHD_YES); + end; + ptr^ := nil; (* reset when done *) + response := MHD_create_response_from_buffer(strlen(me), Pointer(me), + MHD_RESPMEM_PERSISTENT); + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + Result := ret; + end; + +var + d: PMHD_Daemon; +begin + if argc <> 2 then + begin + WriteLn(argv[0], ' PORT'); + Halt(1); + end; + d := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY or MHD_USE_DEBUG or + MHD_USE_DUAL_STACK, StrToInt(argv[1]), nil, nil, @ahc_echo, PAGE, + MHD_OPTION_CONNECTION_TIMEOUT, cuint(120), MHD_OPTION_END); + ReadLn; + MHD_stop_daemon(d); +end. + diff --git a/packages/libmicrohttpd/examples/fileserver_example.pp b/packages/libmicrohttpd/examples/fileserver_example.pp new file mode 100644 index 0000000000..b4a08f8cf1 --- /dev/null +++ b/packages/libmicrohttpd/examples/fileserver_example.pp @@ -0,0 +1,115 @@ +(* + This file is part of libmicrohttpd + Copyright (C) 2007 Christian Grothoff (and other contributing authors) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*) +(** + * @file fileserver_example.pp (Original: fileserver_example.c) + * @brief minimal example for how to use libmicrohttpd to serve files + * @author Christian Grothoff / Silvio Clécio + *) + +program fileserver_example; + +{$mode objfpc}{$H+} + +uses + sysutils, BaseUnix, cutils, libmicrohttpd; + +const + PAGE: Pcchar = '<html><head><title>File not found</title></head><body>File not found</body></html>'; + + function file_reader(cls: Pointer; pos: cuint64; buf: Pcchar; + max: size_t): ssize_t; cdecl; + var + &file: FILEptr; + begin + &file := cls; + fseek(&file, pos, SEEK_SET); + Result := fread(buf, 1, max, &file); + end; + + procedure free_callback(cls: Pointer); cdecl; + var + &file: FILEptr; + begin + &file := cls; + fclose(&file); + end; + + function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar; + method: Pcchar; version: Pcchar; upload_data: Pcchar; + upload_data_size: Psize_t; ptr: PPointer): cint; cdecl; + const + aptr: cint = 0; + var + response: PMHD_Response; + ret: cint; + &file: FILEptr; + buf: stat; + begin + if (0 <> strcomp(method, MHD_HTTP_METHOD_GET)) and + (0 <> strcomp(method, MHD_HTTP_METHOD_HEAD)) then + Exit(MHD_NO); (* unexpected method *) + if @aptr <> ptr^ then + begin + (* do never respond on first call *) + ptr^ := @aptr; + Exit(MHD_YES); + end; + ptr^ := nil; (* reset when done *) + if 0 = FpStat(@url[1], buf) then + &file := fopen(@url[1], fopenread) + else + &file := nil; + if nil = &file then + begin + response := MHD_create_response_from_buffer(strlen(PAGE), Pointer(PAGE), + MHD_RESPMEM_PERSISTENT); + ret := MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response); + MHD_destroy_response(response); + end + else + begin + response := MHD_create_response_from_callback(buf.st_size, 32 * 1024, (* 32k page size *) + @file_reader, &file, @free_callback); + if nil = response then + begin + fclose(&file); + Exit(MHD_NO); + end; + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + end; + Result := ret; + end; + +var + d: PMHD_Daemon; +begin + if argc <> 2 then + begin + WriteLn(argv[0], ' PORT'); + Halt(1); + end; + d := MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG, + StrToInt(argv[1]), nil, nil, @ahc_echo, PAGE, MHD_OPTION_END); + if d = nil then + Halt(1); + ReadLn; + MHD_stop_daemon(d); +end. + diff --git a/packages/libmicrohttpd/examples/fileserver_example_dirs.pp b/packages/libmicrohttpd/examples/fileserver_example_dirs.pp new file mode 100644 index 0000000000..bc06b38c22 --- /dev/null +++ b/packages/libmicrohttpd/examples/fileserver_example_dirs.pp @@ -0,0 +1,167 @@ +(* + This file is part of libmicrohttpd + Copyright (C) 2007 Christian Grothoff (and other contributing authors) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*) + +(** + * @file fileserver_example_dirs.pp (Original: fileserver_example_dirs.c) + * @brief example for how to use libmicrohttpd to serve files (with directory support) + * @author Christian Grothoff / Silvio Clécio + *) + +program fileserver_example_dirs; + +{$mode objfpc}{$H+} + +uses + sysutils, BaseUnix, cutils, libmicrohttpd; + +const + PAGE: Pcchar = '<html><head><title>File not found</title></head><body>File not found</body></html>'; + + function file_reader(cls: Pointer; pos: cuint64; buf: Pcchar; + max: size_t): ssize_t; cdecl; + var + &file: FILEptr; + begin + &file := cls; + fseek(&file, pos, SEEK_SET); + Result := fread(buf, 1, max, &file); + end; + + procedure file_free_callback(cls: Pointer); cdecl; + var + &file: FILEptr; + begin + &file := cls; + fclose(&file); + end; + + procedure dir_free_callback(cls: Pointer); cdecl; + var + dir: pDir; + begin + dir := cls; + if dir <> nil then + FpClosedir(dir^); + end; + + function dir_reader(cls: Pointer; pos: cuint64; buf: Pcchar; + max: size_t): ssize_t; cdecl; + var + dir: pDir; + e: pDirent; + begin + dir := cls; + if max < 512 then + Exit(0); + repeat + e := FpReaddir(dir^); + if e = nil then + Exit(MHD_CONTENT_READER_END_OF_STREAM); + until not (e^.d_name[0] = '.'); + Result := snprintf(buf, max, '<a href="/%s">%s</a><br>', e^.d_name, + e^.d_name); + end; + + function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar; + method: Pcchar; version: Pcchar; upload_data: Pcchar; + upload_data_size: Psize_t; ptr: PPointer): cint; cdecl; + const + aptr: cint = 0; + var + response: PMHD_Response; + ret: cint; + &file: FILEptr; + dir: pDir; + buf: stat; + emsg: array[0..1023] of AnsiChar; + begin + if 0 <> strcomp(method, MHD_HTTP_METHOD_GET) then + Exit(MHD_NO); (* unexpected method *) + if @aptr <> ptr^ then + begin + (* do never respond on first call *) + ptr^ := @aptr; + Exit(MHD_YES); + end; + ptr^ := nil; (* reset when done *) + if (0 = FpStat(@url[1], buf)) and fpS_ISREG(buf.st_mode) then + &file := fopen(@url[1], fopenread) + else + &file := nil; + if &file = nil then + begin + dir := FpOpendir(PChar('.')); + if dir = nil then + begin + (* most likely cause: more concurrent requests than + available file descriptors / 2 *) + snprintf(emsg, SizeOf(emsg), 'Failed to open directory `.'': %s'#10, + strerror(errno^)); + response := MHD_create_response_from_buffer(strlen(emsg), @emsg, + MHD_RESPMEM_MUST_COPY); + if response = nil then + Exit(MHD_NO); + ret := MHD_queue_response(connection, MHD_HTTP_SERVICE_UNAVAILABLE, + response); + MHD_destroy_response(response); + end + else + begin + response := MHD_create_response_from_callback(cuint64(MHD_SIZE_UNKNOWN), + 32 * 1024, @dir_reader, dir, @dir_free_callback); + if response = nil then + begin + FpClosedir(dir^); + Exit(MHD_NO); + end; + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + end; + end + else + begin + response := MHD_create_response_from_callback(buf.st_size, 32 * 1024, (* 32k page size *) + @file_reader, &file, @file_free_callback); + if response = nil then + begin + fclose(&file); + Exit(MHD_NO); + end; + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + end; + Result := ret; + end; + +var + d: PMHD_Daemon; +begin + if argc <> 2 then + begin + WriteLn(argv[0], ' PORT'); + Halt(1); + end; + d := MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG, + StrToInt(argv[1]), nil, nil, @ahc_echo, PAGE, MHD_OPTION_END); + if d = nil then + Halt(1); + ReadLn; + MHD_stop_daemon(d); +end. + diff --git a/packages/libmicrohttpd/examples/fileserver_example_external_select.pp b/packages/libmicrohttpd/examples/fileserver_example_external_select.pp new file mode 100644 index 0000000000..e19ce9f80b --- /dev/null +++ b/packages/libmicrohttpd/examples/fileserver_example_external_select.pp @@ -0,0 +1,146 @@ +(* + This file is part of libmicrohttpd + Copyright (C) 2007, 2008 Christian Grothoff (and other contributing authors) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*) +(** + * @file fileserver_example_external_select.pp (Original: fileserver_example_external_select.c) + * @brief minimal example for how to use libmicrohttpd to server files + * @author Christian Grothoff / Silvio Clécio + *) + +program fileserver_example_external_select; + +{$mode objfpc}{$H+} + +uses + sysutils, BaseUnix, cutils, libmicrohttpd; + +const + PAGE: Pcchar = '<html><head><title>File not found</title></head><body>File not found</body></html>'; + + function file_reader(cls: Pointer; pos: cuint64; buf: Pcchar; + max: size_t): ssize_t; cdecl; + var + &file: FILEptr; + begin + &file := cls; + fseek(&file, pos, SEEK_SET); + Result := fread(buf, 1, max, &file); + end; + + procedure free_callback(cls: Pointer); cdecl; + var + &file: FILEptr; + begin + &file := cls; + fclose(&file); + end; + + function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar; + method: Pcchar; version: Pcchar; upload_data: Pcchar; + upload_data_size: Psize_t; ptr: PPointer): cint; cdecl; + const + aptr: cint = 0; + var + response: PMHD_Response; + ret: cint; + &file: FILEptr; + buf: stat; + begin + if 0 <> strcomp(method, MHD_HTTP_METHOD_GET) then + Exit(MHD_NO); (* unexpected method *) + if @aptr <> ptr^ then + begin + (* do never respond on first call *) + ptr^ := @aptr; + Exit(MHD_YES); + end; + ptr^ := nil; (* reset when done *) + if (0 = FpStat(@url[1], buf)) and fpS_ISREG(buf.st_mode) then + &file := fopen(@url[1], fopenread) + else + &file := nil; + if &file = nil then + begin + response := MHD_create_response_from_buffer(strlen(PAGE), Pointer(PAGE), + MHD_RESPMEM_PERSISTENT); + ret := MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response); + MHD_destroy_response(response); + end + else + begin + response := MHD_create_response_from_callback(buf.st_size, 32 * 1024, (* 32k page size *) + @file_reader, &file, @free_callback); + if response = nil then + begin + fclose(&file); + Exit(MHD_NO); + end; + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + end; + Result := ret; + end; + +var + d: PMHD_Daemon; + &end: time_t; + t: time_t; + tv: timeval; + rs: TFDSet; + ws: TFDSet; + es: TFDSet; + max: MHD_socket; + mhd_timeout: MHD_UNSIGNED_LONG_LONG; +begin + if argc <> 3 then + begin + WriteLn(argv[0], ' PORT SECONDS-TO-RUN'); + Halt(1); + end; + d := MHD_start_daemon(MHD_USE_DEBUG, StrToInt(argv[1]), nil, nil, @ahc_echo, + PAGE, MHD_OPTION_END); + if d = nil then + Halt(1); + &end := fptime + StrToInt(argv[2]); + while True do + begin + t := fptime; + if not (t < &end) then + Break; + tv.tv_sec := &end - t; + tv.tv_usec := 0; + max := 0; + fpFD_ZERO(rs); + fpFD_ZERO(ws); + fpFD_ZERO(es); + if MHD_YES <> MHD_get_fdset (d, @rs, @ws, @es, @max) then + Break; (* fatal internal error *) + if MHD_get_timeout(d, @mhd_timeout) = MHD_YES then + begin + if MHD_UNSIGNED_LONG_LONG(tv.tv_sec) < mhd_timeout div clonglong(1000) then + begin + tv.tv_sec := mhd_timeout div clonglong(1000); + tv.tv_usec := (mhd_timeout - (tv.tv_sec * clonglong(1000))) * clonglong(1000); + end; + end; + fpSelect(max + 1, @rs, @ws, @es, @tv); + MHD_run(d); + end; + MHD_stop_daemon(d); +end. + diff --git a/packages/libmicrohttpd/examples/hellobrowser.pp b/packages/libmicrohttpd/examples/hellobrowser.pp new file mode 100644 index 0000000000..fa7d05cc54 --- /dev/null +++ b/packages/libmicrohttpd/examples/hellobrowser.pp @@ -0,0 +1,42 @@ +(* Feel free to use this example code in any way + you see fit (Public Domain) *) + +// Original example: https://gnunet.org/svn/libmicrohttpd/doc/examples/hellobrowser.c + +program hellobrowser; + +{$mode objfpc}{$H+} + +uses + libmicrohttpd; + +const + PORT = 8888; + + function AnswerToConnection(ACls: Pointer; AConnection: PMHD_Connection; + AUrl: Pcchar; AMethod: Pcchar; AVersion: Pcchar; AUploadData: Pcchar; + AUploadDataSize: Psize_t; AConCls: PPointer): cint; cdecl; + const + PAGE: Pcchar = 'Hello world'; + var + VReturn: cint; + VResponse: PMHD_Response; + begin + VResponse := MHD_create_response_from_buffer(Length(PAGE), Pointer(PAGE), + MHD_RESPMEM_PERSISTENT); + VReturn := MHD_queue_response(AConnection, MHD_HTTP_OK, VResponse); + MHD_destroy_response(VResponse); + Result := VReturn; + end; + +var + VDaemon: PMHD_Daemon; +begin + VDaemon := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, nil, nil, + @AnswerToConnection, nil, MHD_OPTION_END); + if not Assigned(VDaemon) then + Halt(1); + ReadLn; + MHD_stop_daemon(VDaemon) +end. + diff --git a/packages/libmicrohttpd/examples/https_fileserver_example.pp b/packages/libmicrohttpd/examples/https_fileserver_example.pp new file mode 100644 index 0000000000..502428ae74 --- /dev/null +++ b/packages/libmicrohttpd/examples/https_fileserver_example.pp @@ -0,0 +1,194 @@ +(* + This file is part of libmicrohttpd + Copyright (C) 2007, 2008 Christian Grothoff (and other contributing authors) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*) +(** + * @file https_fileserver_example.pp (Original: https_fileserver_example.c) + * @brief a simple HTTPS file server using TLS. + * + * Usage : + * + * 'https_fileserver_example HTTP-PORT' + * + * The certificate & key are required by the server to operate, Omitting the + * path arguments will cause the server to use the hard coded example certificate & key. + * + * 'certtool' may be used to generate these if required. + * + * @author Sagie Amir / Silvio Clécio + *) + +program https_fileserver_example; + +{$mode objfpc}{$H+} + +uses + sysutils, BaseUnix, cutils, libmicrohttpd; + +const + BUF_SIZE = 1024; + MAX_URL_LEN = 255; + + // TODO remove if unused + CAFILE: Pcchar = 'ca.pem'; + CRLFILE: Pcchar = 'crl.pem'; + + EMPTY_PAGE: Pcchar = '<html><head><title>File not found</title></head><body>File not found</body></html>'; + + (* Test Certificate *) + cert_pem: array[0..980] of AnsiChar = + '-----BEGIN CERTIFICATE-----'#10+ + 'MIICpjCCAZCgAwIBAgIESEPtjjALBgkqhkiG9w0BAQUwADAeFw0wODA2MDIxMjU0'#10+ + 'MzhaFw0wOTA2MDIxMjU0NDZaMAAwggEfMAsGCSqGSIb3DQEBAQOCAQ4AMIIBCQKC'#10+ + 'AQC03TyUvK5HmUAirRp067taIEO4bibh5nqolUoUdo/LeblMQV+qnrv/RNAMTx5X'#10+ + 'fNLZ45/kbM9geF8qY0vsPyQvP4jumzK0LOJYuIwmHaUm9vbXnYieILiwCuTgjaud'#10+ + '3VkZDoQ9fteIo+6we9UTpVqZpxpbLulBMh/VsvX0cPJ1VFC7rT59o9hAUlFf9jX/'#10+ + 'GmKdYI79MtgVx0OPBjmmSD6kicBBfmfgkO7bIGwlRtsIyMznxbHu6VuoX/eVxrTv'#10+ + 'rmCwgEXLWRZ6ru8MQl5YfqeGXXRVwMeXU961KefbuvmEPccgCxm8FZ1C1cnDHFXh'#10+ + 'siSgAzMBjC/b6KVhNQ4KnUdZAgMBAAGjLzAtMAwGA1UdEwEB/wQCMAAwHQYDVR0O'#10+ + 'BBYEFJcUvpjvE5fF/yzUshkWDpdYiQh/MAsGCSqGSIb3DQEBBQOCAQEARP7eKSB2'#10+ + 'RNd6XjEjK0SrxtoTnxS3nw9sfcS7/qD1+XHdObtDFqGNSjGYFB3Gpx8fpQhCXdoN'#10+ + '8QUs3/5ZVa5yjZMQewWBgz8kNbnbH40F2y81MHITxxCe1Y+qqHWwVaYLsiOTqj2/'#10+ + '0S3QjEJ9tvklmg7JX09HC4m5QRYfWBeQLD1u8ZjA1Sf1xJriomFVyRLI2VPO2bNe'#10+ + 'JDMXWuP+8kMC7gEvUnJ7A92Y2yrhu3QI3bjPk8uSpHea19Q77tul1UVBJ5g+zpH3'#10+ + 'OsF5p0MyaVf09GTzcLds5nE/osTdXGUyHJapWReVmPm3Zn6gqYlnzD99z+DPIgIV'#10+ + 'RhZvQx74NQnS6g=='#10+ + '-----END CERTIFICATE-----'#10; + + key_pem: array[0..1674] of AnsiChar = + '-----BEGIN RSA PRIVATE KEY-----'#10+ + 'MIIEowIBAAKCAQEAtN08lLyuR5lAIq0adOu7WiBDuG4m4eZ6qJVKFHaPy3m5TEFf'#10+ + 'qp67/0TQDE8eV3zS2eOf5GzPYHhfKmNL7D8kLz+I7psytCziWLiMJh2lJvb2152I'#10+ + 'niC4sArk4I2rnd1ZGQ6EPX7XiKPusHvVE6VamacaWy7pQTIf1bL19HDydVRQu60+'#10+ + 'faPYQFJRX/Y1/xpinWCO/TLYFcdDjwY5pkg+pInAQX5n4JDu2yBsJUbbCMjM58Wx'#10+ + '7ulbqF/3lca0765gsIBFy1kWeq7vDEJeWH6nhl10VcDHl1PetSnn27r5hD3HIAsZ'#10+ + 'vBWdQtXJwxxV4bIkoAMzAYwv2+ilYTUOCp1HWQIDAQABAoIBAArOQv3R7gmqDspj'#10+ + 'lDaTFOz0C4e70QfjGMX0sWnakYnDGn6DU19iv3GnX1S072ejtgc9kcJ4e8VUO79R'#10+ + 'EmqpdRR7k8dJr3RTUCyjzf/C+qiCzcmhCFYGN3KRHA6MeEnkvRuBogX4i5EG1k5l'#10+ + '/5t+YBTZBnqXKWlzQLKoUAiMLPg0eRWh+6q7H4N7kdWWBmTpako7TEqpIwuEnPGx'#10+ + 'u3EPuTR+LN6lF55WBePbCHccUHUQaXuav18NuDkcJmCiMArK9SKb+h0RqLD6oMI/'#10+ + 'dKD6n8cZXeMBkK+C8U/K0sN2hFHACsu30b9XfdnljgP9v+BP8GhnB0nCB6tNBCPo'#10+ + '32srOwECgYEAxWh3iBT4lWqL6bZavVbnhmvtif4nHv2t2/hOs/CAq8iLAw0oWGZc'#10+ + '+JEZTUDMvFRlulr0kcaWra+4fN3OmJnjeuFXZq52lfMgXBIKBmoSaZpIh2aDY1Rd'#10+ + 'RbEse7nQl9hTEPmYspiXLGtnAXW7HuWqVfFFP3ya8rUS3t4d07Hig8ECgYEA6ou6'#10+ + 'OHiBRTbtDqLIv8NghARc/AqwNWgEc9PelCPe5bdCOLBEyFjqKiT2MttnSSUc2Zob'#10+ + 'XhYkHC6zN1Mlq30N0e3Q61YK9LxMdU1vsluXxNq2rfK1Scb1oOlOOtlbV3zA3VRF'#10+ + 'hV3t1nOA9tFmUrwZi0CUMWJE/zbPAyhwWotKyZkCgYEAh0kFicPdbABdrCglXVae'#10+ + 'SnfSjVwYkVuGd5Ze0WADvjYsVkYBHTvhgRNnRJMg+/vWz3Sf4Ps4rgUbqK8Vc20b'#10+ + 'AU5G6H6tlCvPRGm0ZxrwTWDHTcuKRVs+pJE8C/qWoklE/AAhjluWVoGwUMbPGuiH'#10+ + '6Gf1bgHF6oj/Sq7rv/VLZ8ECgYBeq7ml05YyLuJutuwa4yzQ/MXfghzv4aVyb0F3'#10+ + 'QCdXR6o2IYgR6jnSewrZKlA9aPqFJrwHNR6sNXlnSmt5Fcf/RWO/qgJQGLUv3+rG'#10+ + '7kuLTNDR05azSdiZc7J89ID3Bkb+z2YkV+6JUiPq/Ei1+nDBEXb/m+/HqALU/nyj'#10+ + 'P3gXeQKBgBusb8Rbd+KgxSA0hwY6aoRTPRt8LNvXdsB9vRcKKHUFQvxUWiUSS+L9'#10+ + '/Qu1sJbrUquKOHqksV5wCnWnAKyJNJlhHuBToqQTgKXjuNmVdYSe631saiI7PHyC'#10+ + 'eRJ6DxULPxABytJrYCRrNqmXi5TCiqR2mtfalEMOPxz8rUU8dYyx'#10+ + '-----END RSA PRIVATE KEY-----'#10; + + function file_reader(cls: Pointer; pos: cuint64; buf: Pcchar; + max: size_t): ssize_t; cdecl; + var + &file: FILEptr; + begin + &file := cls; + fseek(&file, pos, SEEK_SET); + Result := fread(buf, 1, max, &file); + end; + + procedure file_free_callback(cls: Pointer); cdecl; + var + &file: FILEptr; + begin + &file := cls; + fclose(&file); + end; + + function http_ahc(cls: Pointer; connection: PMHD_Connection; url: Pcchar; + method: Pcchar; version: Pcchar; upload_data: Pcchar; + upload_data_size: Psize_t; ptr: PPointer): cint; cdecl; + const + aptr: cint = 0; + var + response: PMHD_Response; + ret: cint; + &file: FILEptr; + buf: stat; + begin + if 0 <> strcomp(method, MHD_HTTP_METHOD_GET) then + Exit(MHD_NO); (* unexpected method *) + if @aptr <> ptr^ then + begin + (* do never respond on first call *) + ptr^ := @aptr; + Exit(MHD_YES); + end; + ptr^ := nil; (* reset when done *) + if (0 = FpStat(@url[1], buf)) and fpS_ISREG(buf.st_mode) then + &file := fopen(@url[1], fopenread) + else + &file := nil; + if &file = nil then + begin + response := MHD_create_response_from_buffer(strlen(EMPTY_PAGE), + Pointer(EMPTY_PAGE), MHD_RESPMEM_PERSISTENT); + ret := MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response); + MHD_destroy_response(response); + end + else + begin + response := MHD_create_response_from_callback(buf.st_size, 32 * 1024, (* 32k page size *) + @file_reader, &file, @file_free_callback); + if response = nil then + begin + fclose(&file); + Exit(MHD_NO); + end; + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + end; + Result := ret; + end; + +var + TLS_daemon: PMHD_Daemon; +begin + if argc = 2 then + begin + (* TODO check if this is truly necessary - disallow usage of the blocking /dev/random *) + (* gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM, 0); *) + TLS_daemon := MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION or + MHD_USE_DEBUG or MHD_USE_SSL, StrToInt(argv[1]), nil, nil, + @http_ahc, nil, MHD_OPTION_CONNECTION_TIMEOUT, 256, + MHD_OPTION_HTTPS_MEM_KEY, key_pem, + MHD_OPTION_HTTPS_MEM_CERT, cert_pem, + MHD_OPTION_END); + end + else + begin + WriteLn(' Usage: ', argv[0], ' HTTP-PORT'); + Halt(1); + end; + if TLS_daemon = nil then + begin + WriteLn(stderr, 'Error: failed to start TLS_daemon'); + Halt(1); + end + else + WriteLn('MHD daemon listening on port ', argv[1]); + ReadLn; + MHD_stop_daemon(TLS_daemon); +end. + diff --git a/packages/libmicrohttpd/examples/largepost.pp b/packages/libmicrohttpd/examples/largepost.pp new file mode 100644 index 0000000000..6293899ed5 --- /dev/null +++ b/packages/libmicrohttpd/examples/largepost.pp @@ -0,0 +1,187 @@ +(* Feel free to use this example code in any way + you see fit (Public Domain) *) + +// Original example: https://gnunet.org/svn/libmicrohttpd/doc/examples/largepost.c + +program largepost; + +{$mode objfpc}{$H+} + +uses + libmicrohttpd, SysUtils, cutils; + +type + TConnectionInfoStruct = record + ConnectionType: cint; + PostProcessor: PMHD_PostProcessor; + Fp: FILEptr; + AnswerString: Pcchar; + AnswerCode: cint; + end; + PConnectionInfoStruct = ^TConnectionInfoStruct; + +const + PORT = 8888; + POSTBUFFERSIZE = 512; + MAXCLIENTS = 2; + GET = 0; + POST = 1; + +var + NrOfUploadingClients: Cardinal; + AskPage: Pcchar = + '<html><body>'+#10+ + 'Upload a file, please!<br>'+#10+ + 'There are %d clients uploading at the moment.<br>'+#10+ + '<form action="/filepost" method="post" enctype="multipart/form-data">'+#10+ + '<input name="file" type="file">'+#10+ + '<input type="submit" value="Send"></form>'+#10+ + '</body></html>'; + BusyPage: Pcchar = '<html><body>This server is busy, please try again later.</body></html>'; + CompletePage: Pcchar = '<html><body>The upload has been completed.</body></html>'; + ErrorPage: Pcchar = '<html><body>This doesn''t seem to be right.</body></html>'; + ServerErrorPage: Pcchar = '<html><body>An internal server error has occured.</body></html>'; + FileExistsPage: Pcchar = '<html><body>This file already exists.</body></html>'; + + function SendPage(AConnection: PMHD_Connection; APage: Pcchar; AStatusCode: cint): cint; + var + VRet: cint; + VResponse: PMHD_Response; + begin + VResponse := MHD_create_response_from_buffer(Length(APage), + Pointer(APage), MHD_RESPMEM_MUST_COPY); + if not Assigned(VResponse) then + Exit(MHD_NO); + MHD_add_response_header(VResponse, MHD_HTTP_HEADER_CONTENT_TYPE, 'text/html'); + VRet := MHD_queue_response(AConnection, AStatusCode, VResponse); + MHD_destroy_response(VResponse); + Result := VRet; + end; + + function IteratePost(AConInfoCls: Pointer; AKind: MHD_ValueKind; AKey: Pcchar; + AFileName: Pcchar; AContentType: Pcchar; ATransferEncoding: Pcchar; + AData: Pcchar; AOff: cuint64; ASize: size_t): cint; cdecl; + var + VConInfo: PConnectionInfoStruct; + begin + VConInfo := AConInfoCls; + VConInfo^.AnswerString := ServerErrorPage; + VConInfo^.AnswerCode := MHD_HTTP_INTERNAL_SERVER_ERROR; + if StrComp(AKey, 'file') <> 0 then + Exit(MHD_NO); + if not Assigned(VConInfo^.Fp) then + begin + if FileExists(AFileName) then + begin + VConInfo^.AnswerString := FileExistsPage; + VConInfo^.AnswerCode := MHD_HTTP_FORBIDDEN; + Exit(MHD_NO); + end; + VConInfo^.Fp := fopen(AFileName, fappendwrite); + if not Assigned(VConInfo^.Fp) then + Exit(MHD_NO); + end; + if ASize > 0 then + if fwrite(AData, ASize, SizeOf(AnsiChar), VConInfo^.Fp) = 0 then + Exit(MHD_NO); + VConInfo^.AnswerString := CompletePage; + VConInfo^.AnswerCode := MHD_HTTP_OK; + Result := MHD_YES; + end; + + procedure RequestCompleted(ACls: Pointer; AConnection: PMHD_Connection; + AConCls: PPointer; AToe: MHD_RequestTerminationCode); cdecl; + var + VConInfo: PConnectionInfoStruct; + begin + VConInfo := AConCls^; + if not Assigned(VConInfo) then + Exit; + if VConInfo^.ConnectionType = POST then + begin + if Assigned(VConInfo^.PostProcessor) then + begin + MHD_destroy_post_processor(VConInfo^.PostProcessor); + Dec(NrOfUploadingClients); + end; + if Assigned(VConInfo^.Fp) then + fclose(VConInfo^.Fp); + end; + FreeMem(VConInfo); + AConCls^ := nil; + end; + + function AnswerToConnection(ACls: Pointer; AConnection: PMHD_Connection; + AUrl: Pcchar; AMethod: Pcchar; AVersion: Pcchar; AUploadData: Pcchar; + AUploadDataSize: Psize_t; AConCls: PPointer): cint; cdecl; + var + VBuffer: array[0..1024] of AnsiChar; + VConInfo: PConnectionInfoStruct; + begin + if not Assigned(AConCls^) then + begin + if NrOfUploadingClients >= MAXCLIENTS then + Exit(SendPage(AConnection, BusyPage, MHD_HTTP_SERVICE_UNAVAILABLE)); + VConInfo := AllocMem(SizeOf(TConnectionInfoStruct)); + if not Assigned(VConInfo) then + Exit(MHD_NO); + VConInfo^.Fp := nil; + if StrComp(AMethod, 'POST') = 0 then + begin + VConInfo^.PostProcessor := MHD_create_post_processor(AConnection, + POSTBUFFERSIZE, @IteratePost, VConInfo); + if not Assigned(VConInfo^.PostProcessor) then + begin + FreeMem(VConInfo); + Exit(MHD_NO); + end; + Inc(NrOfUploadingClients); + VConInfo^.ConnectionType := POST; + VConInfo^.AnswerCode := MHD_HTTP_OK; + VConInfo^.AnswerString := CompletePage; + end + else + VConInfo^.ConnectionType := GET; + AConCls^ := VConInfo; + Exit(MHD_YES); + end; + if StrComp(AMethod, 'GET') = 0 then + begin + StrLFmt(VBuffer, SizeOf(VBuffer), AskPage, [NrOfUploadingClients]); + Exit(SendPage(AConnection, VBuffer, MHD_HTTP_OK)); + end; + if StrComp(AMethod, 'POST') = 0 then + begin + VConInfo := AConCls^; + if AUploadDataSize^ <> 0 then + begin + MHD_post_process(VConInfo^.PostProcessor, AUploadData, AUploadDataSize^); + AUploadDataSize^ := 0; + Exit(MHD_YES); + end + else + begin + if Assigned(VConInfo^.Fp) then + begin + fclose(VConInfo^.Fp); + VConInfo^.Fp := nil; + end; + (* Now it is safe to open and inspect the file before calling send_page with a response *) + Exit(SendPage(AConnection, VConInfo^.AnswerString, VConInfo^.AnswerCode)); + end; + end; + Result := SendPage(AConnection, ErrorPage, MHD_HTTP_BAD_REQUEST); + end; + +var + VDaemon: PMHD_Daemon; +begin + VDaemon := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, nil, nil, + @AnswerToConnection, nil, MHD_OPTION_NOTIFY_COMPLETED, @RequestCompleted, + nil, MHD_OPTION_END); + if not Assigned(VDaemon) then + Halt(1); + ReadLn; + MHD_stop_daemon(VDaemon); +end. + diff --git a/packages/libmicrohttpd/examples/logging.pp b/packages/libmicrohttpd/examples/logging.pp new file mode 100644 index 0000000000..cb8e47a838 --- /dev/null +++ b/packages/libmicrohttpd/examples/logging.pp @@ -0,0 +1,43 @@ +(* Feel free to use this example code in any way + you see fit (Public Domain) *) + +// Original example: https://gnunet.org/svn/libmicrohttpd/doc/examples/logging.c + +program logging; + +{$mode objfpc}{$H+} + +uses + libmicrohttpd, sysutils; + +const + PORT = 8888; + + function PrintOutKey(ACls: Pointer; AKind: MHD_ValueKind; AKey: Pcchar; + AValue: Pcchar): cint; cdecl; + begin + WriteLn(Format('%s: %s', [AKey, AValue])); + Result := MHD_YES; + end; + + function AnswerToConnection(ACls: Pointer; AConnection: PMHD_Connection; + AUrl: Pcchar; AMethod: Pcchar; AVersion: Pcchar; AUploadData: Pcchar; + AUploadDataSize: Psize_t; AConCls: PPointer): cint; cdecl; + begin + WriteLn(Format('New %s request for %s using version %s', + [AMethod, AUrl, AVersion])); + MHD_get_connection_values(AConnection, MHD_HEADER_KIND, @PrintOutKey, nil); + Result := MHD_NO; + end; + +var + VDaemon: PMHD_Daemon; +begin + VDaemon := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, nil, nil, + @AnswerToConnection, nil, MHD_OPTION_END); + if not Assigned(VDaemon) then + Halt(1); + ReadLn; + MHD_stop_daemon(VDaemon) +end. + diff --git a/packages/libmicrohttpd/examples/magic.inc b/packages/libmicrohttpd/examples/magic.inc new file mode 100644 index 0000000000..b4a5f4e9f1 --- /dev/null +++ b/packages/libmicrohttpd/examples/magic.inc @@ -0,0 +1,15 @@ +{$PACKRECORDS C} + + magic_set = record + end; + + magic_t = ^magic_set; + +const + LIB_NAME = 'magic'; + MAGIC_MIME_TYPE = $000010; + +function magic_open(flags: cint): magic_t; cdecl; external LIB_NAME name 'magic_open'; +procedure magic_close(cookie: magic_t); cdecl; external LIB_NAME name 'magic_close'; +function magic_load(cookie: magic_t; filename: Pcchar): cint; cdecl; external LIB_NAME name 'magic_load'; +function magic_buffer(cookie: magic_t; buffer: Pointer; length: size_t): Pcchar; cdecl; external LIB_NAME name 'magic_buffer'; diff --git a/packages/libmicrohttpd/examples/minimal_example.pp b/packages/libmicrohttpd/examples/minimal_example.pp new file mode 100644 index 0000000000..ec78f0ab77 --- /dev/null +++ b/packages/libmicrohttpd/examples/minimal_example.pp @@ -0,0 +1,82 @@ +(* + This file is part of libmicrohttpd + Copyright (C) 2007 Christian Grothoff (and other contributing authors) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*) +(** + * @file minimal_example.pp (Original: minimal_example.c) + * @brief minimal example for how to use libmicrohttpd + * @author Christian Grothoff / Silvio Clécio + *) + +program minimal_example; + +{$mode objfpc}{$H+} + +uses + sysutils, cutils, libmicrohttpd; + +const + PAGE: Pcchar = '<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>'; + + function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar; + method: Pcchar; version: Pcchar; upload_data: Pcchar; + upload_data_size: Psize_t; ptr: PPointer): cint; cdecl; + const + aptr: cint = 0; + var + me: Pcchar; + response: PMHD_Response; + ret: cint; + begin + me := cls; + if 0 <> strcomp(method, 'GET') then + Exit(MHD_NO); + if @aptr <> ptr^ then + begin + ptr^ := @aptr; + Exit(MHD_YES); + end; + ptr^ := nil; + response := MHD_create_response_from_buffer(strlen(me), Pointer(me), + MHD_RESPMEM_PERSISTENT); + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + Result := ret; + end; + +var + d: PMHD_Daemon; +begin + if argc <> 2 then + begin + WriteLn(argv[0], ' PORT'); + Halt(1); + end; + d := MHD_start_daemon(// MHD_USE_SELECT_INTERNALLY or MHD_USE_DEBUG or MHD_USE_POLL, + MHD_USE_SELECT_INTERNALLY or MHD_USE_DEBUG, + // MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG or MHD_USE_POLL, + // MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG, + StrToInt(argv[1]), + nil, nil, @ahc_echo, PAGE, + MHD_OPTION_CONNECTION_TIMEOUT, cuint(120), + MHD_OPTION_END); + if d = nil then + Halt(1); + ReadLn; + MHD_stop_daemon(d); +end. + diff --git a/packages/libmicrohttpd/examples/minimal_example_comet.pp b/packages/libmicrohttpd/examples/minimal_example_comet.pp new file mode 100644 index 0000000000..292ca09a02 --- /dev/null +++ b/packages/libmicrohttpd/examples/minimal_example_comet.pp @@ -0,0 +1,81 @@ +(* + This file is part of libmicrohttpd + Copyright (C) 2007, 2008 Christian Grothoff (and other contributing authors) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*) +(** + * @file minimal_example.pp (original: minimal_example.c) + * @brief minimal example for how to generate an infinite stream with libmicrohttpd + * @author Christian Grothoff / Silvio Clécio / Gilson Nunes + *) + +program minimal_example_comet; + +{$mode objfpc}{$H+} + +uses + sysutils, cutils, libmicrohttpd; + + function data_generator(cls: Pointer; pos: cuint64; buf: Pcchar; + max: size_t): ssize_t; cdecl; + begin + if max < 80 then + Exit(0); + memset(buf, Ord('A'), max - 1); + buf[79] := #10; + Exit(80); + end; + + function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar; + method: Pcchar; version: Pcchar; upload_data: Pcchar; + upload_data_size: Psize_t; ptr: PPointer): cint; cdecl; + const + aptr: cint = 0; + var + response: PMHD_Response; + ret: cint; + begin + if 0 <> strcomp(method, 'GET') then + Exit(MHD_NO); + if @aptr <> ptr^ then + begin + ptr^ := @aptr; + Exit(MHD_YES); + end; + ptr^ := nil; + response := MHD_create_response_from_callback(UInt64(MHD_SIZE_UNKNOWN), 80, + @data_generator, nil, nil); + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + Result := ret; + end; + +var + d: PMHD_Daemon; +begin + if argc <> 2 then + begin + WriteLn(argv[0], ' PORT'); + Halt(1); + end; + d := MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG, + StrToInt(argv[1]), nil, nil, @ahc_echo, nil, MHD_OPTION_END); + if d = nil then + Halt(1); + ReadLn; + MHD_stop_daemon(d); +end. + diff --git a/packages/libmicrohttpd/examples/post_example.pp b/packages/libmicrohttpd/examples/post_example.pp new file mode 100644 index 0000000000..1bb88b6e61 --- /dev/null +++ b/packages/libmicrohttpd/examples/post_example.pp @@ -0,0 +1,640 @@ +(* + This file is part of libmicrohttpd + Copyright (C) 2011 Christian Grothoff (and other contributing authors) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*) +(** + * @file post_example.pp (Original: post_example.c) + * @brief example for processing POST requests using libmicrohttpd + * @author Christian Grothoff / Silvio Clécio + *) + +program post_example; + +{$mode objfpc}{$H+} + +uses + SysUtils, BaseUnix, cmem, cutils, libmicrohttpd; + +const + (** + * Invalid method page. + *) + METHOD_ERROR = '<html><head><title>Illegal request</title></head><body>Go away.</body></html>'; + + (** + * Invalid URL page. + *) + NOT_FOUND_ERROR = '<html><head><title>Not found</title></head><body>Go away.</body></html>'; + + (** + * Front page. (/) + *) + MAIN_PAGE = '<html><head><title>Welcome</title></head><body><form action="/2" method="post">What is your name? <input type="text" name="v1" value="%s" /><input type="submit" value="Next" /></body></html>'; + + (** + * Second page. (/2) + *) + SECOND_PAGE = '<html><head><title>Tell me more</title></head><body><a href="/">previous</a> <form action="/S" method="post">%s, what is your job? <input type="text" name="v2" value="%s" /><input type="submit" value="Next" /></body></html>'; + + (** + * Second page (/S) + *) + SUBMIT_PAGE = '<html><head><title>Ready to submit?</title></head><body><form action="/F" method="post"><a href="/2">previous </a> <input type="hidden" name="DONE" value="yes" /><input type="submit" value="Submit" /></body></html>'; + + (** + * Last page. + *) + LAST_PAGE = '<html><head><title>Thank you</title></head><body>Thank you.</body></html>'; + + (** + * Name of our cookie. + *) + COOKIE_NAME = 'session'; + +type + (** + * State we keep for each user/session/browser. + *) + PSession = ^TSession; + TSession = packed record + (** + * We keep all sessions in a linked list. + *) + next: PSession; + + (** + * Unique ID for this session. + *) + sid: array[0..33] of Char; + + (** + * Reference counter giving the number of connections + * currently using this session. + *) + rc: cint; + + (** + * Time when this session was last active. + *) + start: time_t; + + (** + * String submitted via form. + *) + value_1: array[0..64] of Char; + + (** + * Another value submitted via form. + *) + value_2: array[0..64] of Char; + end; + + (** + * Data kept per request. + *) + TRequest = packed record + + (** + * Associated session. + *) + session: PSession; + + (** + * Post processor handling form data (IF this is + * a POST request). + *) + pp: PMHD_PostProcessor; + + (** + * URL to serve in response to this POST (if this request + * was a 'POST') + *) + post_url: pcchar; + + end; + PRequest = ^TRequest; + +var + (** + * Linked list of all active sessions. Yes, O(n) but a + * hash table would be overkill for a simple example... + *) + _sessions: PSession; + + (** + * Return the session handle for this connection, or + * create one if this is a new user. + *) + function get_session(connection: PMHD_Connection): PSession; + var + ret: PSession; + cookie: pcchar; + begin + cookie := MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, COOKIE_NAME); + if cookie <> nil then + begin + (* find existing session *) + ret := _sessions; + while nil <> ret do + begin + if StrComp(cookie, ret^.sid) = 0 then + Break; + ret := ret^.next; + end; + if nil <> ret then + begin + Inc(ret^.rc); + Exit(ret); + end; + end; + (* create fresh session *) + ret := CAlloc(1, SizeOf(TSession)); + if nil = ret then + begin + WriteLn(stderr, 'calloc error: ', strerror(errno^)); + Exit(nil); + end; + (* not a super-secure way to generate a random session ID, + but should do for a simple example... *) + snprintf(ret^.sid, SizeOf(ret^.sid), '%X%X%X%X', Cardinal(rand), + Cardinal(rand), Cardinal(rand), Cardinal(rand)); + Inc(ret^.rc); + ret^.start := FpTime; + ret^.next := _sessions; + _sessions := ret; + Result := ret; + end; + +(** + * Type of handler that generates a reply. + * + * @param cls content for the page (handler-specific) + * @param mime mime type to use + * @param session session information + * @param connection connection to process + * @param MHD_YES on success, MHD_NO on failure + *) +type + TPageHandler = function(cls: Pointer; mime: Pcchar; session: PSession; + connection: PMHD_Connection): LongInt; cdecl; + + (** + * Entry we generate for each page served. + *) + TPage = packed record + (** + * Acceptable URL for this page. + *) + url: Pcchar; + + (** + * Mime type to set for the page. + *) + mime: Pcchar; + + (** + * Handler to call to generate response. + *) + handler: TPageHandler; + + (** + * Extra argument to handler. + *) + handler_cls: Pcchar; + end; + + (** + * Add header to response to set a session cookie. + * + * @param session session to use + * @param response response to modify + *) + procedure add_session_cookie(session: PSession; response: PMHD_Response); + var + cstr: array[0..256] of Char; + begin + snprintf(cstr, SizeOf(cstr), '%s=%s', COOKIE_NAME, session^.sid); + if MHD_NO = + MHD_add_response_header(response, MHD_HTTP_HEADER_SET_COOKIE, cstr) then + WriteLn(stderr, 'Failed to set session cookie header!'); + end; + + (** + * Handler that returns a simple static HTTP page that + * is passed in via 'cls'. + * + * @param cls a 'const char *' with the HTML webpage to return + * @param mime mime type to use + * @param session session handle + * @param connection connection to use + *) + function serve_simple_form(cls: Pointer; mime: Pcchar; session: PSession; + connection: PMHD_Connection): cint; cdecl; + var + ret: cint; + form: Pcchar; + response: PMHD_Response; + begin + form := cls; + (* return static form *) + response := MHD_create_response_from_buffer(Length(form), Pointer(form), + MHD_RESPMEM_PERSISTENT); + add_session_cookie(session, response); + MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_ENCODING, mime); + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + Result := ret; + end; + + (** + * Handler that adds the 'v1' value to the given HTML code. + * + * @param cls unused + * @param mime mime type to use + * @param session session handle + * @param connection connection to use + *) + function fill_v1_form(cls: Pointer; mime: Pcchar; session: PSession; + connection: PMHD_Connection): cint; cdecl; + var + ret: cint; + reply: Pcchar; + response: PMHD_Response; + begin + reply := Malloc(strlen(MAIN_PAGE) + strlen(session^.value_1) + 1); + if nil = reply then + Exit(MHD_NO); + snprintf (reply, strlen(MAIN_PAGE) + strlen(session^.value_1) + 1, + MAIN_PAGE, session^.value_1); + (* return static form *) + response := MHD_create_response_from_buffer (strlen(reply), Pointer(reply), + MHD_RESPMEM_MUST_FREE); + if nil = response then + Exit(MHD_NO); + add_session_cookie(session, response); + MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_ENCODING, mime); + ret := MHD_queue_response (connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + Result := ret; + end; + + (** + * Handler that adds the 'v1' and 'v2' values to the given HTML code. + * + * @param cls unused + * @param mime mime type to use + * @param session session handle + * @param connection connection to use + *) + function fill_v1_v2_form(cls: Pointer; mime: Pcchar; session: PSession; + connection: PMHD_Connection): cint; cdecl; + var + ret: cint; + reply: Pcchar; + response: PMHD_Response; + begin + reply := Malloc(strlen(SECOND_PAGE) + strlen(session^.value_1) + + strlen(session^.value_2) + 1); + if nil = reply then + Exit(MHD_NO); + snprintf(reply, strlen(SECOND_PAGE) + strlen(session^.value_1) + + strlen(session^.value_2) + 1, SECOND_PAGE, session^.value_1, + session^.value_2); + (* return static form *) + response := MHD_create_response_from_buffer(strlen(reply), Pointer(reply), + MHD_RESPMEM_MUST_FREE); + if nil = response then + Exit(MHD_NO); + add_session_cookie(session, response); + MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_ENCODING, mime); + ret := MHD_queue_response (connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + Result := ret; + end; + + (** + * Handler used to generate a 404 reply. + * + * @param cls a 'const char *' with the HTML webpage to return + * @param mime mime type to use + * @param session session handle + * @param connection connection to use + *) + function not_found_page(cls: Pointer; mime: Pcchar; session: PSession; + connection: PMHD_Connection): cint; cdecl; + var + ret: cint; + response: PMHD_Response; + begin + (* unsupported HTTP method *) + response := MHD_create_response_from_buffer(Length(NOT_FOUND_ERROR), + Pcchar(NOT_FOUND_ERROR), MHD_RESPMEM_PERSISTENT); + ret := MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response); + MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_ENCODING, mime); + MHD_destroy_response(response); + Result := ret; + end; + +const + (** + * List of all pages served by this HTTP server. + *) + pages: array[0..4] of TPage = ( + (url: '/'; mime: 'text/html'; handler: @fill_v1_form; handler_cls: nil), + (url: '/2'; mime: 'text/html'; handler: @fill_v1_v2_form; handler_cls: nil), + (url: '/S'; mime: 'text/html'; handler: @serve_simple_form; handler_cls: SUBMIT_PAGE), + (url: '/F'; mime: 'text/html'; handler: @serve_simple_form; handler_cls: LAST_PAGE), + (url: nil; mime: nil; handler: @not_found_page; handler_cls: nil) (* 404 *) + ); + + (** + * Iterator over key-value pairs where the value + * maybe made available in increments and/or may + * not be zero-terminated. Used for processing + * POST data. + * + * @param cls user-specified closure + * @param kind type of the value + * @param key 0-terminated key for the value + * @param filename name of the uploaded file, NULL if not known + * @param content_type mime-type of the data, NULL if not known + * @param transfer_encoding encoding of the data, NULL if not known + * @param data pointer to size bytes of data at the + * specified offset + * @param off offset of data in the overall value + * @param size number of bytes in data available + * @return MHD_YES to continue iterating, + * MHD_NO to abort the iteration + *) + function post_iterator(cls: Pointer; kind: MHD_ValueKind; key: Pcchar; + filename: Pcchar; content_type: Pcchar; transfer_encoding: Pcchar; + data: Pcchar; off: cuint64; size: size_t): cint; cdecl; + var + request: PRequest; + session: PSession; + begin + request := cls; + session := request^.session; + if StrComp('DONE', key) = 0 then + begin + WriteLn(stdout, Format('Session `%s'' submitted `%s'', `%s''', [ + session^.sid, session^.value_1, session^.value_2])); + Exit(MHD_YES); + end; + if StrComp('v1', key) = 0 then + begin + if (size + off) > SizeOf(session^.value_1) then + size := SizeOf(session^.value_1) - off - 1; + Move(data^, session^.value_1[off], size); + if (size + off) < SizeOf(session^.value_1) then + session^.value_1[size + off] := #0; + Exit(MHD_YES); + end; + if StrComp('v2', key) = 0 then + begin + if (size + off) > SizeOf(session^.value_2) then + size := SizeOf(session^.value_2) - off - 1; + Move(data^, session^.value_2[off], size); + if (size + off) < SizeOf(session^.value_2) then + session^.value_2[size + off] := #0; + Exit(MHD_YES); + end; + WriteLn(stderr, Format('Unsupported form value `%s''', [key])); + Result := MHD_YES; + end; + + (** + * Main MHD callback for handling requests. + * + * + * @param cls argument given together with the function + * pointer when the handler was registered with MHD + * @param connection handle to connection which is being processed + * @param url the requested url + * @param method the HTTP method used ("GET", "PUT", etc.) + * @param version the HTTP version string (i.e. "HTTP/1.1") + * @param upload_data the data being uploaded (excluding HEADERS, + * for a POST that fits into memory and that is encoded + * with a supported encoding, the POST data will NOT be + * given in upload_data and is instead available as + * part of MHD_get_connection_values; very large POST + * data *will* be made available incrementally in + * upload_data) + * @param upload_data_size set initially to the size of the + * upload_data provided; the method must update this + * value to the number of bytes NOT processed; + * @param ptr pointer that the callback can set to some + * address and that will be preserved by MHD for future + * calls for this request; since the access handler may + * be called many times (i.e., for a PUT/POST operation + * with plenty of upload data) this allows the application + * to easily associate some request-specific state. + * If necessary, this state can be cleaned up in the + * global "MHD_RequestCompleted" callback (which + * can be set with the MHD_OPTION_NOTIFY_COMPLETED). + * Initially, <tt>*con_cls</tt> will be NULL. + * @return MHS_YES if the connection was handled successfully, + * MHS_NO if the socket must be closed due to a serios + * error while handling the request + *) + function create_response(cls: Pointer; connection: PMHD_Connection; + url: Pcchar; method: Pcchar; version: Pcchar; upload_data: Pcchar; + upload_data_size: Psize_t; ptr: PPointer): cint; cdecl; + var + response: PMHD_Response; + request: PRequest; + session: PSession; + ret: cint; + i: Cardinal; + begin + request := ptr^; + if nil = request then + begin + request := CAlloc(1, SizeOf(TRequest)); + if nil = request then + begin + WriteLn(stderr, 'calloc error: ', strerror(errno^)); + Exit(MHD_NO); + end; + ptr^ := request; + if StrComp(method, MHD_HTTP_METHOD_POST) = 0 then + begin + request^.pp := MHD_create_post_processor(connection, 1024, + @post_iterator, request); + if nil = request^.pp then + begin + WriteLn(stderr, Format('Failed to setup post processor for `%s''', + [url])); + Exit(MHD_NO); (* internal error *) + end; + end; + Exit(MHD_YES); + end; + if nil = request^.session then + begin + request^.session := get_session(connection); + if nil = request^.session then + begin + WriteLn(stderr, Format('Failed to setup session for `%s''', [url])); + Exit(MHD_NO); (* internal error *) + end; + end; + session := request^.session; + session^.start := FpTime; + if StrComp(method, MHD_HTTP_METHOD_POST) = 0 then + begin + (* evaluate POST data *) + MHD_post_process(request^.pp, upload_data, upload_data_size^); + if upload_data_size^ <> 0 then + begin + upload_data_size^ := 0; + Exit(MHD_YES); + end; + (* done with POST data, serve response *) + MHD_destroy_post_processor(request^.pp); + request^.pp := nil; + method := MHD_HTTP_METHOD_GET; (* fake 'GET' *) + if nil <> request^.post_url then + url := request^.post_url; + end; + if (StrComp(method, MHD_HTTP_METHOD_GET) = 0) or + (StrComp(method, MHD_HTTP_METHOD_HEAD) = 0) then + begin + (* find out which page to serve *) + i := 0; + while (pages[i].url <> nil) and (StrComp(pages[i].url, url) <> 0) do + Inc(i); + ret := pages[i].handler(pages[i].handler_cls, pages[i].mime, session, + connection); + if ret <> MHD_YES then + WriteLn(stderr, Format('Failed to create page for `%s''', [url])); + Exit(ret); + end; + (* unsupported HTTP method *) + response := MHD_create_response_from_buffer(Length(METHOD_ERROR), + Pcchar(METHOD_ERROR), MHD_RESPMEM_PERSISTENT); + ret := MHD_queue_response(connection, MHD_HTTP_NOT_ACCEPTABLE, response); + MHD_destroy_response(response); + Result := ret; + end; + + (** + * Callback called upon completion of a request. + * Decrements session reference counter. + * + * @param cls not used + * @param connection connection that completed + * @param con_cls session handle + * @param toe status code + *) + procedure request_completed_callback(cls: Pointer; connection: PMHD_Connection; + con_cls: PPointer; toe: MHD_RequestTerminationCode); + var + request: PRequest; + begin + request := con_cls^; + if nil = request then + Exit; + if nil <> request^.session then + Dec(request^.session^.rc); + if nil <> request^.pp then + MHD_destroy_post_processor(request^.pp); + Free(request); + end; + + (** + * Clean up handles of sessions that have been idle for + * too long. + *) + procedure expire_sessions; + var + pos: PSession; + prev: PSession; + next: PSession; + now: time_t; + begin + now := FpTime; + prev := nil; + pos := _sessions; + while nil <> pos do + begin + next := pos^.next; + if (now - pos^.start) > (60 * 60) then + begin + (* expire sessions after 1h *) + if nil = prev then + _sessions := pos^.next + else + prev^.next := next; + Free(pos); + end + else + prev := pos; + pos := next; + end; + end; + +(** + * Call with the port number as the only argument. + * Never terminates (other than by signals, such as CTRL-C). + *) +var + d: PMHD_Daemon; + tv: timeval; + tvp: ptimeval; + rs: TFDSet; + ws: TFDSet; + es: TFDSet; + max: cint; + mhd_timeout: MHD_UNSIGNED_LONG_LONG; +begin + if argc <> 2 then + begin + WriteLn(argv[0], ' PORT'); + Halt(1); + end; + (* initialize PRNG *) + Randomize; + + d := MHD_start_daemon(MHD_USE_DEBUG, StrToInt(argv[1]), nil, nil, + @create_response, nil, MHD_OPTION_CONNECTION_TIMEOUT, cuint(15), + MHD_OPTION_NOTIFY_COMPLETED, @request_completed_callback, nil, MHD_OPTION_END); + if nil = d then + Halt(1); + + while True do + begin + expire_sessions; + max := 0; + fpFD_ZERO(rs); + fpFD_ZERO(ws); + fpFD_ZERO(es); + if MHD_YES <> MHD_get_fdset(d, @rs, @ws, @es, @max) then + Break; (* fatal internal error *) + if MHD_get_timeout(d, @mhd_timeout) = MHD_YES then + begin + tv.tv_sec := mhd_timeout div 1000; + tv.tv_usec := (mhd_timeout - (tv.tv_sec * 1000)) * 1000; + tvp := @tv; + end + else + tvp := nil; + fpSelect(max + 1, @rs, @ws, @es, tvp); + MHD_run(d); + end; + MHD_stop_daemon(d); +end. diff --git a/packages/libmicrohttpd/examples/querystring_example.pp b/packages/libmicrohttpd/examples/querystring_example.pp new file mode 100644 index 0000000000..e95f4e96d9 --- /dev/null +++ b/packages/libmicrohttpd/examples/querystring_example.pp @@ -0,0 +1,89 @@ +(* + This file is part of libmicrohttpd + Copyright (C) 2007, 2008 Christian Grothoff (and other contributing authors) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*) +(** + * @file querystring_example.pp (Original: querystring_example.c) + * @brief example for how to get the query string from libmicrohttpd + * Call with an URI ending with something like "?q=QUERY" + * @author Christian Grothoff / Silvio Clécio + *) + +program querystring_example; + +{$mode objfpc}{$H+} + +uses + sysutils, cmem, ctypes, cutils, libmicrohttpd; + +const + PAGE: Pcchar = '<html><head><title>libmicrohttpd demo</title></head><body>Query string for "%s" was "%s"</body></html>'; + + function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar; + method: Pcchar; version: Pcchar; upload_data: Pcchar; + upload_data_size: Psize_t; ptr: PPointer): cint; cdecl; + const + aptr: cint = 0; + var + fmt: Pcchar; + val: Pcchar; + me: Pcchar; + response: PMHD_Response; + ret: cint; + begin + fmt := cls; + if 0 <> strcomp(method, 'GET') then + Exit(MHD_NO); + if @aptr <> ptr^ then + begin + ptr^ := @aptr; + Exit(MHD_YES); + end; + ptr^ := nil; + val := MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, 'q'); + me := Malloc(snprintf(nil, 0, fmt, Pcchar('q'), val) + 1); + if me = nil then + Exit(MHD_NO); + sprintf(me, fmt, Pcchar('q'), val); + response := MHD_create_response_from_buffer(strlen(me), Pointer(me), + MHD_RESPMEM_MUST_FREE); + if response = nil then + begin + Free(me); + Exit(MHD_NO); + end; + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + Result := ret; + end; + +var + d: PMHD_Daemon; +begin + if argc <> 2 then + begin + WriteLn(argv[0], ' PORT'); + Halt(1); + end; + d := MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG, + StrToInt(argv[1]), nil, nil, @ahc_echo, PAGE, MHD_OPTION_END); + if d = nil then + Halt(1); + ReadLn; + MHD_stop_daemon(d); +end. + diff --git a/packages/libmicrohttpd/examples/refuse_post_example.pp b/packages/libmicrohttpd/examples/refuse_post_example.pp new file mode 100644 index 0000000000..1bad4f6f64 --- /dev/null +++ b/packages/libmicrohttpd/examples/refuse_post_example.pp @@ -0,0 +1,94 @@ +(* + This file is part of libmicrohttpd + Copyright (C) 2007, 2008 Christian Grothoff (and other contributing authors) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*) +(** + * @file refuse_post_example.pp (Original: refuse_post_example.c) + * @brief example for how to refuse a POST request properly + * @author Christian Grothoff, Sebastian Gerhardt and Silvio Clécio + *) + +program refuse_post_example; + +{$mode objfpc}{$H+} + +uses + sysutils, libmicrohttpd; + +const + askpage: Pcchar = + '<html><body>'#10+ + 'Upload a file, please!<br>'#10+ + '<form action="/filepost" method="post" enctype="multipart/form-data">'#10+ + '<input name="file" type="file">'#10+ + '<input type="submit" value=" Send "></form>'#10+ + '</body></html>'; + + BUSYPAGE: Pcchar = '<html><head><title>Webserver busy</title></head><body>We are too busy to process POSTs right now.</body></html>'; + + function ahc_echo(cls: Pointer; connection: PMHD_Connection; url: Pcchar; + method: Pcchar; version: Pcchar; upload_data: Pcchar; + upload_data_size: Psize_t; ptr: PPointer): cint; cdecl; + const + aptr: cint = 0; + var + me: Pcchar; + response: PMHD_Response; + ret: cint; + begin + me := cls; + if (0 <> strcomp(method, 'GET')) and (0 <> strcomp(method, 'POST')) then + Exit(MHD_NO); (* unexpected method *) + if @aptr <> ptr^ then + begin + ptr^ := @aptr; + (* always to busy for POST requests *) + if 0 = strcomp(method, 'POST') then + begin + response := MHD_create_response_from_buffer(strlen(BUSYPAGE), + Pointer(BUSYPAGE), MHD_RESPMEM_PERSISTENT); + ret := MHD_queue_response (connection, MHD_HTTP_SERVICE_UNAVAILABLE, + response); + MHD_destroy_response (response); + Exit(ret); + end; + end; + ptr^ := nil; (* reset when done *) + response := MHD_create_response_from_buffer(strlen(me), Pointer(me), + MHD_RESPMEM_PERSISTENT); + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + Result := ret; + end; + +var + d: PMHD_Daemon; +begin + if argc <> 2 then + begin + WriteLn(argv[0], ' PORT'); + Halt(1); + end; + d := MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION or MHD_USE_DEBUG, + StrToInt(argv[1]), nil, nil, @ahc_echo, Pointer(askpage), + MHD_OPTION_END); + if d = nil then + Halt(1); + ReadLn; + MHD_stop_daemon(d); +end. + diff --git a/packages/libmicrohttpd/examples/responseheaders.pp b/packages/libmicrohttpd/examples/responseheaders.pp new file mode 100644 index 0000000000..e7fc0ca566 --- /dev/null +++ b/packages/libmicrohttpd/examples/responseheaders.pp @@ -0,0 +1,66 @@ +(* Feel free to use this example code in any way + you see fit (Public Domain) *) + +// Original example: https://gnunet.org/svn/libmicrohttpd/doc/examples/responseheaders.c + +program responseheaders; + +{$mode objfpc}{$H+} + +uses + BaseUnix, SysUtils, libmicrohttpd; + +const + PORT = 8888; + FILENAME = 'picture.png'; + MIMETYPE = 'image/png'; + + function AnswerToConnection(ACls: Pointer; AConnection: PMHD_Connection; + AUrl: Pcchar; AMethod: Pcchar; AVersion: Pcchar; AUploadData: Pcchar; + AUploadDataSize: Psize_t; AConCls: PPointer): cint; cdecl; + const + errorstr: Pcchar = '<html><body>An internal server error has occured!</body></html>'; + var + VFd: cint; + VReturn: cint; + VResponse: PMHD_Response; + VSbuf: TStat; + begin + if StrComp(AMethod, 'GET') <> 0 then + Exit(MHD_NO); + VFd := FpOpen(FILENAME, O_RDONLY); + VSbuf := Default(TStat); + if (VFd = -1) or (FpFStat(VFd, VSbuf) <> 0) then + begin + (* error accessing file *) + if VFd <> -1 then + FpClose(VFd); + VResponse := MHD_create_response_from_buffer(Length(errorstr), + Pointer(errorstr), MHD_RESPMEM_PERSISTENT); + if Assigned(VResponse) then + begin + VReturn := MHD_queue_response(AConnection, + MHD_HTTP_INTERNAL_SERVER_ERROR, VResponse); + MHD_destroy_response(VResponse); + Exit(VReturn); + end + else + Exit(MHD_NO); + end; + VResponse := MHD_create_response_from_fd_at_offset64(VSbuf.st_size, VFd, 0); + MHD_add_response_header(VResponse, 'Content-Type', MIMETYPE); + VReturn := MHD_queue_response(AConnection, MHD_HTTP_OK, VResponse); + MHD_destroy_response(VResponse); + Result := VReturn; + end; + +var + VDaemon: PMHD_Daemon; +begin + VDaemon := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, nil, + nil, @AnswerToConnection, nil, MHD_OPTION_END); + if not Assigned(VDaemon) then + Halt(1); + ReadLn; + MHD_stop_daemon(VDaemon); +end. diff --git a/packages/libmicrohttpd/examples/sessions.pp b/packages/libmicrohttpd/examples/sessions.pp new file mode 100644 index 0000000000..0539b6703b --- /dev/null +++ b/packages/libmicrohttpd/examples/sessions.pp @@ -0,0 +1,623 @@ +(* Feel free to use this example code in any way + you see fit (Public Domain) *) + +// Original example: https://gnunet.org/svn/libmicrohttpd/doc/examples/sessions.c + +program sessions; + +{$mode objfpc}{$H+} + +uses + SysUtils, BaseUnix, cmem, cutils, libmicrohttpd; + +const + (** + * Invalid method page. + *) + METHOD_ERROR = '<html><head><title>Illegal request</title></head><body>Go away.</body></html>'; + + (** + * Invalid URL page. + *) + NOT_FOUND_ERROR = '<html><head><title>Not found</title></head><body>Go away.</body></html>'; + + (** + * Front page. (/) + *) + MAIN_PAGE = '<html><head><title>Welcome</title></head><body><form action="/2" method="post">What is your name? <input type="text" name="v1" value="%s" /><input type="submit" value="Next" /></body></html>'; + + (** + * Second page. (/2) + *) + SECOND_PAGE = '<html><head><title>Tell me more</title></head><body><a href="/">previous</a> <form action="/S" method="post">%s, what is your job? <input type="text" name="v2" value="%s" /><input type="submit" value="Next" /></body></html>'; + + (** + * Second page (/S) + *) + SUBMIT_PAGE = '<html><head><title>Ready to submit?</title></head><body><form action="/F" method="post"><a href="/2">previous </a> <input type="hidden" name="DONE" value="yes" /><input type="submit" value="Submit" /></body></html>'; + + (** + * Last page. + *) + LAST_PAGE = '<html><head><title>Thank you</title></head><body>Thank you.</body></html>'; + + (** + * Name of our cookie. + *) + COOKIE_NAME = 'session'; + +type + (** + * State we keep for each user/session/browser. + *) + PSession = ^TSession; + TSession = packed record + (** + * We keep all sessions in a linked list. + *) + next: PSession; + + (** + * Unique ID for this session. + *) + sid: array[0..33] of Char; + + (** + * Reference counter giving the number of connections + * currently using this session. + *) + rc: cint; + + (** + * Time when this session was last active. + *) + start: time_t; + + (** + * String submitted via form. + *) + value_1: array[0..64] of Char; + + (** + * Another value submitted via form. + *) + value_2: array[0..64] of Char; + end; + + (** + * Data kept per request. + *) + TRequest = packed record + + (** + * Associated session. + *) + session: PSession; + + (** + * Post processor handling form data (IF this is + * a POST request). + *) + pp: PMHD_PostProcessor; + + (** + * URL to serve in response to this POST (if this request + * was a 'POST') + *) + post_url: pcchar; + + end; + PRequest = ^TRequest; + +var + (** + * Linked list of all active sessions. Yes, O(n) but a + * hash table would be overkill for a simple example... + *) + _sessions: PSession; + + (** + * Return the session handle for this connection, or + * create one if this is a new user. + *) + function get_session(connection: PMHD_Connection): PSession; + var + ret: PSession; + cookie: pcchar; + begin + cookie := MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, COOKIE_NAME); + if cookie <> nil then + begin + (* find existing session *) + ret := _sessions; + while nil <> ret do + begin + if StrComp(cookie, ret^.sid) = 0 then + Break; + ret := ret^.next; + end; + if nil <> ret then + begin + Inc(ret^.rc); + Exit(ret); + end; + end; + (* create fresh session *) + ret := CAlloc(1, SizeOf(TSession)); + if nil = ret then + begin + WriteLn(stderr, 'calloc error: ', strerror(errno^)); + Exit(nil); + end; + (* not a super-secure way to generate a random session ID, + but should do for a simple example... *) + snprintf(ret^.sid, SizeOf(ret^.sid), '%X%X%X%X', Cardinal(rand), + Cardinal(rand), Cardinal(rand), Cardinal(rand)); + Inc(ret^.rc); + ret^.start := FpTime; + ret^.next := _sessions; + _sessions := ret; + Result := ret; + end; + +(** + * Type of handler that generates a reply. + * + * @param cls content for the page (handler-specific) + * @param mime mime type to use + * @param session session information + * @param connection connection to process + * @param MHD_YES on success, MHD_NO on failure + *) +type + TPageHandler = function(cls: Pointer; mime: Pcchar; session: PSession; + connection: PMHD_Connection): LongInt; cdecl; + + (** + * Entry we generate for each page served. + *) + + { TPage } + + TPage = packed record + (** + * Acceptable URL for this page. + *) + url: Pcchar; + + (** + * Mime type to set for the page. + *) + mime: Pcchar; + + (** + * Handler to call to generate response. + *) + handler: TPageHandler; + + (** + * Extra argument to handler. + *) + handler_cls: Pcchar; + end; + + (** + * Add header to response to set a session cookie. + * + * @param session session to use + * @param response response to modify + *) + procedure add_session_cookie(session: PSession; response: PMHD_Response); + var + cstr: array[0..256] of Char; + begin + snprintf(cstr, SizeOf(cstr), '%s=%s', COOKIE_NAME, session^.sid); + if MHD_NO = + MHD_add_response_header(response, MHD_HTTP_HEADER_SET_COOKIE, cstr) then + WriteLn(stderr, 'Failed to set session cookie header!'); + end; + + (** + * Handler that returns a simple static HTTP page that + * is passed in via 'cls'. + * + * @param cls a 'const char *' with the HTML webpage to return + * @param mime mime type to use + * @param session session handle + * @param connection connection to use + *) + function serve_simple_form(cls: Pointer; mime: Pcchar; session: PSession; + connection: PMHD_Connection): cint; cdecl; + var + ret: cint; + form: Pcchar; + response: PMHD_Response; + begin + form := cls; + (* return static form *) + response := MHD_create_response_from_buffer(Length(form), Pointer(form), + MHD_RESPMEM_PERSISTENT); + add_session_cookie(session, response); + MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_ENCODING, mime); + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + Result := ret; + end; + + (** + * Handler that adds the 'v1' value to the given HTML code. + * + * @param cls a 'const char *' with the HTML webpage to return + * @param mime mime type to use + * @param session session handle + * @param connection connection to use + *) + function fill_v1_form(cls: Pointer; mime: Pcchar; session: PSession; + connection: PMHD_Connection): cint; cdecl; + var + ret: cint; + form: Pcchar; + reply: Pcchar; + response: PMHD_Response; + begin + form := cls; + if asprintf(@reply, form, session^.value_1) = -1 then + (* oops *) + Exit(MHD_NO); + (* return static form *) + response := MHD_create_response_from_buffer(Length(reply), Pointer(reply), + MHD_RESPMEM_MUST_FREE); + add_session_cookie(session, response); + MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_ENCODING, mime); + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + Result := ret; + end; + + (** + * Handler that adds the 'v1' and 'v2' values to the given HTML code. + * + * @param cls a 'const char *' with the HTML webpage to return + * @param mime mime type to use + * @param session session handle + * @param connection connection to use + *) + function fill_v1_v2_form(cls: Pointer; mime: Pcchar; session: PSession; + connection: PMHD_Connection): cint; cdecl; + var + ret: cint; + form: Pcchar; + reply: Pcchar; + response: PMHD_Response; + begin + form := cls; + if asprintf(@reply, form, session^.value_1, session^.value_2) = -1 then + (* oops *) + Exit(MHD_NO); + (* return static form *) + response := MHD_create_response_from_buffer(Length(reply), Pointer(reply), + MHD_RESPMEM_MUST_FREE); + add_session_cookie(session, response); + MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_ENCODING, mime); + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + Result := ret; + end; + + (** + * Handler used to generate a 404 reply. + * + * @param cls a 'const char *' with the HTML webpage to return + * @param mime mime type to use + * @param session session handle + * @param connection connection to use + *) + function not_found_page(cls: Pointer; mime: Pcchar; session: PSession; + connection: PMHD_Connection): cint; cdecl; + var + ret: cint; + response: PMHD_Response; + begin + (* unsupported HTTP method *) + response := MHD_create_response_from_buffer(Length(NOT_FOUND_ERROR), + Pcchar(NOT_FOUND_ERROR), MHD_RESPMEM_PERSISTENT); + ret := MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response); + MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_ENCODING, mime); + MHD_destroy_response(response); + Result := ret; + end; + +const + (** + * List of all pages served by this HTTP server. + *) + pages: array[0..4] of TPage = ( + (url: '/'; mime: 'text/html'; handler: @fill_v1_form; handler_cls: MAIN_PAGE), + (url: '/2'; mime: 'text/html'; handler: @fill_v1_v2_form; handler_cls: SECOND_PAGE), + (url: '/S'; mime: 'text/html'; handler: @serve_simple_form; handler_cls: SUBMIT_PAGE), + (url: '/F'; mime: 'text/html'; handler: @serve_simple_form; handler_cls: LAST_PAGE), + (url: nil; mime: nil; handler: @not_found_page; handler_cls: nil) (* 404 *) + ); + + (** + * Iterator over key-value pairs where the value + * maybe made available in increments and/or may + * not be zero-terminated. Used for processing + * POST data. + * + * @param cls user-specified closure + * @param kind type of the value + * @param key 0-terminated key for the value + * @param filename name of the uploaded file, NULL if not known + * @param content_type mime-type of the data, NULL if not known + * @param transfer_encoding encoding of the data, NULL if not known + * @param data pointer to size bytes of data at the + * specified offset + * @param off offset of data in the overall value + * @param size number of bytes in data available + * @return MHD_YES to continue iterating, + * MHD_NO to abort the iteration + *) + function post_iterator(cls: Pointer; kind: MHD_ValueKind; key: Pcchar; + filename: Pcchar; content_type: Pcchar; transfer_encoding: Pcchar; + data: Pcchar; off: cuint64; size: size_t): cint; cdecl; + var + request: PRequest; + session: PSession; + begin + request := cls; + session := request^.session; + if StrComp('DONE', key) = 0 then + begin + WriteLn(stdout, Format('Session `%s'' submitted `%s'', `%s''', [ + session^.sid, session^.value_1, session^.value_2])); + Exit(MHD_YES); + end; + if StrComp('v1', key) = 0 then + begin + if (size + off) > SizeOf(session^.value_1) then + size := SizeOf(session^.value_1) - off; + Move(data^, session^.value_1[off], size); + if (size + off) < SizeOf(session^.value_1) then + session^.value_1[size + off] := #0; + Exit(MHD_YES); + end; + if StrComp('v2', key) = 0 then + begin + if (size + off) > SizeOf(session^.value_2) then + size := SizeOf(session^.value_2) - off; + Move(data^, session^.value_2[off], size); + if (size + off) < SizeOf(session^.value_2) then + session^.value_2[size + off] := #0; + Exit(MHD_YES); + end; + WriteLn(stderr, Format('Unsupported form value `%s''', [key])); + Result := MHD_YES; + end; + + (** + * Main MHD callback for handling requests. + * + * + * @param cls argument given together with the function + * pointer when the handler was registered with MHD + * @param connection handle to connection which is being processed + * @param url the requested url + * @param method the HTTP method used ("GET", "PUT", etc.) + * @param version the HTTP version string (i.e. "HTTP/1.1") + * @param upload_data the data being uploaded (excluding HEADERS, + * for a POST that fits into memory and that is encoded + * with a supported encoding, the POST data will NOT be + * given in upload_data and is instead available as + * part of MHD_get_connection_values; very large POST + * data *will* be made available incrementally in + * upload_data) + * @param upload_data_size set initially to the size of the + * upload_data provided; the method must update this + * value to the number of bytes NOT processed; + * @param ptr pointer that the callback can set to some + * address and that will be preserved by MHD for future + * calls for this request; since the access handler may + * be called many times (i.e., for a PUT/POST operation + * with plenty of upload data) this allows the application + * to easily associate some request-specific state. + * If necessary, this state can be cleaned up in the + * global "MHD_RequestCompleted" callback (which + * can be set with the MHD_OPTION_NOTIFY_COMPLETED). + * Initially, <tt>*con_cls</tt> will be NULL. + * @return MHS_YES if the connection was handled successfully, + * MHS_NO if the socket must be closed due to a serios + * error while handling the request + *) + function create_response(cls: Pointer; connection: PMHD_Connection; + url: Pcchar; method: Pcchar; version: Pcchar; upload_data: Pcchar; + upload_data_size: Psize_t; ptr: PPointer): cint; cdecl; + var + response: PMHD_Response; + request: PRequest; + session: PSession; + ret: cint; + i: Cardinal; + begin + request := ptr^; + if nil = request then + begin + request := CAlloc(1, SizeOf(TRequest)); + if nil = request then + begin + WriteLn(stderr, 'calloc error: ', strerror(errno^)); + Exit(MHD_NO); + end; + ptr^ := request; + if StrComp(method, MHD_HTTP_METHOD_POST) = 0 then + begin + request^.pp := MHD_create_post_processor(connection, 1024, + @post_iterator, request); + if nil = request^.pp then + begin + WriteLn(stderr, Format('Failed to setup post processor for `%s''', + [url])); + Exit(MHD_NO); (* internal error *) + end; + end; + Exit(MHD_YES); + end; + if nil = request^.session then + begin + request^.session := get_session(connection); + if nil = request^.session then + begin + WriteLn(stderr, Format('Failed to setup session for `%s''', [url])); + Exit(MHD_NO); (* internal error *) + end; + end; + session := request^.session; + session^.start := FpTime; + if StrComp(method, MHD_HTTP_METHOD_POST) = 0 then + begin + (* evaluate POST data *) + MHD_post_process(request^.pp, upload_data, upload_data_size^); + if upload_data_size^ <> 0 then + begin + upload_data_size^ := 0; + Exit(MHD_YES); + end; + (* done with POST data, serve response *) + MHD_destroy_post_processor(request^.pp); + request^.pp := nil; + method := MHD_HTTP_METHOD_GET; (* fake 'GET' *) + if nil <> request^.post_url then + url := request^.post_url; + end; + if (StrComp(method, MHD_HTTP_METHOD_GET) = 0) or + (StrComp(method, MHD_HTTP_METHOD_HEAD) = 0) then + begin + (* find out which page to serve *) + i := 0; + while (pages[i].url <> nil) and (StrComp(pages[i].url, url) <> 0) do + Inc(i); + ret := pages[i].handler(pages[i].handler_cls, pages[i].mime, session, + connection); + if ret <> MHD_YES then + WriteLn(stderr, Format('Failed to create page for `%s''', [url])); + Exit(ret); + end; + (* unsupported HTTP method *) + response := MHD_create_response_from_buffer(Length(METHOD_ERROR), + Pcchar(METHOD_ERROR), MHD_RESPMEM_PERSISTENT); + ret := MHD_queue_response(connection, MHD_HTTP_NOT_ACCEPTABLE, response); + MHD_destroy_response(response); + Result := ret; + end; + + (** + * Callback called upon completion of a request. + * Decrements session reference counter. + * + * @param cls not used + * @param connection connection that completed + * @param con_cls session handle + * @param toe status code + *) + procedure request_completed_callback(cls: Pointer; connection: PMHD_Connection; + con_cls: PPointer; toe: MHD_RequestTerminationCode); + var + request: PRequest; + begin + request := con_cls^; + if nil = request then + Exit; + if nil <> request^.session then + Dec(request^.session^.rc); + if nil <> request^.pp then + MHD_destroy_post_processor(request^.pp); + Free(request); + end; + + (** + * Clean up handles of sessions that have been idle for + * too long. + *) + procedure expire_sessions; + var + pos: PSession; + prev: PSession; + next: PSession; + now: time_t; + begin + now := FpTime; + prev := nil; + pos := _sessions; + while nil <> pos do + begin + next := pos^.next; + if (now - pos^.start) > (60 * 60) then + begin + (* expire sessions after 1h *) + if nil = prev then + _sessions := pos^.next + else + prev^.next := next; + Free(pos); + end + else + prev := pos; + pos := next; + end; + end; + +(** + * Call with the port number as the only argument. + * Never terminates (other than by signals, such as CTRL-C). + *) +var + d: PMHD_Daemon; + tv: timeval; + tvp: ptimeval; + rs: TFDSet; + ws: TFDSet; + es: TFDSet; + max: cint; + mhd_timeout: MHD_UNSIGNED_LONG_LONG; +begin + if argc <> 2 then + begin + WriteLn(argv[0], ' PORT'); + Halt(1); + end; + (* initialize PRNG *) + Randomize; + + d := MHD_start_daemon(MHD_USE_DEBUG, StrToInt(argv[1]), nil, nil, + @create_response, nil, MHD_OPTION_CONNECTION_TIMEOUT, cuint(15), + MHD_OPTION_NOTIFY_COMPLETED, @request_completed_callback, nil, MHD_OPTION_END); + if nil = d then + Halt(1); + + while True do + begin + expire_sessions; + max := 0; + fpFD_ZERO(rs); + fpFD_ZERO(ws); + fpFD_ZERO(es); + if MHD_YES <> MHD_get_fdset(d, @rs, @ws, @es, @max) then + Break; (* fatal internal error *) + if MHD_get_timeout(d, @mhd_timeout) = MHD_YES then + begin + tv.tv_sec := mhd_timeout div 1000; + tv.tv_usec := (mhd_timeout - (tv.tv_sec * 1000)) * 1000; + tvp := @tv; + end + else + tvp := nil; + if -1 = fpSelect(max + 1, @rs, @ws, @es, tvp) then + begin + if (ESysEINTR <> errno^) then + WriteLn(stderr, 'Aborting due to error during select: ', strerror(errno^)); + Break; + end; + MHD_run(d); + end; + MHD_stop_daemon(d); +end. diff --git a/packages/libmicrohttpd/examples/simplepost.pp b/packages/libmicrohttpd/examples/simplepost.pp new file mode 100644 index 0000000000..5693283e3e --- /dev/null +++ b/packages/libmicrohttpd/examples/simplepost.pp @@ -0,0 +1,155 @@ +(* Feel free to use this example code in any way + you see fit (Public Domain) *) + +// Original example: https://gnunet.org/svn/libmicrohttpd/doc/examples/simplepost.c + +program simplepost; + +{$mode objfpc}{$H+} + +uses + SysUtils, cmem, cutils, libmicrohttpd; + +const + PORT = 8888; + POSTBUFFERSIZE = 512; + MAXNAMESIZE = 20; + MAXANSWERSIZE = 512; + GET = 0; + POST = 1; + + askpage: Pcchar = + '<html><body>'+ + 'What''s your name, Sir?<br>'+ + '<form action="/namepost" method="post">'+ + '<input name="name" type="text">'+ + '<input type="submit" value=" Send "></form>'+ + '</body></html>'; + + greetingpage: Pcchar = '<html><body><h1>Welcome, %s!</center></h1></body></html>'; + + errorpage: Pcchar = '<html><body>This doesn''t seem to be right.</body></html>'; + +type + Tconnection_info_struct = packed record + connectiontype: cint; + answerstring: Pcchar; + postprocessor: PMHD_PostProcessor; + end; + Pconnection_info_struct = ^Tconnection_info_struct; + + function send_page(connection: PMHD_Connection; page: Pcchar): cint; cdecl; + var + ret: cint; + response: PMHD_Response; + begin + response := MHD_create_response_from_buffer(Length(page), + Pointer(page), MHD_RESPMEM_PERSISTENT); + if not Assigned(response) then + Exit(MHD_NO); + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + Result := ret; + end; + + function iterate_post(coninfo_cls: Pointer; kind: MHD_ValueKind; + key: Pcchar; filename: Pcchar; content_type: Pcchar; transfer_encoding: Pcchar; + Data: Pcchar; off: cuint64; size: size_t): cint; cdecl; + var + con_info: Pconnection_info_struct; + answerstring: Pcchar; + begin + con_info := coninfo_cls; + if 0 = strcomp(key, 'name') then + begin + if (size > 0) and (size <= MAXNAMESIZE) then + begin + answerstring := Malloc(MAXANSWERSIZE); + if not Assigned(answerstring) then + Exit(MHD_NO); + snprintf(answerstring, MAXANSWERSIZE, greetingpage, Data); + con_info^.answerstring := answerstring; + end + else + con_info^.answerstring := nil; + Exit(MHD_NO); + end; + Result := MHD_YES; + end; + + procedure request_completed(cls: Pointer; connection: PMHD_Connection; + con_cls: PPointer; toe: MHD_RequestTerminationCode); cdecl; + var + con_info: Pconnection_info_struct; + begin + con_info := con_cls^; + if nil = con_info then + Exit; + if con_info^.connectiontype = POST then + begin + MHD_destroy_post_processor(con_info^.postprocessor); + if Assigned(con_info^.answerstring) then + Free(con_info^.answerstring); + end; + Free(con_info); + con_cls^ := nil; + end; + + function answer_to_connection(cls: Pointer; connection: PMHD_Connection; + url: Pcchar; method: Pcchar; version: Pcchar; upload_data: Pcchar; + upload_data_size: Psize_t; con_cls: PPointer): cint; cdecl; + var + con_info: Pconnection_info_struct; + begin + if nil = con_cls^ then + begin + con_info := Malloc(SizeOf(Tconnection_info_struct)); + if nil = con_info then + Exit(MHD_NO); + con_info^.answerstring := nil; + if 0 = strcomp(method, 'POST') then + begin + con_info^.postprocessor := + MHD_create_post_processor(connection, POSTBUFFERSIZE, + @iterate_post, Pointer(con_info)); + if nil = con_info^.postprocessor then + begin + Free(con_info); + Exit(MHD_NO); + end; + con_info^.connectiontype := POST; + end + else + con_info^.connectiontype := GET; + con_cls^ := Pointer(con_info); + Exit(MHD_YES); + end; + if 0 = strcomp(method, 'GET') then + Exit(send_page(connection, askpage)); + if 0 = strcomp(method, 'POST') then + begin + con_info := con_cls^; + if upload_data_size^ <> 0 then + begin + MHD_post_process(con_info^.postprocessor, upload_data, upload_data_size^); + upload_data_size^ := 0; + Exit(MHD_YES); + end + else + if nil <> con_info^.answerstring then + Exit(send_page(connection, con_info^.answerstring)); + end; + Result := send_page(connection, errorpage); + end; + +var + daemon: PMHD_Daemon; +begin + daemon := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, nil, nil, + @answer_to_connection, nil, MHD_OPTION_NOTIFY_COMPLETED, @request_completed, + nil, MHD_OPTION_END); + if nil = daemon then + Halt(1); + ReadLn; + MHD_stop_daemon(daemon); +end. diff --git a/packages/libmicrohttpd/examples/tlsauthentication.pp b/packages/libmicrohttpd/examples/tlsauthentication.pp new file mode 100644 index 0000000000..8ecd0c133a --- /dev/null +++ b/packages/libmicrohttpd/examples/tlsauthentication.pp @@ -0,0 +1,234 @@ +(* Feel free to use this example code in any way + you see fit (Public Domain) *) + +// Original example: https://gnunet.org/svn/libmicrohttpd/doc/examples/tlsauthentication.c + +(* + * Generate PEM files for test this example: + * + * openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem + * + * or + * + * openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout server.key -out server.pem + *) + +program tlsauthentication; + +{$mode objfpc}{$H+} + +uses + SysUtils, ctypes, cmem, cutils, libmicrohttpd; + +const + PORT = 8888; + REALM = '"Maintenance"'; + USER = 'a legitimate user'; + PASSWORD = 'and his password'; + + SERVERKEYFILE = 'server.key'; + SERVERCERTFILE = 'server.pem'; + + function iif(c: cbool; t, f: culong): culong; + begin + if c then + Result := t + else + Result := f; + end; + + function string_to_base64(message: Pcchar): Pcchar; + var + lookup: Pcchar = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + l: culong; + i: cint; + tmp: Pcchar; + len: SizeInt; + begin + len := strlen(message); + tmp := Malloc(len * 2); + if nil = tmp then + Exit(tmp); + tmp[0] := #0; + i := 0; + while i < len do + begin + l := (culong(message[i]) shl 16) + or iif((i + 1) < len, culong(message[i + 1]) shl 8, 0) + or iif((i + 2) < len, culong(message[i + 2]), 0); + strncat(tmp, @lookup[(l shr 18) and $3F], 1); + strncat(tmp, @lookup[(l shr 12) and $3F], 1); + if i + 1 < len then + strncat(tmp, @lookup[(l shr 6) and $3F], 1); + if i + 2 < len then + strncat(tmp, @lookup[l and $3F], 1); + i += 3; + end; + if (len mod 3 = 1) then + strncat(tmp, '===', 3 - len mod 3); + Result := tmp; + end; + + function get_file_size(filename: Pcchar): clong; + var + fp: FILEptr; + size: clong; + begin + fp := fopen(filename, fopenread); + if Assigned(fp) then + begin + if 0 <> fseek(fp, 0, SEEK_END) then + size := 0; + size := ftell(fp); + if -1 = size then + size := 0; + fclose(fp); + Result := size; + end + else + Result := 0; + end; + + function load_file(filename: Pcchar): Pcchar; + var + fp: FILEptr; + buffer: Pcchar; + size: clong; + begin + size := get_file_size(filename); + if size = 0 then + Exit(nil); + fp := fopen(filename, fopenread); + if not Assigned(fp) then + Exit(nil); + buffer := Malloc(size); + if not Assigned(buffer) then + begin + fclose(fp); + Exit(nil); + end; + if size <> fread(buffer, 1, size, fp) then + begin + free(buffer); + buffer := nil; + end; + fclose(fp); + Result := buffer; + end; + + function ask_for_authentication(connection: PMHD_Connection; + realm: Pcchar): cint; cdecl; + var + ret: cint; + response: PMHD_Response; + headervalue: Pcchar; + strbase: Pcchar = 'Basic realm='; + begin + response := MHD_create_response_from_buffer(0, nil, MHD_RESPMEM_PERSISTENT); + if not Assigned(response) then + Exit(MHD_NO); + headervalue := Malloc(strlen(strbase) + strlen(realm) + 1); + if not Assigned(headervalue) then + Exit(MHD_NO); + strcpy(headervalue, strbase); + strcat(headervalue, realm); + ret := MHD_add_response_header(response, 'WWW-Authenticate', headervalue); + Free(headervalue); + if ret <> 1 then + begin + MHD_destroy_response(response); + Exit(MHD_NO); + end; + ret := MHD_queue_response(connection, MHD_HTTP_UNAUTHORIZED, response); + MHD_destroy_response(response); + Result := ret; + end; + + function is_authenticated(connection: PMHD_Connection; + username, password: Pcchar): cint; cdecl; + var + headervalue: Pcchar; + expected_b64, expected: Pcchar; + strbase: Pcchar = 'Basic '; + authenticated: cint; + begin + headervalue := MHD_lookup_connection_value(connection, MHD_HEADER_KIND, + 'Authorization'); + if nil = headervalue then + Exit(0); + if 0 <> strncmp(headervalue, strbase, strlen(strbase)) then + Exit(0); + expected := malloc(strlen(username) + 1 + strlen(password) + 1); + if nil = expected then + Exit(0); + strcpy(expected, username); + strcat(expected, ':'); + strcat(expected, password); + expected_b64 := string_to_base64(expected); + free(expected); + if nil = expected_b64 then + Exit(0); + authenticated := cint(strcomp(headervalue + strlen(strbase), expected_b64) = 0); + Free(expected_b64); + Result := authenticated; + end; + + function secret_page(connection: PMHD_Connection): cint; cdecl; + var + ret: cint; + response: PMHD_Response; + page: Pcchar = '<html><body>A secret.</body></html>'; + begin + response := MHD_create_response_from_buffer(strlen(page), Pointer(page), + MHD_RESPMEM_PERSISTENT); + if not Assigned(response) then + Exit(MHD_NO); + ret := MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + Result := ret; + end; + + function answer_to_connection(cls: Pointer; connection: PMHD_Connection; + url: Pcchar; method: Pcchar; version: Pcchar; upload_data: Pcchar; + upload_data_size: Psize_t; con_cls: PPointer): cint; cdecl; + begin + if 0 <> strcomp(method, 'GET') then + Exit(MHD_NO); + if nil = con_cls^ then + begin + con_cls^ := connection; + Exit(MHD_YES); + end; + if is_authenticated(connection, USER, PASSWORD) <> 1 then + Exit(ask_for_authentication(connection, REALM)); + Result := secret_page(connection); + end; + +var + daemon: PMHD_Daemon; + key_pem: Pcchar; + cert_pem: Pcchar; +begin + key_pem := load_file(SERVERKEYFILE); + cert_pem := load_file(SERVERCERTFILE); + if (key_pem = nil) or (cert_pem = nil) then + begin + WriteLn('The key/certificate files could not be read.'); + Halt(1); + end; + daemon := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY or MHD_USE_SSL, PORT, + nil, nil, @answer_to_connection, nil, MHD_OPTION_HTTPS_MEM_KEY, key_pem, + MHD_OPTION_HTTPS_MEM_CERT, cert_pem, MHD_OPTION_END); + if nil = daemon then + begin + WriteLn(cert_pem); + Free(key_pem); + Free(cert_pem); + Halt(1); + end; + ReadLn; + MHD_stop_daemon(daemon); + Free(key_pem); + Free(cert_pem); +end. + diff --git a/packages/libmicrohttpd/fpmake.pp b/packages/libmicrohttpd/fpmake.pp new file mode 100644 index 0000000000..8d13e0bd9e --- /dev/null +++ b/packages/libmicrohttpd/fpmake.pp @@ -0,0 +1,59 @@ +{$ifndef ALLPACKAGES} +{$mode objfpc}{$H+} +program fpmake; + +uses fpmkunit; + +Var + P : TPackage; + T : TTarget; +begin + With Installer do + begin +{$endif ALLPACKAGES} + + P:=AddPackage('libmicrohttpd'); +{$ifdef ALLPACKAGES} + P.Directory:=ADirectory; +{$endif ALLPACKAGES} + P.Version:='3.1.1'; + P.Author := 'Library: GNU foundation, header: Silvio Clecio)'; + P.License := 'Library: LGPL or later, header: LGPL with modification, '; + P.HomepageURL := 'www.gnu.org'; + P.Email := ''; + P.Description := 'Event based micro-http server library interface'; + P.NeedLibC:= true; + P.OSes := [win32,win64,linux]; + P.SourcePath.Add('src'); + P.IncludePath.Add('src'); + T:=P.Targets.AddUnit('libmicrohttpd.pp'); + P.ExamplePath.Add('examples'); + T:=P.Targets.AddExampleProgram('basicauthentication.pp'); + T:=P.Targets.AddExampleProgram('benchmark_https.pp'); + T:=P.Targets.AddExampleProgram('benchmark.pp'); + T:=P.Targets.AddExampleProgram('chunked_example.pp'); + T:=P.Targets.AddExampleProgram('demo_https.pp'); + T:=P.Targets.AddExampleProgram('demo.pp'); + T:=P.Targets.AddExampleProgram('digest_auth_example.pp'); + T:=P.Targets.AddExampleProgram('dual_stack_example.pp'); + T:=P.Targets.AddExampleProgram('fileserver_example_dirs.pp'); + T:=P.Targets.AddExampleProgram('fileserver_example_external_select.pp'); + T:=P.Targets.AddExampleProgram('fileserver_example.pp'); + T:=P.Targets.AddExampleProgram('hellobrowser.pp'); + T:=P.Targets.AddExampleProgram('https_fileserver_example.pp'); + T:=P.Targets.AddExampleProgram('largepost.pp'); + T:=P.Targets.AddExampleProgram('logging.pp'); + T:=P.Targets.AddExampleProgram('minimal_example_comet.pp'); + T:=P.Targets.AddExampleProgram('minimal_example.pp'); + T:=P.Targets.AddExampleProgram('post_example.pp'); + T:=P.Targets.AddExampleProgram('querystring_example.pp'); + T:=P.Targets.AddExampleProgram('refuse_post_example.pp'); + T:=P.Targets.AddExampleProgram('responseheaders.pp'); + T:=P.Targets.AddExampleProgram('sessions.pp'); + T:=P.Targets.AddExampleProgram('simplepost.pp'); + T:=P.Targets.AddExampleProgram('tlsauthentication.pp'); +{$ifndef ALLPACKAGES} + Run; + end; +end. +{$endif ALLPACKAGES} diff --git a/packages/libmicrohttpd/src/libmicrohttpd.pp b/packages/libmicrohttpd/src/libmicrohttpd.pp new file mode 100644 index 0000000000..eb9e95de34 --- /dev/null +++ b/packages/libmicrohttpd/src/libmicrohttpd.pp @@ -0,0 +1,485 @@ +{ + This file is part of the Free Pascal packages + Copyright (c) 2015 by Silvio Clecio, Gilson Nunes and Joao Morais + + See the file COPYING.FPC, included in this distribution, + for details about the copyright. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + ********************************************************************** + + The GNU libmicrohttpd library is governed by GNU copyright, see the + GNU libmicrohttpd website for this: + + https://www.gnu.org/software/libmicrohttpd/ +} + +unit libmicrohttpd; + +interface + +uses +{$IFDEF MSWINDOWS} + WinSock2, +{$ENDIF} +{$IFDEF FPC} + {$IFDEF UNIX}BaseUnix, Unix,{$ENDIF}CTypes, Sockets +{$ELSE} + WinTypes +{$ENDIF}; + +const + MHD_LIB_NAME = {$IFDEF MSWINDOWS}'libmicrohttpd-10'{$ELSE}'microhttpd'{$ENDIF}; + +{$IFDEF FPC} + {$PACKRECORDS C} +{$ENDIF} + +{$IFDEF UNIX} + FD_SETSIZE = 64; +{$ENDIF} +{$IFDEF UINT64_MAX} + MHD_SIZE_UNKNOWN = UINT64_MAX; +{$ELSE} + MHD_SIZE_UNKNOWN = -1; +{$ENDIF} +{$IFDEF SIZE_MAX} + MHD_CONTENT_READER_END_OF_STREAM = SIZE_MAX; + MHD_CONTENT_READER_END_WITH_ERROR = SIZE_MAX - 1; +{$ELSE} + MHD_CONTENT_READER_END_OF_STREAM = -1; + MHD_CONTENT_READER_END_WITH_ERROR = -2; +{$ENDIF} +{$IFNDEF MHD_SOCKET_DEFINED} + {$IF NOT DEFINED(WIN32) OR DEFINED(_SYS_TYPES_FD_SET)} + MHD_POSIX_SOCKETS = 1; + MHD_INVALID_SOCKET = -1; + {$ELSE} + MHD_WINSOCK_SOCKETS = 1; + MHD_INVALID_SOCKET = INVALID_SOCKET; + {$ENDIF} + MHD_SOCKET_DEFINED = 1; +{$ENDIF} + MHD_VERSION = $00094300; + MHD_YES = 1; + MHD_NO = 0; + MHD_INVALID_NONCE = -1; + MHD_ICY_FLAG = -2147483648; + MHD_UNSIGNED_LONG_LONG_PRINTF = '%llu'; + + // HTTP codes + MHD_HTTP_CONTINUE = 100; + MHD_HTTP_SWITCHING_PROTOCOLS = 101; + MHD_HTTP_PROCESSING = 102; + MHD_HTTP_OK = 200; + MHD_HTTP_CREATED = 201; + MHD_HTTP_ACCEPTED = 202; + MHD_HTTP_NON_AUTHORITATIVE_INFORMATION = 203; + MHD_HTTP_NO_CONTENT = 204; + MHD_HTTP_RESET_CONTENT = 205; + MHD_HTTP_PARTIAL_CONTENT = 206; + MHD_HTTP_MULTI_STATUS = 207; + MHD_HTTP_MULTIPLE_CHOICES = 300; + MHD_HTTP_MOVED_PERMANENTLY = 301; + MHD_HTTP_FOUND = 302; + MHD_HTTP_SEE_OTHER = 303; + MHD_HTTP_NOT_MODIFIED = 304; + MHD_HTTP_USE_PROXY = 305; + MHD_HTTP_SWITCH_PROXY = 306; + MHD_HTTP_TEMPORARY_REDIRECT = 307; + MHD_HTTP_BAD_REQUEST = 400; + MHD_HTTP_UNAUTHORIZED = 401; + MHD_HTTP_PAYMENT_REQUIRED = 402; + MHD_HTTP_FORBIDDEN = 403; + MHD_HTTP_NOT_FOUND = 404; + MHD_HTTP_METHOD_NOT_ALLOWED = 405; + MHD_HTTP_NOT_ACCEPTABLE = 406; + MHD_HTTP_PROXY_AUTHENTICATION_REQUIRED = 407; + MHD_HTTP_REQUEST_TIMEOUT = 408; + MHD_HTTP_CONFLICT = 409; + MHD_HTTP_GONE = 410; + MHD_HTTP_LENGTH_REQUIRED = 411; + MHD_HTTP_PRECONDITION_FAILED = 412; + MHD_HTTP_REQUEST_ENTITY_TOO_LARGE = 413; + MHD_HTTP_REQUEST_URI_TOO_LONG = 414; + MHD_HTTP_UNSUPPORTED_MEDIA_TYPE = 415; + MHD_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416; + MHD_HTTP_EXPECTATION_FAILED = 417; + MHD_HTTP_UNPROCESSABLE_ENTITY = 422; + MHD_HTTP_LOCKED = 423; + MHD_HTTP_FAILED_DEPENDENCY = 424; + MHD_HTTP_UNORDERED_COLLECTION = 425; + MHD_HTTP_UPGRADE_REQUIRED = 426; + MHD_HTTP_NO_RESPONSE = 444; + MHD_HTTP_RETRY_WITH = 449; + MHD_HTTP_BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS = 450; + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451; + MHD_HTTP_INTERNAL_SERVER_ERROR = 500; + MHD_HTTP_NOT_IMPLEMENTED = 501; + MHD_HTTP_BAD_GATEWAY = 502; + MHD_HTTP_SERVICE_UNAVAILABLE = 503; + MHD_HTTP_GATEWAY_TIMEOUT = 504; + MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED = 505; + MHD_HTTP_VARIANT_ALSO_NEGOTIATES = 506; + MHD_HTTP_INSUFFICIENT_STORAGE = 507; + MHD_HTTP_BANDWIDTH_LIMIT_EXCEEDED = 509; + MHD_HTTP_NOT_EXTENDED = 510; + + // HTTP headers + MHD_HTTP_HEADER_ACCEPT = 'Accept'; + MHD_HTTP_HEADER_ACCEPT_CHARSET = 'Accept-Charset'; + MHD_HTTP_HEADER_ACCEPT_ENCODING = 'Accept-Encoding'; + MHD_HTTP_HEADER_ACCEPT_LANGUAGE = 'Accept-Language'; + MHD_HTTP_HEADER_ACCEPT_RANGES = 'Accept-Ranges'; + MHD_HTTP_HEADER_AGE = 'Age'; + MHD_HTTP_HEADER_ALLOW = 'Allow'; + MHD_HTTP_HEADER_AUTHORIZATION = 'Authorization'; + MHD_HTTP_HEADER_CACHE_CONTROL = 'Cache-Control'; + MHD_HTTP_HEADER_CONNECTION = 'Connection'; + MHD_HTTP_HEADER_CONTENT_ENCODING = 'Content-Encoding'; + MHD_HTTP_HEADER_CONTENT_LANGUAGE = 'Content-Language'; + MHD_HTTP_HEADER_CONTENT_LENGTH = 'Content-Length'; + MHD_HTTP_HEADER_CONTENT_LOCATION = 'Content-Location'; + MHD_HTTP_HEADER_CONTENT_MD5 = 'Content-MD5'; + MHD_HTTP_HEADER_CONTENT_RANGE = 'Content-Range'; + MHD_HTTP_HEADER_CONTENT_TYPE = 'Content-Type'; + MHD_HTTP_HEADER_COOKIE = 'Cookie'; + MHD_HTTP_HEADER_DATE = 'Date'; + MHD_HTTP_HEADER_ETAG = 'ETag'; + MHD_HTTP_HEADER_EXPECT = 'Expect'; + MHD_HTTP_HEADER_EXPIRES = 'Expires'; + MHD_HTTP_HEADER_FROM = 'From'; + MHD_HTTP_HEADER_HOST = 'Host'; + MHD_HTTP_HEADER_IF_MATCH = 'If-Match'; + MHD_HTTP_HEADER_IF_MODIFIED_SINCE = 'If-Modified-Since'; + MHD_HTTP_HEADER_IF_NONE_MATCH = 'If-None-Match'; + MHD_HTTP_HEADER_IF_RANGE = 'If-Range'; + MHD_HTTP_HEADER_IF_UNMODIFIED_SINCE = 'If-Unmodified-Since'; + MHD_HTTP_HEADER_LAST_MODIFIED = 'Last-Modified'; + MHD_HTTP_HEADER_LOCATION = 'Location'; + MHD_HTTP_HEADER_MAX_FORWARDS = 'Max-Forwards'; + MHD_HTTP_HEADER_PRAGMA = 'Pragma'; + MHD_HTTP_HEADER_PROXY_AUTHENTICATE = 'Proxy-Authenticate'; + MHD_HTTP_HEADER_PROXY_AUTHORIZATION = 'Proxy-Authorization'; + MHD_HTTP_HEADER_RANGE = 'Range'; + MHD_HTTP_HEADER_REFERER = 'Referer'; + MHD_HTTP_HEADER_RETRY_AFTER = 'Retry-After'; + MHD_HTTP_HEADER_SERVER = 'Server'; + MHD_HTTP_HEADER_SET_COOKIE = 'Set-Cookie'; + MHD_HTTP_HEADER_SET_COOKIE2 = 'Set-Cookie2'; + MHD_HTTP_HEADER_TE = 'TE'; + MHD_HTTP_HEADER_TRAILER = 'Trailer'; + MHD_HTTP_HEADER_TRANSFER_ENCODING = 'Transfer-Encoding'; + MHD_HTTP_HEADER_UPGRADE = 'Upgrade'; + MHD_HTTP_HEADER_USER_AGENT = 'User-Agent'; + MHD_HTTP_HEADER_VARY = 'Vary'; + MHD_HTTP_HEADER_VIA = 'Via'; + MHD_HTTP_HEADER_WARNING = 'Warning'; + MHD_HTTP_HEADER_WWW_AUTHENTICATE = 'WWW-Authenticate'; + MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN = 'Access-Control-Allow-Origin'; + MHD_HTTP_HEADER_CONTENT_DISPOSITION = 'Content-Disposition'; + MHD_HTTP_VERSION_1_0 = 'HTTP/1.0'; + MHD_HTTP_VERSION_1_1 = 'HTTP/1.1'; + MHD_HTTP_METHOD_CONNECT = 'CONNECT'; + MHD_HTTP_METHOD_DELETE = 'DELETE'; + MHD_HTTP_METHOD_GET = 'GET'; + MHD_HTTP_METHOD_HEAD = 'HEAD'; + MHD_HTTP_METHOD_OPTIONS = 'OPTIONS'; + MHD_HTTP_METHOD_POST = 'POST'; + MHD_HTTP_METHOD_PUT = 'PUT'; + MHD_HTTP_METHOD_PATCH = 'PATCH'; + MHD_HTTP_METHOD_TRACE = 'TRACE'; + MHD_HTTP_POST_ENCODING_FORM_URLENCODED = 'application/x-www-form-urlencoded'; + MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA = 'multipart/form-data'; + + // MHD_ValueKind enum items + MHD_RESPONSE_HEADER_KIND = 0; + MHD_HEADER_KIND = 1; + MHD_COOKIE_KIND = 2; + MHD_POSTDATA_KIND = 4; + MHD_GET_ARGUMENT_KIND = 8; + MHD_FOOTER_KIND = 16; + + // MHD_FLAG enum items + MHD_NO_FLAG = 0; + MHD_USE_DEBUG = 1; + MHD_USE_SSL = 2; + MHD_USE_THREAD_PER_CONNECTION = 4; + MHD_USE_SELECT_INTERNALLY = 8; + MHD_USE_IPv6 = 16; + MHD_USE_PEDANTIC_CHECKS = 32; + MHD_USE_POLL = 64; + MHD_USE_POLL_INTERNALLY = MHD_USE_SELECT_INTERNALLY or MHD_USE_POLL; + MHD_SUPPRESS_DATE_NO_CLOCK = 128; + MHD_USE_NO_LISTEN_SOCKET = 256; + MHD_USE_EPOLL_LINUX_ONLY = 512; + MHD_USE_EPOLL_INTERNALLY_LINUX_ONLY = MHD_USE_SELECT_INTERNALLY or MHD_USE_EPOLL_LINUX_ONLY; + MHD_USE_PIPE_FOR_SHUTDOWN = 1024; + MHD_USE_DUAL_STACK = MHD_USE_IPv6 or 2048; + MHD_USE_EPOLL_TURBO = 4096; + MHD_USE_SUSPEND_RESUME = 8192 or MHD_USE_PIPE_FOR_SHUTDOWN; + MHD_USE_TCP_FASTOPEN = 16384; + + // MHD_OPTION enum items + MHD_OPTION_END = 0; + MHD_OPTION_CONNECTION_MEMORY_LIMIT = 1; + MHD_OPTION_CONNECTION_LIMIT = 2; + MHD_OPTION_CONNECTION_TIMEOUT = 3; + MHD_OPTION_NOTIFY_COMPLETED = 4; + MHD_OPTION_PER_IP_CONNECTION_LIMIT = 5; + MHD_OPTION_SOCK_ADDR = 6; + MHD_OPTION_URI_LOG_CALLBACK = 7; + MHD_OPTION_HTTPS_MEM_KEY = 8; + MHD_OPTION_HTTPS_MEM_CERT = 9; + MHD_OPTION_HTTPS_CRED_TYPE = 10; + MHD_OPTION_HTTPS_PRIORITIES = 11; + MHD_OPTION_LISTEN_SOCKET = 12; + MHD_OPTION_EXTERNAL_LOGGER = 13; + MHD_OPTION_THREAD_POOL_SIZE = 14; + MHD_OPTION_ARRAY = 15; + MHD_OPTION_UNESCAPE_CALLBACK = 16; + MHD_OPTION_DIGEST_AUTH_RANDOM = 17; + MHD_OPTION_NONCE_NC_SIZE = 18; + MHD_OPTION_THREAD_STACK_SIZE = 19; + MHD_OPTION_HTTPS_MEM_TRUST = 20; + MHD_OPTION_CONNECTION_MEMORY_INCREMENT = 21; + MHD_OPTION_HTTPS_CERT_CALLBACK = 22; + MHD_OPTION_TCP_FASTOPEN_QUEUE_SIZE = 23; + MHD_OPTION_HTTPS_MEM_DHPARAMS = 24; + MHD_OPTION_LISTENING_ADDRESS_REUSE = 25; + MHD_OPTION_HTTPS_KEY_PASSWORD = 26; + MHD_OPTION_NOTIFY_CONNECTION = 27; + + // MHD_RequestTerminationCode enum items + MHD_REQUEST_TERMINATED_COMPLETED_OK = 0; + MHD_REQUEST_TERMINATED_WITH_ERROR = 1; + MHD_REQUEST_TERMINATED_TIMEOUT_REACHED = 2; + MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN = 3; + MHD_REQUEST_TERMINATED_READ_ERROR = 4; + MHD_REQUEST_TERMINATED_CLIENT_ABORT = 5; + + // MHD_ConnectionNotificationCode enum items + MHD_CONNECTION_NOTIFY_STARTED = 0; + MHD_CONNECTION_NOTIFY_CLOSED = 1; + + // MHD_ConnectionInfoType enum items + MHD_CONNECTION_INFO_CIPHER_ALGO = 0; + MHD_CONNECTION_INFO_PROTOCOL = 1; + MHD_CONNECTION_INFO_CLIENT_ADDRESS = 2; + MHD_CONNECTION_INFO_GNUTLS_SESSION = 3; + MHD_CONNECTION_INFO_GNUTLS_CLIENT_CERT = 4; + MHD_CONNECTION_INFO_DAEMON = 5; + MHD_CONNECTION_INFO_CONNECTION_FD = 6; + MHD_CONNECTION_INFO_SOCKET_CONTEXT = 7; + + // MHD_DaemonInfoType enum items + MHD_DAEMON_INFO_KEY_SIZE = 0; + MHD_DAEMON_INFO_MAC_KEY_SIZE = 1; + MHD_DAEMON_INFO_LISTEN_FD = 2; + MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY = 3; + MHD_DAEMON_INFO_CURRENT_CONNECTIONS = 4; + + // MHD_ResponseFlags enum items + MHD_RF_NONE = 0; + MHD_RF_HTTP_VERSION_1_0_ONLY = 1; + + // MHD_ResponseOptions enum items + MHD_RO_END = 0; + + // MHD_ResponseMemoryMode enum items + MHD_RESPMEM_PERSISTENT = 0; + MHD_RESPMEM_MUST_FREE = 1; + MHD_RESPMEM_MUST_COPY = 2; + + // MHD_CONNECTION_OPTION enum items + MHD_CONNECTION_OPTION_TIMEOUT = 0; + + // MHD_FEATURE enum items + MHD_FEATURE_MESSGES = 1; + MHD_FEATURE_SSL = 2; + MHD_FEATURE_HTTPS_CERT_CALLBACK = 3; + MHD_FEATURE_IPv6 = 4; + MHD_FEATURE_IPv6_ONLY = 5; + MHD_FEATURE_POLL = 6; + MHD_FEATURE_EPOLL = 7; + MHD_FEATURE_SHUTDOWN_LISTEN_SOCKET = 8; + MHD_FEATURE_SOCKETPAIR = 9; + MHD_FEATURE_TCP_FASTOPEN = 10; + MHD_FEATURE_BASIC_AUTH = 11; + MHD_FEATURE_DIGEST_AUTH = 12; + MHD_FEATURE_POSTPROCESSOR = 13; + MHD_FEATURE_HTTPS_KEY_PASSWORD = 14; + MHD_FEATURE_LARGE_FILE = 15; + +type + Pcchar = PAnsiChar; + Ppcchar = ^Pcchar; + va_list = Pointer; +{$IFDEF FPC} + cint = CTypes.cint; + cuint = CTypes.cuint; + cuint16 = CTypes.cuint16; + cuint64 = CTypes.cuint64; + culonglong = CTypes.culonglong; + socklen_t = {$IFDEF UNIX}BaseUnix.socklen_t{$ELSE}LongInt{$ENDIF}; + size_t = {$IFDEF UNIX}BaseUnix{$ELSE}Sockets{$ENDIF}.size_t; + Psize_t = {$IFDEF UNIX}BaseUnix.pSize_t{$ELSE}^Sockets.size_t{$ENDIF}; + Pfd_set = {$IFDEF UNIX}BaseUnix.pFDSet{$ELSE}WinSock2.PFDSet{$ENDIF}; + ssize_t = {$IFDEF UNIX}BaseUnix{$ELSE}Sockets{$ENDIF}.ssize_t; + psockaddr = Sockets.psockaddr; +{$ELSE} + cint = LongInt; + cuint = LongWord; + cuint16 = Word; + cuint64 = UInt64; + culonglong = UInt64; + socklen_t = LongInt; + size_t = WinTypes.SIZE_T; + Psize_t = WinTypes.PSIZE_T; + Pfd_set = WinSock2.PFdSet; + ssize_t = WinTypes.SSIZE_T; + psockaddr = WinSock2.PSockAddr; +{$ENDIF} +{$IFNDEF MHD_SOCKET_DEFINED} + PMHD_socket = ^MHD_socket; + {$IF NOT DEFINED(WIN32) OR DEFINED(_SYS_TYPES_FD_SET)} + MHD_socket = cint; + {$ELSE} + MHD_socket = TSocket; + {$ENDIF} +{$ENDIF} + MHD_UNSIGNED_LONG_LONG = culonglong; + PMHD_UNSIGNED_LONG_LONG = ^MHD_UNSIGNED_LONG_LONG; + + // enums + MHD_ValueKind = LongInt; + + MHD_FLAG = LongInt; + + MHD_OPTION = LongInt; + + MHD_RequestTerminationCode = LongInt; + + MHD_ConnectionNotificationCode = LongInt; + + MHD_ConnectionInfoType = LongInt; + + MHD_DaemonInfoType = LongInt; + + MHD_ResponseFlags = LongInt; + + MHD_ResponseOptions = LongInt; + + MHD_ResponseMemoryMode = LongInt; + + MHD_CONNECTION_OPTION = LongInt; + + MHD_FEATURE = LongInt; + + MHD_Daemon = record + end; + PMHD_Daemon = ^MHD_Daemon; + + MHD_Connection = record + end; + PMHD_Connection = ^MHD_Connection; + + MHD_Response = record + end; + PMHD_Response = ^MHD_Response; + + MHD_PostProcessor = record + end; + PMHD_PostProcessor = ^MHD_PostProcessor; + + MHD_OptionItem = record + option: MHD_OPTION; + value: ssize_t; + ptr_value: Pointer; + end; + PMHD_OptionItem = ^MHD_OptionItem; + + MHD_ConnectionInfo = record + case LongInt of + 0: (cipher_algorithm: cint); + 1: (protocol: cint); + 2: (connect_fd: MHD_socket); + 3: (tls_session: Pointer); + 4: (client_cert: Pointer); + 5: (client_addr: psockaddr); + 6: (daemon: PMHD_Daemon); + 7: (socket_context: ^Pointer); + end; + PMHD_ConnectionInfo = ^MHD_ConnectionInfo; + + MHD_DaemonInfo = record + case LongInt of + 0: (key_size: size_t); + 1: (mac_key_size: size_t); + 2: (listen_fd: MHD_socket); + 3: (num_connections: cuint); + end; + PMHD_DaemonInfo = ^MHD_DaemonInfo; + + MHD_LogCallback = procedure(cls: Pointer; fm: Pcchar; ap: va_list); cdecl; + MHD_PanicCallback = procedure(cls: Pointer; &file: Pcchar; line: cuint; reason: Pcchar); cdecl; + MHD_AcceptPolicyCallback = function(cls: Pointer; addr: psockaddr; addrlen: socklen_t): cint; cdecl; + MHD_AccessHandlerCallback = function(cls: Pointer; connection: PMHD_Connection; url: Pcchar; method: Pcchar; version: Pcchar; upload_data: Pcchar; upload_data_size: pSize_t; con_cls: PPointer): cint; cdecl; + MHD_RequestCompletedCallback = procedure(cls: Pointer; connection: PMHD_Connection; con_cls: PPointer; toe: MHD_RequestTerminationCode); cdecl; + MHD_NotifyConnectionCallback = procedure(cls: Pointer; connection: PMHD_Connection; socket_context: PPointer; toe: MHD_ConnectionNotificationCode); cdecl; + MHD_KeyValueIterator = function(cls: Pointer; kind: MHD_ValueKind; key: Pcchar; value: Pcchar): cint; cdecl; + MHD_ContentReaderCallback = function(cls: pointer; pos: cuint64; buf: Pcchar; max: size_t): ssize_t; cdecl; + MHD_ContentReaderFreeCallback = procedure(cls: Pointer); cdecl; + MHD_PostDataIterator = function(cls: Pointer; kind: MHD_ValueKind; key: Pcchar; filename: Pcchar; content_type: Pcchar; transfer_encoding: Pcchar; data: Pcchar; off: cuint64; size: size_t): cint; cdecl; + + function MHD_get_reason_phrase_for(code: cuint): Pcchar; cdecl; external MHD_LIB_NAME name 'MHD_get_reason_phrase_for'; + function MHD_start_daemon_va(flags: cuint; port: cuint16; apc: MHD_AcceptPolicyCallback; apc_cls: Pointer; dh: MHD_AccessHandlerCallback; dh_cls: Pointer; ap: va_list): PMHD_Daemon; cdecl; external MHD_LIB_NAME name 'MHD_start_daemon_va'; + function MHD_start_daemon(flags: cuint; port: cuint16; apc: MHD_AcceptPolicyCallback; apc_cls: Pointer; dh: MHD_AccessHandlerCallback; dh_cls: Pointer): PMHD_Daemon; cdecl; varargs; external MHD_LIB_NAME name 'MHD_start_daemon'; + function MHD_quiesce_daemon(daemon: PMHD_Daemon): MHD_socket; cdecl; external MHD_LIB_NAME name 'MHD_quiesce_daemon'; + procedure MHD_stop_daemon(daemon: PMHD_Daemon); cdecl; external MHD_LIB_NAME name 'MHD_stop_daemon'; + function MHD_add_connection(daemon: PMHD_Daemon; client_socket: MHD_socket; addr: psockaddr; addrlen: socklen_t): cint; cdecl; external MHD_LIB_NAME name 'MHD_add_connection'; + function MHD_get_fdset(daemon: PMHD_Daemon; read_fd_set: Pfd_set; write_fd_set: Pfd_set; except_fd_set: Pfd_set; max_fd: PMHD_socket): cint; cdecl; external MHD_LIB_NAME name 'MHD_get_fdset'; + function MHD_get_fdset2(daemon: PMHD_Daemon; read_fd_set: Pfd_set; write_fd_set: Pfd_set; except_fd_set: Pfd_set; max_fd: PMHD_socket; fd_setsize: cuint): cint; cdecl; external MHD_LIB_NAME name 'MHD_get_fdset2'; + function MHD_get_timeout(daemon: PMHD_Daemon; timeout: PMHD_UNSIGNED_LONG_LONG): cint; cdecl; external MHD_LIB_NAME name 'MHD_get_timeout'; + function MHD_run(daemon: PMHD_Daemon): cint; cdecl; external MHD_LIB_NAME name 'MHD_run'; + function MHD_run_from_select(daemon: PMHD_Daemon; read_fd_set: Pfd_set; write_fd_set: Pfd_set; except_fd_set: Pfd_set): cint; cdecl; external MHD_LIB_NAME name 'MHD_run_from_select'; + function MHD_get_connection_values(connection: PMHD_Connection; kind: MHD_ValueKind; iterator: MHD_KeyValueIterator; iterator_cls: pointer): cint; cdecl; external MHD_LIB_NAME name 'MHD_get_connection_values'; + function MHD_set_connection_value(connection: PMHD_Connection; kind: MHD_ValueKind; key: Pcchar; value: Pcchar): cint; cdecl; external MHD_LIB_NAME name 'MHD_set_connection_value'; + procedure MHD_set_panic_func(cb: MHD_PanicCallback; cls: Pointer); cdecl; external MHD_LIB_NAME name 'MHD_set_panic_func'; + function MHD_http_unescape(val: Pcchar): size_t; cdecl; external MHD_LIB_NAME name 'MHD_http_unescape'; + function MHD_lookup_connection_value(connection: PMHD_Connection; kind: MHD_ValueKind; key: Pcchar): Pcchar; cdecl; external MHD_LIB_NAME name 'MHD_lookup_connection_value'; + function MHD_queue_response(connection: PMHD_Connection; status_code: cuint; response: PMHD_Response): cint; cdecl; external MHD_LIB_NAME name 'MHD_queue_response'; + procedure MHD_suspend_connection(connection: PMHD_Connection); cdecl; external MHD_LIB_NAME name 'MHD_suspend_connection'; + procedure MHD_resume_connection(connection: PMHD_Connection); cdecl; external MHD_LIB_NAME name 'MHD_resume_connection'; + function MHD_set_response_options(response: PMHD_Response; flags: MHD_ResponseFlags): cint; cdecl; varargs; external MHD_LIB_NAME name 'MHD_set_response_options'; + function MHD_create_response_from_callback(size: cuint64; block_size: size_t; crc: MHD_ContentReaderCallback; crc_cls: pointer; crfc: MHD_ContentReaderFreeCallback): PMHD_Response; cdecl; external MHD_LIB_NAME name 'MHD_create_response_from_callback'; + function MHD_create_response_from_buffer(size: size_t; buffer: Pointer; mode: MHD_ResponseMemoryMode): PMHD_Response; cdecl; external MHD_LIB_NAME name 'MHD_create_response_from_buffer'; + function MHD_create_response_from_fd(size: size_t; fd: cint): PMHD_Response; cdecl; external MHD_LIB_NAME name 'MHD_create_response_from_fd'; + function MHD_create_response_from_fd64(size: cuint64; fd: cint): PMHD_Response; cdecl; external MHD_LIB_NAME name 'MHD_create_response_from_fd64'; + function MHD_create_response_from_fd_at_offset64(size: cuint64; fd: cint; offset: cuint64): PMHD_Response;cdecl;external MHD_LIB_NAME name 'MHD_create_response_from_fd_at_offset64'; + procedure MHD_destroy_response(response: PMHD_Response); cdecl; external MHD_LIB_NAME name 'MHD_destroy_response'; + function MHD_add_response_header(response: PMHD_Response; header: Pcchar; content: Pcchar): cint; cdecl; external MHD_LIB_NAME name 'MHD_add_response_header'; + function MHD_add_response_footer(response: PMHD_Response; footer: Pcchar; content: Pcchar): cint; cdecl; external MHD_LIB_NAME name 'MHD_add_response_footer'; + function MHD_del_response_header(response: PMHD_Response; header: Pcchar; content: Pcchar): cint; cdecl; external MHD_LIB_NAME name 'MHD_del_response_header'; + function MHD_get_response_headers(response: PMHD_Response; iterator: MHD_KeyValueIterator; iterator_cls: Pointer): cint; cdecl; external MHD_LIB_NAME name 'MHD_get_response_headers'; + function MHD_get_response_header(response: PMHD_Response; key: Pcchar): Pcchar; cdecl; external MHD_LIB_NAME name 'MHD_get_response_header'; + function MHD_create_post_processor(connection: PMHD_Connection; buffer_size: size_t; iter: MHD_PostDataIterator; iter_cls: pointer): PMHD_PostProcessor; cdecl; external MHD_LIB_NAME name 'MHD_create_post_processor'; + function MHD_post_process(pp: PMHD_PostProcessor; post_data: Pcchar; post_data_len: size_t): cint; cdecl; external MHD_LIB_NAME name 'MHD_post_process'; + function MHD_destroy_post_processor(pp: PMHD_PostProcessor): cint; cdecl; external MHD_LIB_NAME name 'MHD_destroy_post_processor'; + function MHD_digest_auth_get_username(connection: PMHD_Connection): Pcchar; cdecl; external MHD_LIB_NAME name 'MHD_digest_auth_get_username'; + function MHD_digest_auth_check(connection: PMHD_Connection; realm: Pcchar; username: Pcchar; password: Pcchar; nonce_timeout: cuint): cint; cdecl; external MHD_LIB_NAME name 'MHD_digest_auth_check'; + function MHD_queue_auth_fail_response(connection: PMHD_Connection; realm: Pcchar; opaque: Pcchar; response: PMHD_Response; signal_stale: cint): cint; cdecl; external MHD_LIB_NAME name 'MHD_queue_auth_fail_response'; + function MHD_basic_auth_get_username_password(connection: PMHD_Connection; password: PPcchar): Pcchar; cdecl; external MHD_LIB_NAME name 'MHD_basic_auth_get_username_password'; + function MHD_queue_basic_auth_fail_response(connection: PMHD_Connection; realm: Pcchar; response: PMHD_Response): cint; cdecl; external MHD_LIB_NAME name 'MHD_queue_basic_auth_fail_response'; + function MHD_get_connection_info(connection: PMHD_Connection; info_type: MHD_ConnectionInfoType): PMHD_ConnectionInfo; cdecl; varargs; external MHD_LIB_NAME name 'MHD_get_connection_info'; + function MHD_set_connection_option(connection: PMHD_Connection; option: MHD_CONNECTION_OPTION): cint; cdecl; varargs; external MHD_LIB_NAME name 'MHD_set_connection_option'; + function MHD_get_daemon_info(daemon: PMHD_Daemon; info_type: MHD_DaemonInfoType): PMHD_DaemonInfo; cdecl; varargs; external MHD_LIB_NAME name 'MHD_get_daemon_info'; + function MHD_get_version: Pcchar; cdecl; external MHD_LIB_NAME name 'MHD_get_version'; + function MHD_is_feature_supported(feature: MHD_FEATURE): cint; cdecl; external MHD_LIB_NAME name 'MHD_is_feature_supported'; + +implementation + +end. |