diff --git a/mbuni/Makefile.am b/mbuni/Makefile.am new file mode 100644 index 0000000..ef215c3 --- /dev/null +++ b/mbuni/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = mmlib mmsc diff --git a/mbuni/Makefile.in b/mbuni/Makefile.in new file mode 100644 index 0000000..2c73b3c --- /dev/null +++ b/mbuni/Makefile.in @@ -0,0 +1,563 @@ +# Makefile.in generated by automake 1.9.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = . +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = . +DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(srcdir)/config.h.in \ + $(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \ + TODO config.guess config.sub depcomp install-sh ltmain.sh \ + missing +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno configure.status.lineno +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + { test ! -d $(distdir) \ + || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -fr $(distdir); }; } +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +distuninstallcheck_listfiles = find . -type f -print +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXE_EXT = @EXE_EXT@ +GW_CONFIG = @GW_CONFIG@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host_alias = @host_alias@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = mmlib mmsc +all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +am--refresh: + @: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --gnu '; \ + cd $(srcdir) && $(AUTOMAKE) --gnu \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) + +config.h: stamp-h1 + @if test ! -f $@; then \ + rm -f stamp-h1; \ + $(MAKE) stamp-h1; \ + else :; fi + +stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status config.h +$(srcdir)/config.h.in: $(am__configure_deps) + cd $(top_srcdir) && $(AUTOHEADER) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.h stamp-h1 +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + $(am__remove_distdir) + mkdir $(distdir) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done + -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r $(distdir) +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2 + $(am__remove_distdir) + +dist-tarZ: distdir + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__remove_distdir) + +dist-shar: distdir + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__remove_distdir) + +dist dist-all: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir); chmod a+w $(distdir) + mkdir $(distdir)/_build + mkdir $(distdir)/_inst + chmod a-w $(distdir) + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && cd $(distdir)/_build \ + && ../configure --srcdir=.. --prefix="$$dc_install_base" \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck + $(am__remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}' +distuninstallcheck: + @cd $(distuninstallcheck_dir) \ + && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am +check: check-recursive +all-am: Makefile config.h +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-hdr distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am am--refresh check \ + check-am clean clean-generic clean-recursive ctags \ + ctags-recursive dist dist-all dist-bzip2 dist-gzip dist-shar \ + dist-tarZ dist-zip distcheck distclean distclean-generic \ + distclean-hdr distclean-recursive distclean-tags \ + distcleancheck distdir distuninstallcheck dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic maintainer-clean-recursive \ + mostlyclean mostlyclean-generic mostlyclean-recursive pdf \ + pdf-am ps ps-am tags tags-recursive uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/mbuni/NEWS b/mbuni/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/mbuni/Notes b/mbuni/Notes new file mode 100644 index 0000000..85ca60b --- /dev/null +++ b/mbuni/Notes @@ -0,0 +1,15 @@ +Requires: +- For media + - libiconv + - sox, lame, mpg123, amrencoder/amrdecoder (audio) + - imagemagick (image) + +- Mail server (postfix/sendmail) + +Wap setup: + - Port 9200/9201 for UDP WAP when not using security (connectless or otherwise) should be set aside + - Send port param with 9201 as port ID + - Put http proxy on ports 9200 and 9201 for HTTP based connections + - For nokia 6600, either manual setup, or send GPRS settings then change to use GSM and put in number to call (send + ) proxy address, etc. + diff --git a/mbuni/README b/mbuni/README new file mode 100644 index 0000000..e69de29 diff --git a/mbuni/doc/examples/mmsc.conf b/mbuni/doc/examples/mmsc.conf new file mode 100644 index 0000000..de2287a --- /dev/null +++ b/mbuni/doc/examples/mmsc.conf @@ -0,0 +1,45 @@ +group = core +log-file = log/ours.log +log-level = 0 + +group = mmsbox +name = "My MMSC" +hostname = ds.co.ug +host-alias = mmsc +local-mmsc-domains = ds.co.ug,dsmagic.com,lilo,lilo.ds.co.ug +local-prefixes = 034;+25634;25634 +send-queue-directory = spool/global +mm1-queue-directory = spool/mm1 +mm4-queue-directory = spool/mm4 +max-send-threads = 5 +send-mail-prog = /usr/sbin/sendmail -f '%f' '%t' +unified-prefix = "+25634,034,34" +maximum-send-attempts = 50 +default-message-expiry = 360000 +queue-run-interval = 0.1 +send-attempt-back-off = 300 +sendsms-url = http://localhost:13013/cgi-bin/sendsms +sendsms-username = tester +sendsms-password = foobar +#sendsms-global-sender = 198 +mms-port = 1981 +#allow-ip = 192.168.129.11 +email2mms-relay-prefixes = "034;035;25634" +ua-profile-cache-directory = misc/profiles +billing-module-parameters = "log/cdr.log" +# billing-library = billdemo.so +prov-server-notify-script = ~/src/mmprov/provnotify.sh +prov-server-sub-status-script = ~/src/mmprov/rcptstatus.sh +notify-unprovisioned = yes +mms-notify-text = "You have received a multimedia message from %S, go to XXX to view it" +mm-box-host = test.ds.co.ug +mms-notify-unprovisioned-text = "This is a test" +mms-to-email-txt = "This is a multimedia message (HTML suppressed)" +mms-to-email-html = "This is a multimedia message powered by Digital Solutions" +mms-message-too-large-txt = "You have received a multimedia message from %S that is too large for your phone. Go to xxx to view it" + +group = mmsproxy +name = "A test mms proxy" +host = test.com +allowed-prefix = "035" +denied-prefix = "034" diff --git a/mbuni/ltmain.sh b/mbuni/ltmain.sh new file mode 100755 index 0000000..5eb4c3a --- /dev/null +++ b/mbuni/ltmain.sh @@ -0,0 +1,6402 @@ +# ltmain.sh - Provide generalized library-building support services. +# NOTE: Changing this file will not affect anything until you rerun configure. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003 +# Free Software Foundation, Inc. +# Originally by Gordon Matzigkeit , 1996 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Check that we have a working $echo. +if test "X$1" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift +elif test "X$1" = X--fallback-echo; then + # Avoid inline document here, it may be left over + : +elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then + # Yippee, $echo works! + : +else + # Restart under the correct shell, and then maybe $echo will work. + exec $SHELL "$0" --no-reexec ${1+"$@"} +fi + +if test "X$1" = X--fallback-echo; then + # used as fallback echo + shift + cat <&2 + $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 + exit 1 +fi + +# Global variables. +mode=$default_mode +nonopt= +prev= +prevopt= +run= +show="$echo" +show_help= +execute_dlfiles= +lo2o="s/\\.lo\$/.${objext}/" +o2lo="s/\\.${objext}\$/.lo/" + +##################################### +# Shell function definitions: +# This seems to be the best place for them + +# Need a lot of goo to handle *both* DLLs and import libs +# Has to be a shell function in order to 'eat' the argument +# that is supplied when $file_magic_command is called. +win32_libid () { + win32_libid_type="unknown" + win32_fileres=`file -L $1 2>/dev/null` + case $win32_fileres in + *ar\ archive\ import\ library*) # definitely import + win32_libid_type="x86 archive import" + ;; + *ar\ archive*) # could be an import, or static + if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | \ + grep -E 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then + win32_nmres=`eval $NM -f posix -A $1 | \ + sed -n -e '1,100{/ I /{x;/import/!{s/^/import/;h;p;};x;};}'` + if test "X$win32_nmres" = "Ximport" ; then + win32_libid_type="x86 archive import" + else + win32_libid_type="x86 archive static" + fi + fi + ;; + *DLL*) + win32_libid_type="x86 DLL" + ;; + *executable*) # but shell scripts are "executable" too... + case $win32_fileres in + *MS\ Windows\ PE\ Intel*) + win32_libid_type="x86 DLL" + ;; + esac + ;; + esac + $echo $win32_libid_type +} + +# End of Shell function definitions +##################################### + +# Parse our command line options once, thoroughly. +while test "$#" -gt 0 +do + arg="$1" + shift + + case $arg in + -*=*) optarg=`$echo "X$arg" | $Xsed -e 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + execute_dlfiles) + execute_dlfiles="$execute_dlfiles $arg" + ;; + tag) + tagname="$arg" + preserve_args="${preserve_args}=$arg" + + # Check whether tagname contains only valid characters + case $tagname in + *[!-_A-Za-z0-9,/]*) + $echo "$progname: invalid tag name: $tagname" 1>&2 + exit 1 + ;; + esac + + case $tagname in + CC) + # Don't test for the "default" C tag, as we know, it's there, but + # not specially marked. + ;; + *) + if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "$0" > /dev/null; then + taglist="$taglist $tagname" + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$tagname'$/,/^# ### END LIBTOOL TAG CONFIG: '$tagname'$/p' < $0`" + else + $echo "$progname: ignoring unknown tag $tagname" 1>&2 + fi + ;; + esac + ;; + *) + eval "$prev=\$arg" + ;; + esac + + prev= + prevopt= + continue + fi + + # Have we seen a non-optional argument yet? + case $arg in + --help) + show_help=yes + ;; + + --version) + $echo "$PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP" + $echo + $echo "Copyright (C) 2003 Free Software Foundation, Inc." + $echo "This is free software; see the source for copying conditions. There is NO" + $echo "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + exit 0 + ;; + + --config) + ${SED} -e '1,/^# ### BEGIN LIBTOOL CONFIG/d' -e '/^# ### END LIBTOOL CONFIG/,$d' $0 + # Now print the configurations for the tags. + for tagname in $taglist; do + ${SED} -n -e "/^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$/,/^# ### END LIBTOOL TAG CONFIG: $tagname$/p" < "$0" + done + exit 0 + ;; + + --debug) + $echo "$progname: enabling shell trace mode" + set -x + preserve_args="$preserve_args $arg" + ;; + + --dry-run | -n) + run=: + ;; + + --features) + $echo "host: $host" + if test "$build_libtool_libs" = yes; then + $echo "enable shared libraries" + else + $echo "disable shared libraries" + fi + if test "$build_old_libs" = yes; then + $echo "enable static libraries" + else + $echo "disable static libraries" + fi + exit 0 + ;; + + --finish) mode="finish" ;; + + --mode) prevopt="--mode" prev=mode ;; + --mode=*) mode="$optarg" ;; + + --preserve-dup-deps) duplicate_deps="yes" ;; + + --quiet | --silent) + show=: + preserve_args="$preserve_args $arg" + ;; + + --tag) prevopt="--tag" prev=tag ;; + --tag=*) + set tag "$optarg" ${1+"$@"} + shift + prev=tag + preserve_args="$preserve_args --tag" + ;; + + -dlopen) + prevopt="-dlopen" + prev=execute_dlfiles + ;; + + -*) + $echo "$modename: unrecognized option \`$arg'" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; + + *) + nonopt="$arg" + break + ;; + esac +done + +if test -n "$prevopt"; then + $echo "$modename: option \`$prevopt' requires an argument" 1>&2 + $echo "$help" 1>&2 + exit 1 +fi + +# If this variable is set in any of the actions, the command in it +# will be execed at the end. This prevents here-documents from being +# left over by shells. +exec_cmd= + +if test -z "$show_help"; then + + # Infer the operation mode. + if test -z "$mode"; then + $echo "*** Warning: inferring the mode of operation is deprecated." 1>&2 + $echo "*** Future versions of Libtool will require -mode=MODE be specified." 1>&2 + case $nonopt in + *cc | cc* | *++ | gcc* | *-gcc* | g++* | xlc*) + mode=link + for arg + do + case $arg in + -c) + mode=compile + break + ;; + esac + done + ;; + *db | *dbx | *strace | *truss) + mode=execute + ;; + *install*|cp|mv) + mode=install + ;; + *rm) + mode=uninstall + ;; + *) + # If we have no mode, but dlfiles were specified, then do execute mode. + test -n "$execute_dlfiles" && mode=execute + + # Just use the default operation mode. + if test -z "$mode"; then + if test -n "$nonopt"; then + $echo "$modename: warning: cannot infer operation mode from \`$nonopt'" 1>&2 + else + $echo "$modename: warning: cannot infer operation mode without MODE-ARGS" 1>&2 + fi + fi + ;; + esac + fi + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$execute_dlfiles" && test "$mode" != execute; then + $echo "$modename: unrecognized option \`-dlopen'" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + # Change the help message to a mode-specific one. + generic_help="$help" + help="Try \`$modename --help --mode=$mode' for more information." + + # These modes are in order of execution frequency so that they run quickly. + case $mode in + # libtool compile mode + compile) + modename="$modename: compile" + # Get the compilation command and the source file. + base_compile= + srcfile="$nonopt" # always keep a non-empty value in "srcfile" + suppress_opt=yes + suppress_output= + arg_mode=normal + libobj= + later= + + for arg + do + case "$arg_mode" in + arg ) + # do not "continue". Instead, add this to base_compile + lastarg="$arg" + arg_mode=normal + ;; + + target ) + libobj="$arg" + arg_mode=normal + continue + ;; + + normal ) + # Accept any command-line options. + case $arg in + -o) + if test -n "$libobj" ; then + $echo "$modename: you cannot specify \`-o' more than once" 1>&2 + exit 1 + fi + arg_mode=target + continue + ;; + + -static | -prefer-pic | -prefer-non-pic) + later="$later $arg" + continue + ;; + + -no-suppress) + suppress_opt=no + continue + ;; + + -Xcompiler) + arg_mode=arg # the next one goes into the "base_compile" arg list + continue # The current "srcfile" will either be retained or + ;; # replaced later. I would guess that would be a bug. + + -Wc,*) + args=`$echo "X$arg" | $Xsed -e "s/^-Wc,//"` + lastarg= + save_ifs="$IFS"; IFS=',' + for arg in $args; do + IFS="$save_ifs" + + # Double-quote args containing other shell metacharacters. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + lastarg="$lastarg $arg" + done + IFS="$save_ifs" + lastarg=`$echo "X$lastarg" | $Xsed -e "s/^ //"` + + # Add the arguments to base_compile. + base_compile="$base_compile $lastarg" + continue + ;; + + * ) + # Accept the current argument as the source file. + # The previous "srcfile" becomes the current argument. + # + lastarg="$srcfile" + srcfile="$arg" + ;; + esac # case $arg + ;; + esac # case $arg_mode + + # Aesthetically quote the previous argument. + lastarg=`$echo "X$lastarg" | $Xsed -e "$sed_quote_subst"` + + case $lastarg in + # Double-quote args containing other shell metacharacters. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + lastarg="\"$lastarg\"" + ;; + esac + + base_compile="$base_compile $lastarg" + done # for arg + + case $arg_mode in + arg) + $echo "$modename: you must specify an argument for -Xcompile" + exit 1 + ;; + target) + $echo "$modename: you must specify a target with \`-o'" 1>&2 + exit 1 + ;; + *) + # Get the name of the library object. + [ -z "$libobj" ] && libobj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%'` + ;; + esac + + # Recognize several different file suffixes. + # If the user specifies -o file.o, it is replaced with file.lo + xform='[cCFSifmso]' + case $libobj in + *.ada) xform=ada ;; + *.adb) xform=adb ;; + *.ads) xform=ads ;; + *.asm) xform=asm ;; + *.c++) xform=c++ ;; + *.cc) xform=cc ;; + *.ii) xform=ii ;; + *.class) xform=class ;; + *.cpp) xform=cpp ;; + *.cxx) xform=cxx ;; + *.f90) xform=f90 ;; + *.for) xform=for ;; + *.java) xform=java ;; + esac + + libobj=`$echo "X$libobj" | $Xsed -e "s/\.$xform$/.lo/"` + + case $libobj in + *.lo) obj=`$echo "X$libobj" | $Xsed -e "$lo2o"` ;; + *) + $echo "$modename: cannot determine name of library object from \`$libobj'" 1>&2 + exit 1 + ;; + esac + + # Infer tagged configuration to use if any are available and + # if one wasn't chosen via the "--tag" command line option. + # Only attempt this if the compiler in the base compile + # command doesn't match the default compiler. + if test -n "$available_tags" && test -z "$tagname"; then + case $base_compile in + # Blanks in the command may have been stripped by the calling shell, + # but not from the CC environment variable when configure was run. + " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "*) ;; + # Blanks at the start of $base_compile will cause this to fail + # if we don't check for them as well. + *) + for z in $available_tags; do + if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$0" > /dev/null; then + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $0`" + case "$base_compile " in + "$CC "* | " $CC "* | "`$echo $CC` "* | " `$echo $CC` "*) + # The compiler in the base compile command matches + # the one in the tagged configuration. + # Assume this is the tagged configuration we want. + tagname=$z + break + ;; + esac + fi + done + # If $tagname still isn't set, then no tagged configuration + # was found and let the user know that the "--tag" command + # line option must be used. + if test -z "$tagname"; then + $echo "$modename: unable to infer tagged configuration" + $echo "$modename: specify a tag with \`--tag'" 1>&2 + exit 1 +# else +# $echo "$modename: using $tagname tagged configuration" + fi + ;; + esac + fi + + for arg in $later; do + case $arg in + -static) + build_old_libs=yes + continue + ;; + + -prefer-pic) + pic_mode=yes + continue + ;; + + -prefer-non-pic) + pic_mode=no + continue + ;; + esac + done + + objname=`$echo "X$obj" | $Xsed -e 's%^.*/%%'` + xdir=`$echo "X$obj" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$obj"; then + xdir= + else + xdir=$xdir/ + fi + lobj=${xdir}$objdir/$objname + + if test -z "$base_compile"; then + $echo "$modename: you must specify a compilation command" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + # Delete any leftover library objects. + if test "$build_old_libs" = yes; then + removelist="$obj $lobj $libobj ${libobj}T" + else + removelist="$lobj $libobj ${libobj}T" + fi + + $run $rm $removelist + trap "$run $rm $removelist; exit 1" 1 2 15 + + # On Cygwin there's no "real" PIC flag so we must build both object types + case $host_os in + cygwin* | mingw* | pw32* | os2*) + pic_mode=default + ;; + esac + if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then + # non-PIC code in shared libraries is not supported + pic_mode=default + fi + + # Calculate the filename of the output object if compiler does + # not support -o with -c + if test "$compiler_c_o" = no; then + output_obj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext} + lockfile="$output_obj.lock" + removelist="$removelist $output_obj $lockfile" + trap "$run $rm $removelist; exit 1" 1 2 15 + else + output_obj= + need_locks=no + lockfile= + fi + + # Lock this critical section if it is needed + # We use this script file to make the link, it avoids creating a new file + if test "$need_locks" = yes; then + until $run ln "$0" "$lockfile" 2>/dev/null; do + $show "Waiting for $lockfile to be removed" + sleep 2 + done + elif test "$need_locks" = warn; then + if test -f "$lockfile"; then + $echo "\ +*** ERROR, $lockfile exists and contains: +`cat $lockfile 2>/dev/null` + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit 1 + fi + $echo $srcfile > "$lockfile" + fi + + if test -n "$fix_srcfile_path"; then + eval srcfile=\"$fix_srcfile_path\" + fi + + $run $rm "$libobj" "${libobj}T" + + # Create a libtool object file (analogous to a ".la" file), + # but don't create it if we're doing a dry run. + test -z "$run" && cat > ${libobj}T </dev/null`" != "X$srcfile"; then + $echo "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit 1 + fi + + # Just move the object if needed, then go on to compile the next one + if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then + $show "$mv $output_obj $lobj" + if $run $mv $output_obj $lobj; then : + else + error=$? + $run $rm $removelist + exit $error + fi + fi + + # Append the name of the PIC object to the libtool object file. + test -z "$run" && cat >> ${libobj}T <> ${libobj}T </dev/null`" != "X$srcfile"; then + $echo "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit 1 + fi + + # Just move the object if needed + if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then + $show "$mv $output_obj $obj" + if $run $mv $output_obj $obj; then : + else + error=$? + $run $rm $removelist + exit $error + fi + fi + + # Append the name of the non-PIC object the libtool object file. + # Only append if the libtool object file exists. + test -z "$run" && cat >> ${libobj}T <> ${libobj}T < /dev/null; then + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $0`" + case $base_compile in + "$CC "* | " $CC "* | "`$echo $CC` "* | " `$echo $CC` "*) + # The compiler in $compile_command matches + # the one in the tagged configuration. + # Assume this is the tagged configuration we want. + tagname=$z + break + ;; + esac + fi + done + # If $tagname still isn't set, then no tagged configuration + # was found and let the user know that the "--tag" command + # line option must be used. + if test -z "$tagname"; then + $echo "$modename: unable to infer tagged configuration" + $echo "$modename: specify a tag with \`--tag'" 1>&2 + exit 1 +# else +# $echo "$modename: using $tagname tagged configuration" + fi + ;; + esac + fi + + # We need to know -static, to get the right output filenames. + for arg + do + case $arg in + -all-static | -static) + if test "X$arg" = "X-all-static"; then + if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then + $echo "$modename: warning: complete static linking is impossible in this configuration" 1>&2 + fi + if test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + else + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + fi + build_libtool_libs=no + build_old_libs=yes + prefer_static_libs=yes + break + ;; + esac + done + + # See if our shared archives depend on static archives. + test -n "$old_archive_from_new_cmds" && build_old_libs=yes + + # Go through the arguments, transforming them on the way. + while test "$#" -gt 0; do + arg="$1" + shift + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + qarg=\"`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`\" ### testsuite: skip nested quoting test + ;; + *) qarg=$arg ;; + esac + libtool_args="$libtool_args $qarg" + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + output) + compile_command="$compile_command @OUTPUT@" + finalize_command="$finalize_command @OUTPUT@" + ;; + esac + + case $prev in + dlfiles|dlprefiles) + if test "$preload" = no; then + # Add the symbol object into the linking commands. + compile_command="$compile_command @SYMFILE@" + finalize_command="$finalize_command @SYMFILE@" + preload=yes + fi + case $arg in + *.la | *.lo) ;; # We handle these cases below. + force) + if test "$dlself" = no; then + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + self) + if test "$prev" = dlprefiles; then + dlself=yes + elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then + dlself=yes + else + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + *) + if test "$prev" = dlfiles; then + dlfiles="$dlfiles $arg" + else + dlprefiles="$dlprefiles $arg" + fi + prev= + continue + ;; + esac + ;; + expsyms) + export_symbols="$arg" + if test ! -f "$arg"; then + $echo "$modename: symbol file \`$arg' does not exist" + exit 1 + fi + prev= + continue + ;; + expsyms_regex) + export_symbols_regex="$arg" + prev= + continue + ;; + inst_prefix) + inst_prefix_dir="$arg" + prev= + continue + ;; + precious_regex) + precious_files_regex="$arg" + prev= + continue + ;; + release) + release="-$arg" + prev= + continue + ;; + objectlist) + if test -f "$arg"; then + save_arg=$arg + moreargs= + for fil in `cat $save_arg` + do +# moreargs="$moreargs $fil" + arg=$fil + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + pic_object= + non_pic_object= + + # Read the .lo file + # If there is no directory component, then add one. + case $arg in + */* | *\\*) . $arg ;; + *) . ./$arg ;; + esac + + if test -z "$pic_object" || \ + test -z "$non_pic_object" || + test "$pic_object" = none && \ + test "$non_pic_object" = none; then + $echo "$modename: cannot find name of object for \`$arg'" 1>&2 + exit 1 + fi + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + dlfiles="$dlfiles $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles $pic_object" + prev= + fi + + # A PIC object. + libobjs="$libobjs $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + non_pic_objects="$non_pic_objects $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + fi + else + # Only an error if not doing a dry-run. + if test -z "$run"; then + $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 + exit 1 + else + # Dry-run case. + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` + non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` + libobjs="$libobjs $pic_object" + non_pic_objects="$non_pic_objects $non_pic_object" + fi + fi + done + else + $echo "$modename: link input file \`$save_arg' does not exist" + exit 1 + fi + arg=$save_arg + prev= + continue + ;; + rpath | xrpath) + # We need an absolute path. + case $arg in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + $echo "$modename: only absolute run-paths are allowed" 1>&2 + exit 1 + ;; + esac + if test "$prev" = rpath; then + case "$rpath " in + *" $arg "*) ;; + *) rpath="$rpath $arg" ;; + esac + else + case "$xrpath " in + *" $arg "*) ;; + *) xrpath="$xrpath $arg" ;; + esac + fi + prev= + continue + ;; + xcompiler) + compiler_flags="$compiler_flags $qarg" + prev= + compile_command="$compile_command $qarg" + finalize_command="$finalize_command $qarg" + continue + ;; + xlinker) + linker_flags="$linker_flags $qarg" + compiler_flags="$compiler_flags $wl$qarg" + prev= + compile_command="$compile_command $wl$qarg" + finalize_command="$finalize_command $wl$qarg" + continue + ;; + xcclinker) + linker_flags="$linker_flags $qarg" + compiler_flags="$compiler_flags $qarg" + prev= + compile_command="$compile_command $qarg" + finalize_command="$finalize_command $qarg" + continue + ;; + *) + eval "$prev=\"\$arg\"" + prev= + continue + ;; + esac + fi # test -n "$prev" + + prevarg="$arg" + + case $arg in + -all-static) + if test -n "$link_static_flag"; then + compile_command="$compile_command $link_static_flag" + finalize_command="$finalize_command $link_static_flag" + fi + continue + ;; + + -allow-undefined) + # FIXME: remove this flag sometime in the future. + $echo "$modename: \`-allow-undefined' is deprecated because it is the default" 1>&2 + continue + ;; + + -avoid-version) + avoid_version=yes + continue + ;; + + -dlopen) + prev=dlfiles + continue + ;; + + -dlpreopen) + prev=dlprefiles + continue + ;; + + -export-dynamic) + export_dynamic=yes + continue + ;; + + -export-symbols | -export-symbols-regex) + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + $echo "$modename: more than one -exported-symbols argument is not allowed" + exit 1 + fi + if test "X$arg" = "X-export-symbols"; then + prev=expsyms + else + prev=expsyms_regex + fi + continue + ;; + + -inst-prefix-dir) + prev=inst_prefix + continue + ;; + + # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* + # so, if we see these flags be careful not to treat them like -L + -L[A-Z][A-Z]*:*) + case $with_gcc/$host in + no/*-*-irix* | /*-*-irix*) + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + ;; + esac + continue + ;; + + -L*) + dir=`$echo "X$arg" | $Xsed -e 's/^-L//'` + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + $echo "$modename: cannot determine absolute directory name of \`$dir'" 1>&2 + exit 1 + fi + dir="$absdir" + ;; + esac + case "$deplibs " in + *" -L$dir "*) ;; + *) + deplibs="$deplibs -L$dir" + lib_search_path="$lib_search_path $dir" + ;; + esac + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) + case :$dllsearchpath: in + *":$dir:"*) ;; + *) dllsearchpath="$dllsearchpath:$dir";; + esac + ;; + esac + continue + ;; + + -l*) + if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then + case $host in + *-*-cygwin* | *-*-pw32* | *-*-beos*) + # These systems don't actually have a C or math library (as such) + continue + ;; + *-*-mingw* | *-*-os2*) + # These systems don't actually have a C library (as such) + test "X$arg" = "X-lc" && continue + ;; + *-*-openbsd* | *-*-freebsd*) + # Do not include libc due to us having libc/libc_r. + test "X$arg" = "X-lc" && continue + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C and math libraries are in the System framework + deplibs="$deplibs -framework System" + continue + esac + elif test "X$arg" = "X-lc_r"; then + case $host in + *-*-openbsd* | *-*-freebsd*) + # Do not include libc_r directly, use -pthread flag. + continue + ;; + esac + fi + deplibs="$deplibs $arg" + continue + ;; + + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe) + deplibs="$deplibs $arg" + continue + ;; + + -module) + module=yes + continue + ;; + + # gcc -m* arguments should be passed to the linker via $compiler_flags + # in order to pass architecture information to the linker + # (e.g. 32 vs 64-bit). This may also be accomplished via -Wl,-mfoo + # but this is not reliable with gcc because gcc may use -mfoo to + # select a different linker, different libraries, etc, while + # -Wl,-mfoo simply passes -mfoo to the linker. + -m*) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + if test "$with_gcc" = "yes" ; then + compiler_flags="$compiler_flags $arg" + fi + continue + ;; + + -shrext) + prev=shrext + continue + ;; + + -no-fast-install) + fast_install=no + continue + ;; + + -no-install) + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) + # The PATH hackery in wrapper scripts is required on Windows + # in order for the loader to find any dlls it needs. + $echo "$modename: warning: \`-no-install' is ignored for $host" 1>&2 + $echo "$modename: warning: assuming \`-no-fast-install' instead" 1>&2 + fast_install=no + ;; + *) no_install=yes ;; + esac + continue + ;; + + -no-undefined) + allow_undefined=no + continue + ;; + + -objectlist) + prev=objectlist + continue + ;; + + -o) prev=output ;; + + -precious-files-regex) + prev=precious_regex + continue + ;; + + -release) + prev=release + continue + ;; + + -rpath) + prev=rpath + continue + ;; + + -R) + prev=xrpath + continue + ;; + + -R*) + dir=`$echo "X$arg" | $Xsed -e 's/^-R//'` + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + $echo "$modename: only absolute run-paths are allowed" 1>&2 + exit 1 + ;; + esac + case "$xrpath " in + *" $dir "*) ;; + *) xrpath="$xrpath $dir" ;; + esac + continue + ;; + + -static) + # The effects of -static are defined in a previous loop. + # We used to do the same as -all-static on platforms that + # didn't have a PIC flag, but the assumption that the effects + # would be equivalent was wrong. It would break on at least + # Digital Unix and AIX. + continue + ;; + + -thread-safe) + thread_safe=yes + continue + ;; + + -version-info) + prev=vinfo + continue + ;; + -version-number) + prev=vinfo + vinfo_number=yes + continue + ;; + + -Wc,*) + args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wc,//'` + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + case $flag in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + flag="\"$flag\"" + ;; + esac + arg="$arg $wl$flag" + compiler_flags="$compiler_flags $flag" + done + IFS="$save_ifs" + arg=`$echo "X$arg" | $Xsed -e "s/^ //"` + ;; + + -Wl,*) + args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wl,//'` + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + case $flag in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + flag="\"$flag\"" + ;; + esac + arg="$arg $wl$flag" + compiler_flags="$compiler_flags $wl$flag" + linker_flags="$linker_flags $flag" + done + IFS="$save_ifs" + arg=`$echo "X$arg" | $Xsed -e "s/^ //"` + ;; + + -Xcompiler) + prev=xcompiler + continue + ;; + + -Xlinker) + prev=xlinker + continue + ;; + + -XCClinker) + prev=xcclinker + continue + ;; + + # Some other compiler flag. + -* | +*) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + ;; + + *.$objext) + # A standard object. + objs="$objs $arg" + ;; + + *.lo) + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + pic_object= + non_pic_object= + + # Read the .lo file + # If there is no directory component, then add one. + case $arg in + */* | *\\*) . $arg ;; + *) . ./$arg ;; + esac + + if test -z "$pic_object" || \ + test -z "$non_pic_object" || + test "$pic_object" = none && \ + test "$non_pic_object" = none; then + $echo "$modename: cannot find name of object for \`$arg'" 1>&2 + exit 1 + fi + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + dlfiles="$dlfiles $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles $pic_object" + prev= + fi + + # A PIC object. + libobjs="$libobjs $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + non_pic_objects="$non_pic_objects $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + fi + else + # Only an error if not doing a dry-run. + if test -z "$run"; then + $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 + exit 1 + else + # Dry-run case. + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` + non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` + libobjs="$libobjs $pic_object" + non_pic_objects="$non_pic_objects $non_pic_object" + fi + fi + ;; + + *.$libext) + # An archive. + deplibs="$deplibs $arg" + old_deplibs="$old_deplibs $arg" + continue + ;; + + *.la) + # A libtool-controlled library. + + if test "$prev" = dlfiles; then + # This library was specified with -dlopen. + dlfiles="$dlfiles $arg" + prev= + elif test "$prev" = dlprefiles; then + # The library was specified with -dlpreopen. + dlprefiles="$dlprefiles $arg" + prev= + else + deplibs="$deplibs $arg" + fi + continue + ;; + + # Some other compiler argument. + *) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + ;; + esac # arg + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + fi + done # argument parsing loop + + if test -n "$prev"; then + $echo "$modename: the \`$prevarg' option requires an argument" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + fi + + oldlibs= + # calculate the name of the file, without its directory + outputname=`$echo "X$output" | $Xsed -e 's%^.*/%%'` + libobjs_save="$libobjs" + + if test -n "$shlibpath_var"; then + # get the directories listed in $shlibpath_var + eval shlib_search_path=\`\$echo \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\` + else + shlib_search_path= + fi + eval sys_lib_search_path=\"$sys_lib_search_path_spec\" + eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" + + output_objdir=`$echo "X$output" | $Xsed -e 's%/[^/]*$%%'` + if test "X$output_objdir" = "X$output"; then + output_objdir="$objdir" + else + output_objdir="$output_objdir/$objdir" + fi + # Create the object directory. + if test ! -d "$output_objdir"; then + $show "$mkdir $output_objdir" + $run $mkdir $output_objdir + status=$? + if test "$status" -ne 0 && test ! -d "$output_objdir"; then + exit $status + fi + fi + + # Determine the type of output + case $output in + "") + $echo "$modename: you must specify an output file" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; + *.$libext) linkmode=oldlib ;; + *.lo | *.$objext) linkmode=obj ;; + *.la) linkmode=lib ;; + *) linkmode=prog ;; # Anything else should be a program. + esac + + case $host in + *cygwin* | *mingw* | *pw32*) + # don't eliminate duplcations in $postdeps and $predeps + duplicate_compiler_generated_deps=yes + ;; + *) + duplicate_compiler_generated_deps=$duplicate_deps + ;; + esac + specialdeplibs= + + libs= + # Find all interdependent deplibs by searching for libraries + # that are linked more than once (e.g. -la -lb -la) + for deplib in $deplibs; do + if test "X$duplicate_deps" = "Xyes" ; then + case "$libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + libs="$libs $deplib" + done + + if test "$linkmode" = lib; then + libs="$predeps $libs $compiler_lib_search_path $postdeps" + + # Compute libraries that are listed more than once in $predeps + # $postdeps and mark them as special (i.e., whose duplicates are + # not to be eliminated). + pre_post_deps= + if test "X$duplicate_compiler_generated_deps" = "Xyes" ; then + for pre_post_dep in $predeps $postdeps; do + case "$pre_post_deps " in + *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;; + esac + pre_post_deps="$pre_post_deps $pre_post_dep" + done + fi + pre_post_deps= + fi + + deplibs= + newdependency_libs= + newlib_search_path= + need_relink=no # whether we're linking any uninstalled libtool libraries + notinst_deplibs= # not-installed libtool libraries + notinst_path= # paths that contain not-installed libtool libraries + case $linkmode in + lib) + passes="conv link" + for file in $dlfiles $dlprefiles; do + case $file in + *.la) ;; + *) + $echo "$modename: libraries can \`-dlopen' only libtool libraries: $file" 1>&2 + exit 1 + ;; + esac + done + ;; + prog) + compile_deplibs= + finalize_deplibs= + alldeplibs=no + newdlfiles= + newdlprefiles= + passes="conv scan dlopen dlpreopen link" + ;; + *) passes="conv" + ;; + esac + for pass in $passes; do + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan"; then + libs="$deplibs" + deplibs= + fi + if test "$linkmode" = prog; then + case $pass in + dlopen) libs="$dlfiles" ;; + dlpreopen) libs="$dlprefiles" ;; + link) libs="$deplibs %DEPLIBS% $dependency_libs" ;; + esac + fi + if test "$pass" = dlopen; then + # Collect dlpreopened libraries + save_deplibs="$deplibs" + deplibs= + fi + for deplib in $libs; do + lib= + found=no + case $deplib in + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + fi + continue + ;; + -l*) + if test "$linkmode" != lib && test "$linkmode" != prog; then + $echo "$modename: warning: \`-l' is ignored for archives/objects" 1>&2 + continue + fi + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + name=`$echo "X$deplib" | $Xsed -e 's/^-l//'` + for searchdir in $newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path; do + for search_ext in .la $shrext .so .a; do + # Search the libtool library + lib="$searchdir/lib${name}${search_ext}" + if test -f "$lib"; then + if test "$search_ext" = ".la"; then + found=yes + else + found=no + fi + break 2 + fi + done + done + if test "$found" != yes; then + # deplib doesn't seem to be a libtool library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + else # deplib is a libtool library + # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, + # We need to do some special things here, and not later. + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $deplib "*) + if (${SED} -e '2q' $lib | + grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + library_names= + old_library= + case $lib in + */* | *\\*) . $lib ;; + *) . ./$lib ;; + esac + for l in $old_library $library_names; do + ll="$l" + done + if test "X$ll" = "X$old_library" ; then # only static version available + found=no + ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` + test "X$ladir" = "X$lib" && ladir="." + lib=$ladir/$old_library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + fi + fi + ;; + *) ;; + esac + fi + fi + ;; # -l + -L*) + case $linkmode in + lib) + deplibs="$deplib $deplibs" + test "$pass" = conv && continue + newdependency_libs="$deplib $newdependency_libs" + newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` + ;; + prog) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + if test "$pass" = scan; then + deplibs="$deplib $deplibs" + newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + ;; + *) + $echo "$modename: warning: \`-L' is ignored for archives/objects" 1>&2 + ;; + esac # linkmode + continue + ;; # -L + -R*) + if test "$pass" = link; then + dir=`$echo "X$deplib" | $Xsed -e 's/^-R//'` + # Make sure the xrpath contains only unique directories. + case "$xrpath " in + *" $dir "*) ;; + *) xrpath="$xrpath $dir" ;; + esac + fi + deplibs="$deplib $deplibs" + continue + ;; + *.la) lib="$deplib" ;; + *.$libext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + case $linkmode in + lib) + if test "$deplibs_check_method" != pass_all; then + $echo + $echo "*** Warning: Trying to link with static lib archive $deplib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have" + $echo "*** because the file extensions .$libext of this argument makes me believe" + $echo "*** that it is just a static archive that I should not used here." + else + $echo + $echo "*** Warning: Linking the shared library $output against the" + $echo "*** static library $deplib is not portable!" + deplibs="$deplib $deplibs" + fi + continue + ;; + prog) + if test "$pass" != link; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + continue + ;; + esac # linkmode + ;; # *.$libext + *.lo | *.$objext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + elif test "$linkmode" = prog; then + if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then + # If there is no dlopen support or we're linking statically, + # we need to preload. + newdlprefiles="$newdlprefiles $deplib" + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + newdlfiles="$newdlfiles $deplib" + fi + fi + continue + ;; + %DEPLIBS%) + alldeplibs=yes + continue + ;; + esac # case $deplib + if test "$found" = yes || test -f "$lib"; then : + else + $echo "$modename: cannot find the library \`$lib'" 1>&2 + exit 1 + fi + + # Check to see that this really is a libtool archive. + if (${SED} -e '2q' $lib | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + else + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + exit 1 + fi + + ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` + test "X$ladir" = "X$lib" && ladir="." + + dlname= + dlopen= + dlpreopen= + libdir= + library_names= + old_library= + # If the library was installed with an old release of libtool, + # it will not redefine variables installed, or shouldnotlink + installed=yes + shouldnotlink=no + + # Read the .la file + case $lib in + */* | *\\*) . $lib ;; + *) . ./$lib ;; + esac + + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan" || + { test "$linkmode" != prog && test "$linkmode" != lib; }; then + test -n "$dlopen" && dlfiles="$dlfiles $dlopen" + test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen" + fi + + if test "$pass" = conv; then + # Only check for convenience libraries + deplibs="$lib $deplibs" + if test -z "$libdir"; then + if test -z "$old_library"; then + $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 + exit 1 + fi + # It is a libtool convenience library, so add in its objects. + convenience="$convenience $ladir/$objdir/$old_library" + old_convenience="$old_convenience $ladir/$objdir/$old_library" + tmp_libs= + for deplib in $dependency_libs; do + deplibs="$deplib $deplibs" + if test "X$duplicate_deps" = "Xyes" ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done + elif test "$linkmode" != prog && test "$linkmode" != lib; then + $echo "$modename: \`$lib' is not a convenience library" 1>&2 + exit 1 + fi + continue + fi # $pass = conv + + + # Get the name of the library we link against. + linklib= + for l in $old_library $library_names; do + linklib="$l" + done + if test -z "$linklib"; then + $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 + exit 1 + fi + + # This library was specified with -dlopen. + if test "$pass" = dlopen; then + if test -z "$libdir"; then + $echo "$modename: cannot -dlopen a convenience library: \`$lib'" 1>&2 + exit 1 + fi + if test -z "$dlname" || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then + # If there is no dlname, no dlopen support or we're linking + # statically, we need to preload. We also need to preload any + # dependent libraries so libltdl's deplib preloader doesn't + # bomb out in the load deplibs phase. + dlprefiles="$dlprefiles $lib $dependency_libs" + else + newdlfiles="$newdlfiles $lib" + fi + continue + fi # $pass = dlopen + + # We need an absolute path. + case $ladir in + [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; + *) + abs_ladir=`cd "$ladir" && pwd` + if test -z "$abs_ladir"; then + $echo "$modename: warning: cannot determine absolute directory name of \`$ladir'" 1>&2 + $echo "$modename: passing it literally to the linker, although it might fail" 1>&2 + abs_ladir="$ladir" + fi + ;; + esac + laname=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` + + # Find the relevant object directory and library name. + if test "X$installed" = Xyes; then + if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then + $echo "$modename: warning: library \`$lib' was moved." 1>&2 + dir="$ladir" + absdir="$abs_ladir" + libdir="$abs_ladir" + else + dir="$libdir" + absdir="$libdir" + fi + else + dir="$ladir/$objdir" + absdir="$abs_ladir/$objdir" + # Remove this search path later + notinst_path="$notinst_path $abs_ladir" + fi # $installed = yes + name=`$echo "X$laname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` + + # This library was specified with -dlpreopen. + if test "$pass" = dlpreopen; then + if test -z "$libdir"; then + $echo "$modename: cannot -dlpreopen a convenience library: \`$lib'" 1>&2 + exit 1 + fi + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + newdlprefiles="$newdlprefiles $dir/$old_library" + # Otherwise, use the dlname, so that lt_dlopen finds it. + elif test -n "$dlname"; then + newdlprefiles="$newdlprefiles $dir/$dlname" + else + newdlprefiles="$newdlprefiles $dir/$linklib" + fi + fi # $pass = dlpreopen + + if test -z "$libdir"; then + # Link the convenience library + if test "$linkmode" = lib; then + deplibs="$dir/$old_library $deplibs" + elif test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$dir/$old_library $compile_deplibs" + finalize_deplibs="$dir/$old_library $finalize_deplibs" + else + deplibs="$lib $deplibs" # used for prog,scan pass + fi + continue + fi + + + if test "$linkmode" = prog && test "$pass" != link; then + newlib_search_path="$newlib_search_path $ladir" + deplibs="$lib $deplibs" + + linkalldeplibs=no + if test "$link_all_deplibs" != no || test -z "$library_names" || + test "$build_libtool_libs" = no; then + linkalldeplibs=yes + fi + + tmp_libs= + for deplib in $dependency_libs; do + case $deplib in + -L*) newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'`;; ### testsuite: skip nested quoting test + esac + # Need to link against all dependency_libs? + if test "$linkalldeplibs" = yes; then + deplibs="$deplib $deplibs" + else + # Need to hardcode shared library paths + # or/and link against static libraries + newdependency_libs="$deplib $newdependency_libs" + fi + if test "X$duplicate_deps" = "Xyes" ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done # for deplib + continue + fi # $linkmode = prog... + + if test "$linkmode,$pass" = "prog,link"; then + if test -n "$library_names" && + { test "$prefer_static_libs" = no || test -z "$old_library"; }; then + # We need to hardcode the library path + if test -n "$shlibpath_var"; then + # Make sure the rpath contains only unique directories. + case "$temp_rpath " in + *" $dir "*) ;; + *" $absdir "*) ;; + *) temp_rpath="$temp_rpath $dir" ;; + esac + fi + + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) compile_rpath="$compile_rpath $absdir" + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" + esac + ;; + esac + fi # $linkmode,$pass = prog,link... + + if test "$alldeplibs" = yes && + { test "$deplibs_check_method" = pass_all || + { test "$build_libtool_libs" = yes && + test -n "$library_names"; }; }; then + # We only need to search for static libraries + continue + fi + fi + + link_static=no # Whether the deplib will be linked statically + if test -n "$library_names" && + { test "$prefer_static_libs" = no || test -z "$old_library"; }; then + if test "$installed" = no; then + notinst_deplibs="$notinst_deplibs $lib" + need_relink=yes + fi + # This is a shared library + + # Warn about portability, can't link against -module's on some systems (darwin) + if test "$shouldnotlink" = yes && test "$pass" = link ; then + $echo + if test "$linkmode" = prog; then + $echo "*** Warning: Linking the executable $output against the loadable module" + else + $echo "*** Warning: Linking the shared library $output against the loadable module" + fi + $echo "*** $linklib is not portable!" + fi + if test "$linkmode" = lib && + test "$hardcode_into_libs" = yes; then + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) compile_rpath="$compile_rpath $absdir" + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" + esac + ;; + esac + fi + + if test -n "$old_archive_from_expsyms_cmds"; then + # figure out the soname + set dummy $library_names + realname="$2" + shift; shift + libname=`eval \\$echo \"$libname_spec\"` + # use dlname if we got it. it's perfectly good, no? + if test -n "$dlname"; then + soname="$dlname" + elif test -n "$soname_spec"; then + # bleh windows + case $host in + *cygwin* | mingw*) + major=`expr $current - $age` + versuffix="-$major" + ;; + esac + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + + # Make a new name for the extract_expsyms_cmds to use + soroot="$soname" + soname=`$echo $soroot | ${SED} -e 's/^.*\///'` + newlib="libimp-`$echo $soname | ${SED} 's/^lib//;s/\.dll$//'`.a" + + # If the library has no export list, then create one now + if test -f "$output_objdir/$soname-def"; then : + else + $show "extracting exported symbol list from \`$soname'" + save_ifs="$IFS"; IFS='~' + cmds=$extract_expsyms_cmds + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + + # Create $newlib + if test -f "$output_objdir/$newlib"; then :; else + $show "generating import library for \`$soname'" + save_ifs="$IFS"; IFS='~' + cmds=$old_archive_from_expsyms_cmds + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + # make sure the library variables are pointing to the new library + dir=$output_objdir + linklib=$newlib + fi # test -n "$old_archive_from_expsyms_cmds" + + if test "$linkmode" = prog || test "$mode" != relink; then + add_shlibpath= + add_dir= + add= + lib_linked=yes + case $hardcode_action in + immediate | unsupported) + if test "$hardcode_direct" = no; then + add="$dir/$linklib" + case $host in + *-*-sco3.2v5* ) add_dir="-L$dir" ;; + *-*-darwin* ) + # if the lib is a module then we can not link against it, someone + # is ignoring the new warnings I added + if /usr/bin/file -L $add 2> /dev/null | grep "bundle" >/dev/null ; then + $echo "** Warning, lib $linklib is a module, not a shared library" + if test -z "$old_library" ; then + $echo + $echo "** And there doesn't seem to be a static archive available" + $echo "** The link will probably fail, sorry" + else + add="$dir/$old_library" + fi + fi + esac + elif test "$hardcode_minus_L" = no; then + case $host in + *-*-sunos*) add_shlibpath="$dir" ;; + esac + add_dir="-L$dir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = no; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + relink) + if test "$hardcode_direct" = yes; then + add="$dir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$dir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case "$libdir" in + [\\/]*) + add_dir="$add_dir -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + *) lib_linked=no ;; + esac + + if test "$lib_linked" != yes; then + $echo "$modename: configuration error: unsupported hardcode properties" + exit 1 + fi + + if test -n "$add_shlibpath"; then + case :$compile_shlibpath: in + *":$add_shlibpath:"*) ;; + *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;; + esac + fi + if test "$linkmode" = prog; then + test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" + test -n "$add" && compile_deplibs="$add $compile_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + if test "$hardcode_direct" != yes && \ + test "$hardcode_minus_L" != yes && \ + test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; + esac + fi + fi + fi + + if test "$linkmode" = prog || test "$mode" = relink; then + add_shlibpath= + add_dir= + add= + # Finalize command for both is simple: just hardcode it. + if test "$hardcode_direct" = yes; then + add="$libdir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$libdir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; + esac + add="-l$name" + elif test "$hardcode_automatic" = yes; then + if test -n "$inst_prefix_dir" && test -f "$inst_prefix_dir$libdir/$linklib" ; then + add="$inst_prefix_dir$libdir/$linklib" + else + add="$libdir/$linklib" + fi + else + # We cannot seem to hardcode it, guess we'll fake it. + add_dir="-L$libdir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case "$libdir" in + [\\/]*) + add_dir="$add_dir -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + fi + + if test "$linkmode" = prog; then + test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" + test -n "$add" && finalize_deplibs="$add $finalize_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + fi + fi + elif test "$linkmode" = prog; then + # Here we assume that one of hardcode_direct or hardcode_minus_L + # is not unsupported. This is valid on all known static and + # shared platforms. + if test "$hardcode_direct" != unsupported; then + test -n "$old_library" && linklib="$old_library" + compile_deplibs="$dir/$linklib $compile_deplibs" + finalize_deplibs="$dir/$linklib $finalize_deplibs" + else + compile_deplibs="-l$name -L$dir $compile_deplibs" + finalize_deplibs="-l$name -L$dir $finalize_deplibs" + fi + elif test "$build_libtool_libs" = yes; then + # Not a shared library + if test "$deplibs_check_method" != pass_all; then + # We're trying link a shared library against a static one + # but the system doesn't support it. + + # Just print a warning and add the library to dependency_libs so + # that the program can be linked against the static library. + $echo + $echo "*** Warning: This system can not link to static lib archive $lib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have." + if test "$module" = yes; then + $echo "*** But as you try to build a module library, libtool will still create " + $echo "*** a static module, that should work as long as the dlopening application" + $echo "*** is linked with the -dlopen flag to resolve symbols at runtime." + if test -z "$global_symbol_pipe"; then + $echo + $echo "*** However, this would only work if libtool was able to extract symbol" + $echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + $echo "*** not find such a program. So, this module is probably useless." + $echo "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + else + convenience="$convenience $dir/$old_library" + old_convenience="$old_convenience $dir/$old_library" + deplibs="$dir/$old_library $deplibs" + link_static=yes + fi + fi # link shared/static library? + + if test "$linkmode" = lib; then + if test -n "$dependency_libs" && + { test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes || + test "$link_static" = yes; }; then + # Extract -R from dependency_libs + temp_deplibs= + for libdir in $dependency_libs; do + case $libdir in + -R*) temp_xrpath=`$echo "X$libdir" | $Xsed -e 's/^-R//'` + case " $xrpath " in + *" $temp_xrpath "*) ;; + *) xrpath="$xrpath $temp_xrpath";; + esac;; + *) temp_deplibs="$temp_deplibs $libdir";; + esac + done + dependency_libs="$temp_deplibs" + fi + + newlib_search_path="$newlib_search_path $absdir" + # Link against this library + test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" + # ... and its dependency_libs + tmp_libs= + for deplib in $dependency_libs; do + newdependency_libs="$deplib $newdependency_libs" + if test "X$duplicate_deps" = "Xyes" ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done + + if test "$link_all_deplibs" != no; then + # Add the search paths of all dependency libraries + for deplib in $dependency_libs; do + case $deplib in + -L*) path="$deplib" ;; + *.la) + dir=`$echo "X$deplib" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$deplib" && dir="." + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + $echo "$modename: warning: cannot determine absolute directory name of \`$dir'" 1>&2 + absdir="$dir" + fi + ;; + esac + if grep "^installed=no" $deplib > /dev/null; then + path="$absdir/$objdir" + else + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + if test -z "$libdir"; then + $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2 + exit 1 + fi + if test "$absdir" != "$libdir"; then + $echo "$modename: warning: \`$deplib' seems to be moved" 1>&2 + fi + path="$absdir" + fi + depdepl= + case $host in + *-*-darwin*) + # we do not want to link against static libs, but need to link against shared + eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` + if test -n "$deplibrary_names" ; then + for tmp in $deplibrary_names ; do + depdepl=$tmp + done + if test -f "$path/$depdepl" ; then + depdepl="$path/$depdepl" + fi + # do not add paths which are already there + case " $newlib_search_path " in + *" $path "*) ;; + *) newlib_search_path="$newlib_search_path $path";; + esac + fi + path="" + ;; + *) + path="-L$path" + ;; + esac + + ;; + -l*) + case $host in + *-*-darwin*) + # Again, we only want to link against shared libraries + eval tmp_libs=`$echo "X$deplib" | $Xsed -e "s,^\-l,,"` + for tmp in $newlib_search_path ; do + if test -f "$tmp/lib$tmp_libs.dylib" ; then + eval depdepl="$tmp/lib$tmp_libs.dylib" + break + fi + done + path="" + ;; + *) continue ;; + esac + ;; + *) continue ;; + esac + case " $deplibs " in + *" $depdepl "*) ;; + *) deplibs="$deplibs $depdepl" ;; + esac + case " $deplibs " in + *" $path "*) ;; + *) deplibs="$deplibs $path" ;; + esac + done + fi # link_all_deplibs != no + fi # linkmode = lib + done # for deplib in $libs + dependency_libs="$newdependency_libs" + if test "$pass" = dlpreopen; then + # Link the dlpreopened libraries before other libraries + for deplib in $save_deplibs; do + deplibs="$deplib $deplibs" + done + fi + if test "$pass" != dlopen; then + if test "$pass" != conv; then + # Make sure lib_search_path contains only unique directories. + lib_search_path= + for dir in $newlib_search_path; do + case "$lib_search_path " in + *" $dir "*) ;; + *) lib_search_path="$lib_search_path $dir" ;; + esac + done + newlib_search_path= + fi + + if test "$linkmode,$pass" != "prog,link"; then + vars="deplibs" + else + vars="compile_deplibs finalize_deplibs" + fi + for var in $vars dependency_libs; do + # Add libraries to $var in reverse order + eval tmp_libs=\"\$$var\" + new_libs= + for deplib in $tmp_libs; do + # FIXME: Pedantically, this is the right thing to do, so + # that some nasty dependency loop isn't accidentally + # broken: + #new_libs="$deplib $new_libs" + # Pragmatically, this seems to cause very few problems in + # practice: + case $deplib in + -L*) new_libs="$deplib $new_libs" ;; + -R*) ;; + *) + # And here is the reason: when a library appears more + # than once as an explicit dependence of a library, or + # is implicitly linked in more than once by the + # compiler, it is considered special, and multiple + # occurrences thereof are not removed. Compare this + # with having the same library being listed as a + # dependency of multiple other libraries: in this case, + # we know (pedantically, we assume) the library does not + # need to be listed more than once, so we keep only the + # last copy. This is not always right, but it is rare + # enough that we require users that really mean to play + # such unportable linking tricks to link the library + # using -Wl,-lname, so that libtool does not consider it + # for duplicate removal. + case " $specialdeplibs " in + *" $deplib "*) new_libs="$deplib $new_libs" ;; + *) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$deplib $new_libs" ;; + esac + ;; + esac + ;; + esac + done + tmp_libs= + for deplib in $new_libs; do + case $deplib in + -L*) + case " $tmp_libs " in + *" $deplib "*) ;; + *) tmp_libs="$tmp_libs $deplib" ;; + esac + ;; + *) tmp_libs="$tmp_libs $deplib" ;; + esac + done + eval $var=\"$tmp_libs\" + done # for var + fi + # Last step: remove runtime libs from dependency_libs (they stay in deplibs) + tmp_libs= + for i in $dependency_libs ; do + case " $predeps $postdeps $compiler_lib_search_path " in + *" $i "*) + i="" + ;; + esac + if test -n "$i" ; then + tmp_libs="$tmp_libs $i" + fi + done + dependency_libs=$tmp_libs + done # for pass + if test "$linkmode" = prog; then + dlfiles="$newdlfiles" + dlprefiles="$newdlprefiles" + fi + + case $linkmode in + oldlib) + if test -n "$deplibs"; then + $echo "$modename: warning: \`-l' and \`-L' are ignored for archives" 1>&2 + fi + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + $echo "$modename: warning: \`-dlopen' is ignored for archives" 1>&2 + fi + + if test -n "$rpath"; then + $echo "$modename: warning: \`-rpath' is ignored for archives" 1>&2 + fi + + if test -n "$xrpath"; then + $echo "$modename: warning: \`-R' is ignored for archives" 1>&2 + fi + + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info/-version-number' is ignored for archives" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for archives" 1>&2 + fi + + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + $echo "$modename: warning: \`-export-symbols' is ignored for archives" 1>&2 + fi + + # Now set the variables for building old libraries. + build_libtool_libs=no + oldlibs="$output" + objs="$objs$old_deplibs" + ;; + + lib) + # Make sure we only generate libraries of the form `libNAME.la'. + case $outputname in + lib*) + name=`$echo "X$outputname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` + eval shared_ext=\"$shrext\" + eval libname=\"$libname_spec\" + ;; + *) + if test "$module" = no; then + $echo "$modename: libtool library \`$output' must begin with \`lib'" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + if test "$need_lib_prefix" != no; then + # Add the "lib" prefix for modules if required + name=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` + eval shared_ext=\"$shrext\" + eval libname=\"$libname_spec\" + else + libname=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` + fi + ;; + esac + + if test -n "$objs"; then + if test "$deplibs_check_method" != pass_all; then + $echo "$modename: cannot build libtool library \`$output' from non-libtool objects on this host:$objs" 2>&1 + exit 1 + else + $echo + $echo "*** Warning: Linking the shared library $output against the non-libtool" + $echo "*** objects $objs is not portable!" + libobjs="$libobjs $objs" + fi + fi + + if test "$dlself" != no; then + $echo "$modename: warning: \`-dlopen self' is ignored for libtool libraries" 1>&2 + fi + + set dummy $rpath + if test "$#" -gt 2; then + $echo "$modename: warning: ignoring multiple \`-rpath's for a libtool library" 1>&2 + fi + install_libdir="$2" + + oldlibs= + if test -z "$rpath"; then + if test "$build_libtool_libs" = yes; then + # Building a libtool convenience library. + # Some compilers have problems with a `.al' extension so + # convenience libraries should have the same extension an + # archive normally would. + oldlibs="$output_objdir/$libname.$libext $oldlibs" + build_libtool_libs=convenience + build_old_libs=yes + fi + + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info/-version-number' is ignored for convenience libraries" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for convenience libraries" 1>&2 + fi + else + + # Parse the version information argument. + save_ifs="$IFS"; IFS=':' + set dummy $vinfo 0 0 0 + IFS="$save_ifs" + + if test -n "$8"; then + $echo "$modename: too many parameters to \`-version-info'" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + # convert absolute version numbers to libtool ages + # this retains compatibility with .la files and attempts + # to make the code below a bit more comprehensible + + case $vinfo_number in + yes) + number_major="$2" + number_minor="$3" + number_revision="$4" + # + # There are really only two kinds -- those that + # use the current revision as the major version + # and those that subtract age and use age as + # a minor version. But, then there is irix + # which has an extra 1 added just for fun + # + case $version_type in + darwin|linux|osf|windows) + current=`expr $number_major + $number_minor` + age="$number_minor" + revision="$number_revision" + ;; + freebsd-aout|freebsd-elf|sunos) + current="$number_major" + revision="$number_minor" + age="0" + ;; + irix|nonstopux) + current=`expr $number_major + $number_minor - 1` + age="$number_minor" + revision="$number_minor" + ;; + esac + ;; + no) + current="$2" + revision="$3" + age="$4" + ;; + esac + + # Check that each of the things are valid numbers. + case $current in + 0 | [1-9] | [1-9][0-9] | [1-9][0-9][0-9]) ;; + *) + $echo "$modename: CURRENT \`$current' is not a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit 1 + ;; + esac + + case $revision in + 0 | [1-9] | [1-9][0-9] | [1-9][0-9][0-9]) ;; + *) + $echo "$modename: REVISION \`$revision' is not a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit 1 + ;; + esac + + case $age in + 0 | [1-9] | [1-9][0-9] | [1-9][0-9][0-9]) ;; + *) + $echo "$modename: AGE \`$age' is not a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit 1 + ;; + esac + + if test "$age" -gt "$current"; then + $echo "$modename: AGE \`$age' is greater than the current interface number \`$current'" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit 1 + fi + + # Calculate the version variables. + major= + versuffix= + verstring= + case $version_type in + none) ;; + + darwin) + # Like Linux, but with the current version available in + # verstring for coding it into the library header + major=.`expr $current - $age` + versuffix="$major.$age.$revision" + # Darwin ld doesn't like 0 for these options... + minor_current=`expr $current + 1` + verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" + ;; + + freebsd-aout) + major=".$current" + versuffix=".$current.$revision"; + ;; + + freebsd-elf) + major=".$current" + versuffix=".$current"; + ;; + + irix | nonstopux) + major=`expr $current - $age + 1` + + case $version_type in + nonstopux) verstring_prefix=nonstopux ;; + *) verstring_prefix=sgi ;; + esac + verstring="$verstring_prefix$major.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$revision + while test "$loop" -ne 0; do + iface=`expr $revision - $loop` + loop=`expr $loop - 1` + verstring="$verstring_prefix$major.$iface:$verstring" + done + + # Before this point, $major must not contain `.'. + major=.$major + versuffix="$major.$revision" + ;; + + linux) + major=.`expr $current - $age` + versuffix="$major.$age.$revision" + ;; + + osf) + major=.`expr $current - $age` + versuffix=".$current.$age.$revision" + verstring="$current.$age.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$age + while test "$loop" -ne 0; do + iface=`expr $current - $loop` + loop=`expr $loop - 1` + verstring="$verstring:${iface}.0" + done + + # Make executables depend on our current version. + verstring="$verstring:${current}.0" + ;; + + sunos) + major=".$current" + versuffix=".$current.$revision" + ;; + + windows) + # Use '-' rather than '.', since we only want one + # extension on DOS 8.3 filesystems. + major=`expr $current - $age` + versuffix="-$major" + ;; + + *) + $echo "$modename: unknown library version type \`$version_type'" 1>&2 + $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 + exit 1 + ;; + esac + + # Clear the version info if we defaulted, and they specified a release. + if test -z "$vinfo" && test -n "$release"; then + major= + case $version_type in + darwin) + # we can't check for "0.0" in archive_cmds due to quoting + # problems, so we reset it completely + verstring= + ;; + *) + verstring="0.0" + ;; + esac + if test "$need_version" = no; then + versuffix= + else + versuffix=".0.0" + fi + fi + + # Remove version info from name if versioning should be avoided + if test "$avoid_version" = yes && test "$need_version" = no; then + major= + versuffix= + verstring="" + fi + + # Check to see if the archive will have undefined symbols. + if test "$allow_undefined" = yes; then + if test "$allow_undefined_flag" = unsupported; then + $echo "$modename: warning: undefined symbols not allowed in $host shared libraries" 1>&2 + build_libtool_libs=no + build_old_libs=yes + fi + else + # Don't allow undefined symbols. + allow_undefined_flag="$no_undefined_flag" + fi + fi + + if test "$mode" != relink; then + # Remove our outputs, but don't remove object files since they + # may have been created when compiling PIC objects. + removelist= + tempremovelist=`$echo "$output_objdir/*"` + for p in $tempremovelist; do + case $p in + *.$objext) + ;; + $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) + if echo $p | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 + then + continue + fi + removelist="$removelist $p" + ;; + *) ;; + esac + done + if test -n "$removelist"; then + $show "${rm}r $removelist" + $run ${rm}r $removelist + fi + fi + + # Now set the variables for building old libraries. + if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then + oldlibs="$oldlibs $output_objdir/$libname.$libext" + + # Transform .lo files to .o files. + oldobjs="$objs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP` + fi + + # Eliminate all temporary directories. + for path in $notinst_path; do + lib_search_path=`$echo "$lib_search_path " | ${SED} -e 's% $path % %g'` + deplibs=`$echo "$deplibs " | ${SED} -e 's% -L$path % %g'` + dependency_libs=`$echo "$dependency_libs " | ${SED} -e 's% -L$path % %g'` + done + + if test -n "$xrpath"; then + # If the user specified any rpath flags, then add them. + temp_xrpath= + for libdir in $xrpath; do + temp_xrpath="$temp_xrpath -R$libdir" + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" ;; + esac + done + if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then + dependency_libs="$temp_xrpath $dependency_libs" + fi + fi + + # Make sure dlfiles contains only unique files that won't be dlpreopened + old_dlfiles="$dlfiles" + dlfiles= + for lib in $old_dlfiles; do + case " $dlprefiles $dlfiles " in + *" $lib "*) ;; + *) dlfiles="$dlfiles $lib" ;; + esac + done + + # Make sure dlprefiles contains only unique files + old_dlprefiles="$dlprefiles" + dlprefiles= + for lib in $old_dlprefiles; do + case "$dlprefiles " in + *" $lib "*) ;; + *) dlprefiles="$dlprefiles $lib" ;; + esac + done + + if test "$build_libtool_libs" = yes; then + if test -n "$rpath"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos*) + # these systems don't actually have a c library (as such)! + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C library is in the System framework + deplibs="$deplibs -framework System" + ;; + *-*-netbsd*) + # Don't link with libc until the a.out ld.so is fixed. + ;; + *-*-openbsd* | *-*-freebsd*) + # Do not include libc due to us having libc/libc_r. + test "X$arg" = "X-lc" && continue + ;; + *) + # Add libc to deplibs on all other systems if necessary. + if test "$build_libtool_need_lc" = "yes"; then + deplibs="$deplibs -lc" + fi + ;; + esac + fi + + # Transform deplibs into only deplibs that can be linked in shared. + name_save=$name + libname_save=$libname + release_save=$release + versuffix_save=$versuffix + major_save=$major + # I'm not sure if I'm treating the release correctly. I think + # release should show up in the -l (ie -lgmp5) so we don't want to + # add it in twice. Is that correct? + release="" + versuffix="" + major="" + newdeplibs= + droppeddeps=no + case $deplibs_check_method in + pass_all) + # Don't check for shared/static. Everything works. + # This might be a little naive. We might want to check + # whether the library exists or not. But this is on + # osf3 & osf4 and I'm not really sure... Just + # implementing what was already the behavior. + newdeplibs=$deplibs + ;; + test_compile) + # This code stresses the "libraries are programs" paradigm to its + # limits. Maybe even breaks it. We compile a program, linking it + # against the deplibs as a proxy for the library. Then we can check + # whether they linked in statically or dynamically with ldd. + $rm conftest.c + cat > conftest.c </dev/null` + for potent_lib in $potential_libs; do + # Follow soft links. + if ls -lLd "$potent_lib" 2>/dev/null \ + | grep " -> " >/dev/null; then + continue + fi + # The statement above tries to avoid entering an + # endless loop below, in case of cyclic links. + # We might still enter an endless loop, since a link + # loop can be closed while we follow links, + # but so what? + potlib="$potent_lib" + while test -h "$potlib" 2>/dev/null; do + potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` + case $potliblink in + [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; + *) potlib=`$echo "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";; + esac + done + if eval $file_magic_cmd \"\$potlib\" 2>/dev/null \ + | ${SED} 10q \ + | $EGREP "$file_magic_regex" > /dev/null; then + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + $echo + $echo "*** Warning: linker path does not have real file for library $a_deplib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have" + $echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $echo "*** with $libname but no candidates were found. (...for file magic test)" + else + $echo "*** with $libname and none of the candidates passed a file format test" + $echo "*** using a file magic. Last file checked: $potlib" + fi + fi + else + # Add a -L argument. + newdeplibs="$newdeplibs $a_deplib" + fi + done # Gone through all deplibs. + ;; + match_pattern*) + set dummy $deplibs_check_method + match_pattern_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"` + for a_deplib in $deplibs; do + name="`expr $a_deplib : '-l\(.*\)'`" + # If $name is empty we are operating on a -L argument. + if test -n "$name" && test "$name" != "0"; then + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $a_deplib "*) + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + ;; + esac + fi + if test -n "$a_deplib" ; then + libname=`eval \\$echo \"$libname_spec\"` + for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do + potential_libs=`ls $i/$libname[.-]* 2>/dev/null` + for potent_lib in $potential_libs; do + potlib="$potent_lib" # see symlink-check above in file_magic test + if eval $echo \"$potent_lib\" 2>/dev/null \ + | ${SED} 10q \ + | $EGREP "$match_pattern_regex" > /dev/null; then + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + $echo + $echo "*** Warning: linker path does not have real file for library $a_deplib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have" + $echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $echo "*** with $libname but no candidates were found. (...for regex pattern test)" + else + $echo "*** with $libname and none of the candidates passed a file format test" + $echo "*** using a regex pattern. Last file checked: $potlib" + fi + fi + else + # Add a -L argument. + newdeplibs="$newdeplibs $a_deplib" + fi + done # Gone through all deplibs. + ;; + none | unknown | *) + newdeplibs="" + tmp_deplibs=`$echo "X $deplibs" | $Xsed -e 's/ -lc$//' \ + -e 's/ -[LR][^ ]*//g'` + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + for i in $predeps $postdeps ; do + # can't use Xsed below, because $i might contain '/' + tmp_deplibs=`$echo "X $tmp_deplibs" | ${SED} -e "1s,^X,," -e "s,$i,,"` + done + fi + if $echo "X $tmp_deplibs" | $Xsed -e 's/[ ]//g' \ + | grep . >/dev/null; then + $echo + if test "X$deplibs_check_method" = "Xnone"; then + $echo "*** Warning: inter-library dependencies are not supported in this platform." + else + $echo "*** Warning: inter-library dependencies are not known to be supported." + fi + $echo "*** All declared inter-library dependencies are being dropped." + droppeddeps=yes + fi + ;; + esac + versuffix=$versuffix_save + major=$major_save + release=$release_save + libname=$libname_save + name=$name_save + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + newdeplibs=`$echo "X $newdeplibs" | $Xsed -e 's/ -lc / -framework System /'` + ;; + esac + + if test "$droppeddeps" = yes; then + if test "$module" = yes; then + $echo + $echo "*** Warning: libtool could not satisfy all declared inter-library" + $echo "*** dependencies of module $libname. Therefore, libtool will create" + $echo "*** a static module, that should work as long as the dlopening" + $echo "*** application is linked with the -dlopen flag." + if test -z "$global_symbol_pipe"; then + $echo + $echo "*** However, this would only work if libtool was able to extract symbol" + $echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + $echo "*** not find such a program. So, this module is probably useless." + $echo "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + else + $echo "*** The inter-library dependencies that have been dropped here will be" + $echo "*** automatically added whenever a program is linked with this library" + $echo "*** or is declared to -dlopen it." + + if test "$allow_undefined" = no; then + $echo + $echo "*** Since this library must not contain undefined symbols," + $echo "*** because either the platform does not support them or" + $echo "*** it was explicitly requested with -no-undefined," + $echo "*** libtool will only create a static version of it." + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + fi + fi + # Done checking deplibs! + deplibs=$newdeplibs + fi + + # All the library-specific variables (install_libdir is set above). + library_names= + old_library= + dlname= + + # Test again, we may have decided not to build it any more + if test "$build_libtool_libs" = yes; then + if test "$hardcode_into_libs" = yes; then + # Hardcode the library paths + hardcode_libdirs= + dep_rpath= + rpath="$finalize_rpath" + test "$mode" != relink && rpath="$compile_rpath$rpath" + for libdir in $rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + dep_rpath="$dep_rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath="$perm_rpath $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + if test -n "$hardcode_libdir_flag_spec_ld"; then + eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\" + else + eval dep_rpath=\"$hardcode_libdir_flag_spec\" + fi + fi + if test -n "$runpath_var" && test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + rpath="$rpath$dir:" + done + eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" + fi + test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" + fi + + shlibpath="$finalize_shlibpath" + test "$mode" != relink && shlibpath="$compile_shlibpath$shlibpath" + if test -n "$shlibpath"; then + eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" + fi + + # Get the real and link names of the library. + eval shared_ext=\"$shrext\" + eval library_names=\"$library_names_spec\" + set dummy $library_names + realname="$2" + shift; shift + + if test -n "$soname_spec"; then + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + if test -z "$dlname"; then + dlname=$soname + fi + + lib="$output_objdir/$realname" + for link + do + linknames="$linknames $link" + done + + # Use standard objects if they are pic + test -z "$pic_flag" && libobjs=`$echo "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then + $show "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $run $rm $export_symbols + cmds=$export_symbols_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + if len=`expr "X$cmd" : ".*"` && + test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then + $show "$cmd" + $run eval "$cmd" || exit $? + skipped_export=false + else + # The command line is too long to execute in one step. + $show "using reloadable object file for export list..." + skipped_export=: + fi + done + IFS="$save_ifs" + if test -n "$export_symbols_regex"; then + $show "$EGREP -e \"$export_symbols_regex\" \"$export_symbols\" > \"${export_symbols}T\"" + $run eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + $show "$mv \"${export_symbols}T\" \"$export_symbols\"" + $run eval '$mv "${export_symbols}T" "$export_symbols"' + fi + fi + fi + + if test -n "$export_symbols" && test -n "$include_expsyms"; then + $run eval '$echo "X$include_expsyms" | $SP2NL >> "$export_symbols"' + fi + + tmp_deplibs= + for test_deplib in $deplibs; do + case " $convenience " in + *" $test_deplib "*) ;; + *) + tmp_deplibs="$tmp_deplibs $test_deplib" + ;; + esac + done + deplibs="$tmp_deplibs" + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + else + gentop="$output_objdir/${outputname}x" + $show "${rm}r $gentop" + $run ${rm}r "$gentop" + $show "$mkdir $gentop" + $run $mkdir "$gentop" + status=$? + if test "$status" -ne 0 && test ! -d "$gentop"; then + exit $status + fi + generated="$generated $gentop" + + for xlib in $convenience; do + # Extract the objects. + case $xlib in + [\\/]* | [A-Za-z]:[\\/]*) xabs="$xlib" ;; + *) xabs=`pwd`"/$xlib" ;; + esac + xlib=`$echo "X$xlib" | $Xsed -e 's%^.*/%%'` + xdir="$gentop/$xlib" + + $show "${rm}r $xdir" + $run ${rm}r "$xdir" + $show "$mkdir $xdir" + $run $mkdir "$xdir" + status=$? + if test "$status" -ne 0 && test ! -d "$xdir"; then + exit $status + fi + # We will extract separately just the conflicting names and we will no + # longer touch any unique names. It is faster to leave these extract + # automatically by $AR in one run. + $show "(cd $xdir && $AR x $xabs)" + $run eval "(cd \$xdir && $AR x \$xabs)" || exit $? + if ($AR t "$xabs" | sort | sort -uc >/dev/null 2>&1); then + : + else + $echo "$modename: warning: object name conflicts; renaming object files" 1>&2 + $echo "$modename: warning: to ensure that they will not overwrite" 1>&2 + $AR t "$xabs" | sort | uniq -cd | while read -r count name + do + i=1 + while test "$i" -le "$count" + do + # Put our $i before any first dot (extension) + # Never overwrite any file + name_to="$name" + while test "X$name_to" = "X$name" || test -f "$xdir/$name_to" + do + name_to=`$echo "X$name_to" | $Xsed -e "s/\([^.]*\)/\1-$i/"` + done + $show "(cd $xdir && $AR xN $i $xabs '$name' && $mv '$name' '$name_to')" + $run eval "(cd \$xdir && $AR xN $i \$xabs '$name' && $mv '$name' '$name_to')" || exit $? + i=`expr $i + 1` + done + done + fi + + libobjs="$libobjs "`find $xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP` + done + fi + fi + + if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then + eval flag=\"$thread_safe_flag_spec\" + linker_flags="$linker_flags $flag" + fi + + # Make a backup of the uninstalled library when relinking + if test "$mode" = relink; then + $run eval '(cd $output_objdir && $rm ${realname}U && $mv $realname ${realname}U)' || exit $? + fi + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + eval test_cmds=\"$module_expsym_cmds\" + cmds=$module_expsym_cmds + else + eval test_cmds=\"$module_cmds\" + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + eval test_cmds=\"$archive_expsym_cmds\" + cmds=$archive_expsym_cmds + else + eval test_cmds=\"$archive_cmds\" + cmds=$archive_cmds + fi + fi + + if test "X$skipped_export" != "X:" && len=`expr "X$test_cmds" : ".*"` && + test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then + : + else + # The command line is too long to link in one step, link piecewise. + $echo "creating reloadable object files..." + + # Save the value of $output and $libobjs because we want to + # use them later. If we have whole_archive_flag_spec, we + # want to use save_libobjs as it was before + # whole_archive_flag_spec was expanded, because we can't + # assume the linker understands whole_archive_flag_spec. + # This may have to be revisited, in case too many + # convenience libraries get linked in and end up exceeding + # the spec. + if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + fi + save_output=$output + + # Clear the reloadable object creation command queue and + # initialize k to one. + test_cmds= + concat_cmds= + objlist= + delfiles= + last_robj= + k=1 + output=$output_objdir/$save_output-${k}.$objext + # Loop over the list of objects to be linked. + for obj in $save_libobjs + do + eval test_cmds=\"$reload_cmds $objlist $last_robj\" + if test "X$objlist" = X || + { len=`expr "X$test_cmds" : ".*"` && + test "$len" -le "$max_cmd_len"; }; then + objlist="$objlist $obj" + else + # The command $test_cmds is almost too long, add a + # command to the queue. + if test "$k" -eq 1 ; then + # The first file doesn't have a previous command to add. + eval concat_cmds=\"$reload_cmds $objlist $last_robj\" + else + # All subsequent reloadable object files will link in + # the last one created. + eval concat_cmds=\"\$concat_cmds~$reload_cmds $objlist $last_robj\" + fi + last_robj=$output_objdir/$save_output-${k}.$objext + k=`expr $k + 1` + output=$output_objdir/$save_output-${k}.$objext + objlist=$obj + len=1 + fi + done + # Handle the remaining objects by creating one last + # reloadable object file. All subsequent reloadable object + # files will link in the last one created. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$reload_cmds $objlist $last_robj\" + + if ${skipped_export-false}; then + $show "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $run $rm $export_symbols + libobjs=$output + # Append the command to create the export file. + eval concat_cmds=\"\$concat_cmds~$export_symbols_cmds\" + fi + + # Set up a command to remove the reloadale object files + # after they are used. + i=0 + while test "$i" -lt "$k" + do + i=`expr $i + 1` + delfiles="$delfiles $output_objdir/$save_output-${i}.$objext" + done + + $echo "creating a temporary reloadable object file: $output" + + # Loop through the commands generated above and execute them. + save_ifs="$IFS"; IFS='~' + for cmd in $concat_cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + + libobjs=$output + # Restore the value of output. + output=$save_output + + if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + fi + # Expand the library linking commands again to reset the + # value of $libobjs for piecewise linking. + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + cmds=$module_expsym_cmds + else + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + cmds=$archive_expsym_cmds + else + cmds=$archive_cmds + fi + fi + + # Append the command to remove the reloadable object files + # to the just-reset $cmds. + eval cmds=\"\$cmds~\$rm $delfiles\" + fi + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + + # Restore the uninstalled library and exit + if test "$mode" = relink; then + $run eval '(cd $output_objdir && $rm ${realname}T && $mv $realname ${realname}T && $mv "$realname"U $realname)' || exit $? + exit 0 + fi + + # Create links to the real library. + for linkname in $linknames; do + if test "$realname" != "$linkname"; then + $show "(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)" + $run eval '(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)' || exit $? + fi + done + + # If -module or -export-dynamic was specified, set the dlname. + if test "$module" = yes || test "$export_dynamic" = yes; then + # On all known operating systems, these are identical. + dlname="$soname" + fi + fi + ;; + + obj) + if test -n "$deplibs"; then + $echo "$modename: warning: \`-l' and \`-L' are ignored for objects" 1>&2 + fi + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + $echo "$modename: warning: \`-dlopen' is ignored for objects" 1>&2 + fi + + if test -n "$rpath"; then + $echo "$modename: warning: \`-rpath' is ignored for objects" 1>&2 + fi + + if test -n "$xrpath"; then + $echo "$modename: warning: \`-R' is ignored for objects" 1>&2 + fi + + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info' is ignored for objects" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for objects" 1>&2 + fi + + case $output in + *.lo) + if test -n "$objs$old_deplibs"; then + $echo "$modename: cannot build library object \`$output' from non-libtool objects" 1>&2 + exit 1 + fi + libobj="$output" + obj=`$echo "X$output" | $Xsed -e "$lo2o"` + ;; + *) + libobj= + obj="$output" + ;; + esac + + # Delete the old objects. + $run $rm $obj $libobj + + # Objects from convenience libraries. This assumes + # single-version convenience libraries. Whenever we create + # different ones for PIC/non-PIC, this we'll have to duplicate + # the extraction. + reload_conv_objs= + gentop= + # reload_cmds runs $LD directly, so let us get rid of + # -Wl from whole_archive_flag_spec + wl= + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + eval reload_conv_objs=\"\$reload_objs $whole_archive_flag_spec\" + else + gentop="$output_objdir/${obj}x" + $show "${rm}r $gentop" + $run ${rm}r "$gentop" + $show "$mkdir $gentop" + $run $mkdir "$gentop" + status=$? + if test "$status" -ne 0 && test ! -d "$gentop"; then + exit $status + fi + generated="$generated $gentop" + + for xlib in $convenience; do + # Extract the objects. + case $xlib in + [\\/]* | [A-Za-z]:[\\/]*) xabs="$xlib" ;; + *) xabs=`pwd`"/$xlib" ;; + esac + xlib=`$echo "X$xlib" | $Xsed -e 's%^.*/%%'` + xdir="$gentop/$xlib" + + $show "${rm}r $xdir" + $run ${rm}r "$xdir" + $show "$mkdir $xdir" + $run $mkdir "$xdir" + status=$? + if test "$status" -ne 0 && test ! -d "$xdir"; then + exit $status + fi + # We will extract separately just the conflicting names and we will no + # longer touch any unique names. It is faster to leave these extract + # automatically by $AR in one run. + $show "(cd $xdir && $AR x $xabs)" + $run eval "(cd \$xdir && $AR x \$xabs)" || exit $? + if ($AR t "$xabs" | sort | sort -uc >/dev/null 2>&1); then + : + else + $echo "$modename: warning: object name conflicts; renaming object files" 1>&2 + $echo "$modename: warning: to ensure that they will not overwrite" 1>&2 + $AR t "$xabs" | sort | uniq -cd | while read -r count name + do + i=1 + while test "$i" -le "$count" + do + # Put our $i before any first dot (extension) + # Never overwrite any file + name_to="$name" + while test "X$name_to" = "X$name" || test -f "$xdir/$name_to" + do + name_to=`$echo "X$name_to" | $Xsed -e "s/\([^.]*\)/\1-$i/"` + done + $show "(cd $xdir && $AR xN $i $xabs '$name' && $mv '$name' '$name_to')" + $run eval "(cd \$xdir && $AR xN $i \$xabs '$name' && $mv '$name' '$name_to')" || exit $? + i=`expr $i + 1` + done + done + fi + + reload_conv_objs="$reload_objs "`find $xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP` + done + fi + fi + + # Create the old-style object. + reload_objs="$objs$old_deplibs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test + + output="$obj" + cmds=$reload_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + + # Exit if we aren't doing a library object file. + if test -z "$libobj"; then + if test -n "$gentop"; then + $show "${rm}r $gentop" + $run ${rm}r $gentop + fi + + exit 0 + fi + + if test "$build_libtool_libs" != yes; then + if test -n "$gentop"; then + $show "${rm}r $gentop" + $run ${rm}r $gentop + fi + + # Create an invalid libtool object if no PIC, so that we don't + # accidentally link it into a program. + # $show "echo timestamp > $libobj" + # $run eval "echo timestamp > $libobj" || exit $? + exit 0 + fi + + if test -n "$pic_flag" || test "$pic_mode" != default; then + # Only do commands if we really have different PIC objects. + reload_objs="$libobjs $reload_conv_objs" + output="$libobj" + cmds=$reload_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + + if test -n "$gentop"; then + $show "${rm}r $gentop" + $run ${rm}r $gentop + fi + + exit 0 + ;; + + prog) + case $host in + *cygwin*) output=`$echo $output | ${SED} -e 's,.exe$,,;s,$,.exe,'` ;; + esac + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info' is ignored for programs" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for programs" 1>&2 + fi + + if test "$preload" = yes; then + if test "$dlopen_support" = unknown && test "$dlopen_self" = unknown && + test "$dlopen_self_static" = unknown; then + $echo "$modename: warning: \`AC_LIBTOOL_DLOPEN' not used. Assuming no dlopen support." + fi + fi + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + compile_deplibs=`$echo "X $compile_deplibs" | $Xsed -e 's/ -lc / -framework System /'` + finalize_deplibs=`$echo "X $finalize_deplibs" | $Xsed -e 's/ -lc / -framework System /'` + ;; + esac + + case $host in + *darwin*) + # Don't allow lazy linking, it breaks C++ global constructors + if test "$tagname" = CXX ; then + compile_command="$compile_command ${wl}-bind_at_load" + finalize_command="$finalize_command ${wl}-bind_at_load" + fi + ;; + esac + + compile_command="$compile_command $compile_deplibs" + finalize_command="$finalize_command $finalize_deplibs" + + if test -n "$rpath$xrpath"; then + # If the user specified any rpath flags, then add them. + for libdir in $rpath $xrpath; do + # This is the magic to use -rpath. + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" ;; + esac + done + fi + + # Now hardcode the library paths + rpath= + hardcode_libdirs= + for libdir in $compile_rpath $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + rpath="$rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath="$perm_rpath $libdir" ;; + esac + fi + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) + case :$dllsearchpath: in + *":$libdir:"*) ;; + *) dllsearchpath="$dllsearchpath:$libdir";; + esac + ;; + esac + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + compile_rpath="$rpath" + + rpath= + hardcode_libdirs= + for libdir in $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + rpath="$rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$finalize_perm_rpath " in + *" $libdir "*) ;; + *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + finalize_rpath="$rpath" + + if test -n "$libobjs" && test "$build_old_libs" = yes; then + # Transform all the library objects into standard objects. + compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + fi + + dlsyms= + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + if test -n "$NM" && test -n "$global_symbol_pipe"; then + dlsyms="${outputname}S.c" + else + $echo "$modename: not configured to extract global symbols from dlpreopened files" 1>&2 + fi + fi + + if test -n "$dlsyms"; then + case $dlsyms in + "") ;; + *.c) + # Discover the nlist of each of the dlfiles. + nlist="$output_objdir/${outputname}.nm" + + $show "$rm $nlist ${nlist}S ${nlist}T" + $run $rm "$nlist" "${nlist}S" "${nlist}T" + + # Parse the name list into a source file. + $show "creating $output_objdir/$dlsyms" + + test -z "$run" && $echo > "$output_objdir/$dlsyms" "\ +/* $dlsyms - symbol resolution table for \`$outputname' dlsym emulation. */ +/* Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP */ + +#ifdef __cplusplus +extern \"C\" { +#endif + +/* Prevent the only kind of declaration conflicts we can make. */ +#define lt_preloaded_symbols some_other_symbol + +/* External symbol declarations for the compiler. */\ +" + + if test "$dlself" = yes; then + $show "generating symbol list for \`$output'" + + test -z "$run" && $echo ': @PROGRAM@ ' > "$nlist" + + # Add our own program objects to the symbol list. + progfiles=`$echo "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + for arg in $progfiles; do + $show "extracting global C symbols from \`$arg'" + $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" + done + + if test -n "$exclude_expsyms"; then + $run eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' + $run eval '$mv "$nlist"T "$nlist"' + fi + + if test -n "$export_symbols_regex"; then + $run eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' + $run eval '$mv "$nlist"T "$nlist"' + fi + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + export_symbols="$output_objdir/$output.exp" + $run $rm $export_symbols + $run eval "${SED} -n -e '/^: @PROGRAM@$/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' + else + $run eval "${SED} -e 's/\([][.*^$]\)/\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$output.exp"' + $run eval 'grep -f "$output_objdir/$output.exp" < "$nlist" > "$nlist"T' + $run eval 'mv "$nlist"T "$nlist"' + fi + fi + + for arg in $dlprefiles; do + $show "extracting global C symbols from \`$arg'" + name=`$echo "$arg" | ${SED} -e 's%^.*/%%'` + $run eval '$echo ": $name " >> "$nlist"' + $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" + done + + if test -z "$run"; then + # Make sure we have at least an empty file. + test -f "$nlist" || : > "$nlist" + + if test -n "$exclude_expsyms"; then + $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T + $mv "$nlist"T "$nlist" + fi + + # Try sorting and uniquifying the output. + if grep -v "^: " < "$nlist" | + if sort -k 3 /dev/null 2>&1; then + sort -k 3 + else + sort +2 + fi | + uniq > "$nlist"S; then + : + else + grep -v "^: " < "$nlist" > "$nlist"S + fi + + if test -f "$nlist"S; then + eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$dlsyms"' + else + $echo '/* NONE */' >> "$output_objdir/$dlsyms" + fi + + $echo >> "$output_objdir/$dlsyms" "\ + +#undef lt_preloaded_symbols + +#if defined (__STDC__) && __STDC__ +# define lt_ptr void * +#else +# define lt_ptr char * +# define const +#endif + +/* The mapping between symbol names and symbols. */ +const struct { + const char *name; + lt_ptr address; +} +lt_preloaded_symbols[] = +{\ +" + + eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$dlsyms" + + $echo >> "$output_objdir/$dlsyms" "\ + {0, (lt_ptr) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif\ +" + fi + + pic_flag_for_symtable= + case $host in + # compiling the symbol table file with pic_flag works around + # a FreeBSD bug that causes programs to crash when -lm is + # linked before any other PIC object. But we must not use + # pic_flag when linking with -static. The problem exists in + # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. + *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) + case "$compile_command " in + *" -static "*) ;; + *) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND";; + esac;; + *-*-hpux*) + case "$compile_command " in + *" -static "*) ;; + *) pic_flag_for_symtable=" $pic_flag";; + esac + esac + + # Now compile the dynamic symbol file. + $show "(cd $output_objdir && $LTCC -c$no_builtin_flag$pic_flag_for_symtable \"$dlsyms\")" + $run eval '(cd $output_objdir && $LTCC -c$no_builtin_flag$pic_flag_for_symtable "$dlsyms")' || exit $? + + # Clean up the generated files. + $show "$rm $output_objdir/$dlsyms $nlist ${nlist}S ${nlist}T" + $run $rm "$output_objdir/$dlsyms" "$nlist" "${nlist}S" "${nlist}T" + + # Transform the symbol file into the correct name. + compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` + finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` + ;; + *) + $echo "$modename: unknown suffix for \`$dlsyms'" 1>&2 + exit 1 + ;; + esac + else + # We keep going just in case the user didn't refer to + # lt_preloaded_symbols. The linker will fail if global_symbol_pipe + # really was required. + + # Nullify the symbol file. + compile_command=`$echo "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"` + finalize_command=`$echo "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"` + fi + + if test "$need_relink" = no || test "$build_libtool_libs" != yes; then + # Replace the output file specification. + compile_command=`$echo "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` + link_command="$compile_command$compile_rpath" + + # We have no uninstalled library dependencies, so finalize right now. + $show "$link_command" + $run eval "$link_command" + status=$? + + # Delete the generated files. + if test -n "$dlsyms"; then + $show "$rm $output_objdir/${outputname}S.${objext}" + $run $rm "$output_objdir/${outputname}S.${objext}" + fi + + exit $status + fi + + if test -n "$shlibpath_var"; then + # We should set the shlibpath_var + rpath= + for dir in $temp_rpath; do + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) + # Absolute path. + rpath="$rpath$dir:" + ;; + *) + # Relative path: add a thisdir entry. + rpath="$rpath\$thisdir/$dir:" + ;; + esac + done + temp_rpath="$rpath" + fi + + if test -n "$compile_shlibpath$finalize_shlibpath"; then + compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" + fi + if test -n "$finalize_shlibpath"; then + finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" + fi + + compile_var= + finalize_var= + if test -n "$runpath_var"; then + if test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + rpath="$rpath$dir:" + done + compile_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + if test -n "$finalize_perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $finalize_perm_rpath; do + rpath="$rpath$dir:" + done + finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + fi + + if test "$no_install" = yes; then + # We don't need to create a wrapper script. + link_command="$compile_var$compile_command$compile_rpath" + # Replace the output file specification. + link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` + # Delete the old output file. + $run $rm $output + # Link the executable and exit + $show "$link_command" + $run eval "$link_command" || exit $? + exit 0 + fi + + if test "$hardcode_action" = relink; then + # Fast installation is not supported + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + + $echo "$modename: warning: this platform does not like uninstalled shared libraries" 1>&2 + $echo "$modename: \`$output' will be relinked during installation" 1>&2 + else + if test "$fast_install" != no; then + link_command="$finalize_var$compile_command$finalize_rpath" + if test "$fast_install" = yes; then + relink_command=`$echo "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'` + else + # fast_install is set to needless + relink_command= + fi + else + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + fi + fi + + # Replace the output file specification. + link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` + + # Delete the old output files. + $run $rm $output $output_objdir/$outputname $output_objdir/lt-$outputname + + $show "$link_command" + $run eval "$link_command" || exit $? + + # Now create the wrapper script. + $show "creating $output" + + # Quote the relink command for shipping. + if test -n "$relink_command"; then + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"` + relink_command="$var=\"$var_value\"; export $var; $relink_command" + fi + done + relink_command="(cd `pwd`; $relink_command)" + relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"` + fi + + # Quote $echo for shipping. + if test "X$echo" = "X$SHELL $0 --fallback-echo"; then + case $0 in + [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $0 --fallback-echo";; + *) qecho="$SHELL `pwd`/$0 --fallback-echo";; + esac + qecho=`$echo "X$qecho" | $Xsed -e "$sed_quote_subst"` + else + qecho=`$echo "X$echo" | $Xsed -e "$sed_quote_subst"` + fi + + # Only actually do things if our run command is non-null. + if test -z "$run"; then + # win32 will think the script is a binary if it has + # a .exe suffix, so we strip it off here. + case $output in + *.exe) output=`$echo $output|${SED} 's,.exe$,,'` ;; + esac + # test for cygwin because mv fails w/o .exe extensions + case $host in + *cygwin*) + exeext=.exe + outputname=`$echo $outputname|${SED} 's,.exe$,,'` ;; + *) exeext= ;; + esac + case $host in + *cygwin* | *mingw* ) + cwrappersource=`$echo ${objdir}/lt-${output}.c` + cwrapper=`$echo ${output}.exe` + $rm $cwrappersource $cwrapper + trap "$rm $cwrappersource $cwrapper; exit 1" 1 2 15 + + cat > $cwrappersource <> $cwrappersource<<"EOF" +#include +#include +#include +#include +#include +#include + +#if defined(PATH_MAX) +# define LT_PATHMAX PATH_MAX +#elif defined(MAXPATHLEN) +# define LT_PATHMAX MAXPATHLEN +#else +# define LT_PATHMAX 1024 +#endif + +#ifndef DIR_SEPARATOR +#define DIR_SEPARATOR '/' +#endif + +#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ + defined (__OS2__) +#define HAVE_DOS_BASED_FILE_SYSTEM +#ifndef DIR_SEPARATOR_2 +#define DIR_SEPARATOR_2 '\\' +#endif +#endif + +#ifndef DIR_SEPARATOR_2 +# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) +#else /* DIR_SEPARATOR_2 */ +# define IS_DIR_SEPARATOR(ch) \ + (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) +#endif /* DIR_SEPARATOR_2 */ + +#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) +#define XFREE(stale) do { \ + if (stale) { free ((void *) stale); stale = 0; } \ +} while (0) + +const char *program_name = NULL; + +void * xmalloc (size_t num); +char * xstrdup (const char *string); +char * basename (const char *name); +char * fnqualify(const char *path); +char * strendzap(char *str, const char *pat); +void lt_fatal (const char *message, ...); + +int +main (int argc, char *argv[]) +{ + char **newargz; + int i; + + program_name = (char *) xstrdup ((char *) basename (argv[0])); + newargz = XMALLOC(char *, argc+2); +EOF + + cat >> $cwrappersource <> $cwrappersource <<"EOF" + newargz[1] = fnqualify(argv[0]); + /* we know the script has the same name, without the .exe */ + /* so make sure newargz[1] doesn't end in .exe */ + strendzap(newargz[1],".exe"); + for (i = 1; i < argc; i++) + newargz[i+1] = xstrdup(argv[i]); + newargz[argc+1] = NULL; +EOF + + cat >> $cwrappersource <> $cwrappersource <<"EOF" +} + +void * +xmalloc (size_t num) +{ + void * p = (void *) malloc (num); + if (!p) + lt_fatal ("Memory exhausted"); + + return p; +} + +char * +xstrdup (const char *string) +{ + return string ? strcpy ((char *) xmalloc (strlen (string) + 1), string) : NULL +; +} + +char * +basename (const char *name) +{ + const char *base; + +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + /* Skip over the disk name in MSDOS pathnames. */ + if (isalpha (name[0]) && name[1] == ':') + name += 2; +#endif + + for (base = name; *name; name++) + if (IS_DIR_SEPARATOR (*name)) + base = name + 1; + return (char *) base; +} + +char * +fnqualify(const char *path) +{ + size_t size; + char *p; + char tmp[LT_PATHMAX + 1]; + + assert(path != NULL); + + /* Is it qualified already? */ +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + if (isalpha (path[0]) && path[1] == ':') + return xstrdup (path); +#endif + if (IS_DIR_SEPARATOR (path[0])) + return xstrdup (path); + + /* prepend the current directory */ + /* doesn't handle '~' */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal ("getcwd failed"); + size = strlen(tmp) + 1 + strlen(path) + 1; /* +2 for '/' and '\0' */ + p = XMALLOC(char, size); + sprintf(p, "%s%c%s", tmp, DIR_SEPARATOR, path); + return p; +} + +char * +strendzap(char *str, const char *pat) +{ + size_t len, patlen; + + assert(str != NULL); + assert(pat != NULL); + + len = strlen(str); + patlen = strlen(pat); + + if (patlen <= len) + { + str += len - patlen; + if (strcmp(str, pat) == 0) + *str = '\0'; + } + return str; +} + +static void +lt_error_core (int exit_status, const char * mode, + const char * message, va_list ap) +{ + fprintf (stderr, "%s: %s: ", program_name, mode); + vfprintf (stderr, message, ap); + fprintf (stderr, ".\n"); + + if (exit_status >= 0) + exit (exit_status); +} + +void +lt_fatal (const char *message, ...) +{ + va_list ap; + va_start (ap, message); + lt_error_core (EXIT_FAILURE, "FATAL", message, ap); + va_end (ap); +} +EOF + # we should really use a build-platform specific compiler + # here, but OTOH, the wrappers (shell script and this C one) + # are only useful if you want to execute the "real" binary. + # Since the "real" binary is built for $host, then this + # wrapper might as well be built for $host, too. + $run $LTCC -s -o $cwrapper $cwrappersource + ;; + esac + $rm $output + trap "$rm $output; exit 1" 1 2 15 + + $echo > $output "\ +#! $SHELL + +# $output - temporary wrapper script for $objdir/$outputname +# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP +# +# The $output program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed='${SED} -e 1s/^X//' +sed_quote_subst='$sed_quote_subst' + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +if test \"\${CDPATH+set}\" = set; then CDPATH=:; export CDPATH; fi + +relink_command=\"$relink_command\" + +# This environment variable determines our operation mode. +if test \"\$libtool_install_magic\" = \"$magic\"; then + # install mode needs the following variable: + notinst_deplibs='$notinst_deplibs' +else + # When we are sourced in execute mode, \$file and \$echo are already set. + if test \"\$libtool_execute_magic\" != \"$magic\"; then + echo=\"$qecho\" + file=\"\$0\" + # Make sure echo works. + if test \"X\$1\" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift + elif test \"X\`(\$echo '\t') 2>/dev/null\`\" = 'X\t'; then + # Yippee, \$echo works! + : + else + # Restart under the correct shell, and then maybe \$echo will work. + exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"} + fi + fi\ +" + $echo >> $output "\ + + # Find the directory that this script lives in. + thisdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\` + test \"x\$thisdir\" = \"x\$file\" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=\`ls -ld \"\$file\" | ${SED} -n 's/.*-> //p'\` + while test -n \"\$file\"; do + destdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\` + + # If there was a directory component, then change thisdir. + if test \"x\$destdir\" != \"x\$file\"; then + case \"\$destdir\" in + [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; + *) thisdir=\"\$thisdir/\$destdir\" ;; + esac + fi + + file=\`\$echo \"X\$file\" | \$Xsed -e 's%^.*/%%'\` + file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\` + done + + # Try to get the absolute directory name. + absdir=\`cd \"\$thisdir\" && pwd\` + test -n \"\$absdir\" && thisdir=\"\$absdir\" +" + + if test "$fast_install" = yes; then + $echo >> $output "\ + program=lt-'$outputname'$exeext + progdir=\"\$thisdir/$objdir\" + + if test ! -f \"\$progdir/\$program\" || \\ + { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ + test \"X\$file\" != \"X\$progdir/\$program\"; }; then + + file=\"\$\$-\$program\" + + if test ! -d \"\$progdir\"; then + $mkdir \"\$progdir\" + else + $rm \"\$progdir/\$file\" + fi" + + $echo >> $output "\ + + # relink executable if necessary + if test -n \"\$relink_command\"; then + if relink_command_output=\`eval \$relink_command 2>&1\`; then : + else + $echo \"\$relink_command_output\" >&2 + $rm \"\$progdir/\$file\" + exit 1 + fi + fi + + $mv \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || + { $rm \"\$progdir/\$program\"; + $mv \"\$progdir/\$file\" \"\$progdir/\$program\"; } + $rm \"\$progdir/\$file\" + fi" + else + $echo >> $output "\ + program='$outputname' + progdir=\"\$thisdir/$objdir\" +" + fi + + $echo >> $output "\ + + if test -f \"\$progdir/\$program\"; then" + + # Export our shlibpath_var if we have one. + if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + $echo >> $output "\ + # Add our own library path to $shlibpath_var + $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" + + # Some systems cannot cope with colon-terminated $shlibpath_var + # The second colon is a workaround for a bug in BeOS R4 sed + $shlibpath_var=\`\$echo \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\` + + export $shlibpath_var +" + fi + + # fixup the dll searchpath if we need to. + if test -n "$dllsearchpath"; then + $echo >> $output "\ + # Add the dll search path components to the executable PATH + PATH=$dllsearchpath:\$PATH +" + fi + + $echo >> $output "\ + if test \"\$libtool_execute_magic\" != \"$magic\"; then + # Run the actual program with our arguments. +" + case $host in + # Backslashes separate directories on plain windows + *-*-mingw | *-*-os2*) + $echo >> $output "\ + exec \$progdir\\\\\$program \${1+\"\$@\"} +" + ;; + + *) + $echo >> $output "\ + exec \$progdir/\$program \${1+\"\$@\"} +" + ;; + esac + $echo >> $output "\ + \$echo \"\$0: cannot exec \$program \${1+\"\$@\"}\" + exit 1 + fi + else + # The program doesn't exist. + \$echo \"\$0: error: \$progdir/\$program does not exist\" 1>&2 + \$echo \"This script is just a wrapper for \$program.\" 1>&2 + $echo \"See the $PACKAGE documentation for more information.\" 1>&2 + exit 1 + fi +fi\ +" + chmod +x $output + fi + exit 0 + ;; + esac + + # See if we need to build an old-fashioned archive. + for oldlib in $oldlibs; do + + if test "$build_libtool_libs" = convenience; then + oldobjs="$libobjs_save" + addlibs="$convenience" + build_libtool_libs=no + else + if test "$build_libtool_libs" = module; then + oldobjs="$libobjs_save" + build_libtool_libs=no + else + oldobjs="$old_deplibs $non_pic_objects" + fi + addlibs="$old_convenience" + fi + + if test -n "$addlibs"; then + gentop="$output_objdir/${outputname}x" + $show "${rm}r $gentop" + $run ${rm}r "$gentop" + $show "$mkdir $gentop" + $run $mkdir "$gentop" + status=$? + if test "$status" -ne 0 && test ! -d "$gentop"; then + exit $status + fi + generated="$generated $gentop" + + # Add in members from convenience archives. + for xlib in $addlibs; do + # Extract the objects. + case $xlib in + [\\/]* | [A-Za-z]:[\\/]*) xabs="$xlib" ;; + *) xabs=`pwd`"/$xlib" ;; + esac + xlib=`$echo "X$xlib" | $Xsed -e 's%^.*/%%'` + xdir="$gentop/$xlib" + + $show "${rm}r $xdir" + $run ${rm}r "$xdir" + $show "$mkdir $xdir" + $run $mkdir "$xdir" + status=$? + if test "$status" -ne 0 && test ! -d "$xdir"; then + exit $status + fi + # We will extract separately just the conflicting names and we will no + # longer touch any unique names. It is faster to leave these extract + # automatically by $AR in one run. + $show "(cd $xdir && $AR x $xabs)" + $run eval "(cd \$xdir && $AR x \$xabs)" || exit $? + if ($AR t "$xabs" | sort | sort -uc >/dev/null 2>&1); then + : + else + $echo "$modename: warning: object name conflicts; renaming object files" 1>&2 + $echo "$modename: warning: to ensure that they will not overwrite" 1>&2 + $AR t "$xabs" | sort | uniq -cd | while read -r count name + do + i=1 + while test "$i" -le "$count" + do + # Put our $i before any first dot (extension) + # Never overwrite any file + name_to="$name" + while test "X$name_to" = "X$name" || test -f "$xdir/$name_to" + do + name_to=`$echo "X$name_to" | $Xsed -e "s/\([^.]*\)/\1-$i/"` + done + $show "(cd $xdir && $AR xN $i $xabs '$name' && $mv '$name' '$name_to')" + $run eval "(cd \$xdir && $AR xN $i \$xabs '$name' && $mv '$name' '$name_to')" || exit $? + i=`expr $i + 1` + done + done + fi + + oldobjs="$oldobjs "`find $xdir -name \*.${objext} -print -o -name \*.lo -print | $NL2SP` + done + fi + + # Do each command in the archive commands. + if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then + cmds=$old_archive_from_new_cmds + else + eval cmds=\"$old_archive_cmds\" + + if len=`expr "X$cmds" : ".*"` && + test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then + cmds=$old_archive_cmds + else + # the command line is too long to link in one step, link in parts + $echo "using piecewise archive linking..." + save_RANLIB=$RANLIB + RANLIB=: + objlist= + concat_cmds= + save_oldobjs=$oldobjs + # GNU ar 2.10+ was changed to match POSIX; thus no paths are + # encoded into archives. This makes 'ar r' malfunction in + # this piecewise linking case whenever conflicting object + # names appear in distinct ar calls; check, warn and compensate. + if (for obj in $save_oldobjs + do + $echo "X$obj" | $Xsed -e 's%^.*/%%' + done | sort | sort -uc >/dev/null 2>&1); then + : + else + $echo "$modename: warning: object name conflicts; overriding AR_FLAGS to 'cq'" 1>&2 + $echo "$modename: warning: to ensure that POSIX-compatible ar will work" 1>&2 + AR_FLAGS=cq + fi + # Is there a better way of finding the last object in the list? + for obj in $save_oldobjs + do + last_oldobj=$obj + done + for obj in $save_oldobjs + do + oldobjs="$objlist $obj" + objlist="$objlist $obj" + eval test_cmds=\"$old_archive_cmds\" + if len=`expr "X$test_cmds" : ".*"` && + test "$len" -le "$max_cmd_len"; then + : + else + # the above command should be used before it gets too long + oldobjs=$objlist + if test "$obj" = "$last_oldobj" ; then + RANLIB=$save_RANLIB + fi + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" + objlist= + fi + done + RANLIB=$save_RANLIB + oldobjs=$objlist + if test "X$oldobjs" = "X" ; then + eval cmds=\"\$concat_cmds\" + else + eval cmds=\"\$concat_cmds~\$old_archive_cmds\" + fi + fi + fi + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + eval cmd=\"$cmd\" + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + done + + if test -n "$generated"; then + $show "${rm}r$generated" + $run ${rm}r$generated + fi + + # Now create the libtool archive. + case $output in + *.la) + old_library= + test "$build_old_libs" = yes && old_library="$libname.$libext" + $show "creating $output" + + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"` + relink_command="$var=\"$var_value\"; export $var; $relink_command" + fi + done + # Quote the link command for shipping. + relink_command="(cd `pwd`; $SHELL $0 $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" + relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"` + if test "$hardcode_automatic" = yes ; then + relink_command= + fi + # Only create the output if not a dry run. + if test -z "$run"; then + for installed in no yes; do + if test "$installed" = yes; then + if test -z "$install_libdir"; then + break + fi + output="$output_objdir/$outputname"i + # Replace all uninstalled libtool libraries with the installed ones + newdependency_libs= + for deplib in $dependency_libs; do + case $deplib in + *.la) + name=`$echo "X$deplib" | $Xsed -e 's%^.*/%%'` + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + if test -z "$libdir"; then + $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2 + exit 1 + fi + newdependency_libs="$newdependency_libs $libdir/$name" + ;; + *) newdependency_libs="$newdependency_libs $deplib" ;; + esac + done + dependency_libs="$newdependency_libs" + newdlfiles= + for lib in $dlfiles; do + name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + if test -z "$libdir"; then + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + exit 1 + fi + newdlfiles="$newdlfiles $libdir/$name" + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + if test -z "$libdir"; then + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + exit 1 + fi + newdlprefiles="$newdlprefiles $libdir/$name" + done + dlprefiles="$newdlprefiles" + else + newdlfiles= + for lib in $dlfiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + newdlfiles="$newdlfiles $abs" + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + newdlprefiles="$newdlprefiles $abs" + done + dlprefiles="$newdlprefiles" + fi + $rm $output + # place dlname in correct position for cygwin + tdlname=$dlname + case $host,$output,$installed,$module,$dlname in + *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;; + esac + $echo > $output "\ +# $outputname - a libtool library file +# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='$tdlname' + +# Names of this library. +library_names='$library_names' + +# The name of the static archive. +old_library='$old_library' + +# Libraries that this one depends upon. +dependency_libs='$dependency_libs' + +# Version information for $libname. +current=$current +age=$age +revision=$revision + +# Is this an already installed library? +installed=$installed + +# Should we warn about portability when linking against -modules? +shouldnotlink=$module + +# Files to dlopen/dlpreopen +dlopen='$dlfiles' +dlpreopen='$dlprefiles' + +# Directory that this library needs to be installed in: +libdir='$install_libdir'" + if test "$installed" = no && test "$need_relink" = yes; then + $echo >> $output "\ +relink_command=\"$relink_command\"" + fi + done + fi + + # Do a symbolic link so that the libtool archive can be found in + # LD_LIBRARY_PATH before the program is installed. + $show "(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)" + $run eval '(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)' || exit $? + ;; + esac + exit 0 + ;; + + # libtool install mode + install) + modename="$modename: install" + + # There may be an optional sh(1) argument at the beginning of + # install_prog (especially on Windows NT). + if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || + # Allow the use of GNU shtool's install command. + $echo "X$nonopt" | $Xsed | grep shtool > /dev/null; then + # Aesthetically quote it. + arg=`$echo "X$nonopt" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + arg="\"$arg\"" + ;; + esac + install_prog="$arg " + arg="$1" + shift + else + install_prog= + arg="$nonopt" + fi + + # The real first argument should be the name of the installation program. + # Aesthetically quote it. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + arg="\"$arg\"" + ;; + esac + install_prog="$install_prog$arg" + + # We need to accept at least all the BSD install flags. + dest= + files= + opts= + prev= + install_type= + isdir=no + stripme= + for arg + do + if test -n "$dest"; then + files="$files $dest" + dest="$arg" + continue + fi + + case $arg in + -d) isdir=yes ;; + -f) prev="-f" ;; + -g) prev="-g" ;; + -m) prev="-m" ;; + -o) prev="-o" ;; + -s) + stripme=" -s" + continue + ;; + -*) ;; + + *) + # If the previous option needed an argument, then skip it. + if test -n "$prev"; then + prev= + else + dest="$arg" + continue + fi + ;; + esac + + # Aesthetically quote the argument. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + arg="\"$arg\"" + ;; + esac + install_prog="$install_prog $arg" + done + + if test -z "$install_prog"; then + $echo "$modename: you must specify an install program" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + if test -n "$prev"; then + $echo "$modename: the \`$prev' option requires an argument" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + if test -z "$files"; then + if test -z "$dest"; then + $echo "$modename: no file or destination specified" 1>&2 + else + $echo "$modename: you must specify a destination" 1>&2 + fi + $echo "$help" 1>&2 + exit 1 + fi + + # Strip any trailing slash from the destination. + dest=`$echo "X$dest" | $Xsed -e 's%/$%%'` + + # Check to see that the destination is a directory. + test -d "$dest" && isdir=yes + if test "$isdir" = yes; then + destdir="$dest" + destname= + else + destdir=`$echo "X$dest" | $Xsed -e 's%/[^/]*$%%'` + test "X$destdir" = "X$dest" && destdir=. + destname=`$echo "X$dest" | $Xsed -e 's%^.*/%%'` + + # Not a directory, so check to see that there is only one file specified. + set dummy $files + if test "$#" -gt 2; then + $echo "$modename: \`$dest' is not a directory" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + fi + case $destdir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + for file in $files; do + case $file in + *.lo) ;; + *) + $echo "$modename: \`$destdir' must be an absolute directory name" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; + esac + done + ;; + esac + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + staticlibs= + future_libdirs= + current_libdirs= + for file in $files; do + + # Do each installation. + case $file in + *.$libext) + # Do the static libraries later. + staticlibs="$staticlibs $file" + ;; + + *.la) + # Check to see that this really is a libtool archive. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + else + $echo "$modename: \`$file' is not a valid libtool archive" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + library_names= + old_library= + relink_command= + # If there is no directory component, then add one. + case $file in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Add the libdir to current_libdirs if it is the destination. + if test "X$destdir" = "X$libdir"; then + case "$current_libdirs " in + *" $libdir "*) ;; + *) current_libdirs="$current_libdirs $libdir" ;; + esac + else + # Note the libdir as a future libdir. + case "$future_libdirs " in + *" $libdir "*) ;; + *) future_libdirs="$future_libdirs $libdir" ;; + esac + fi + + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`/ + test "X$dir" = "X$file/" && dir= + dir="$dir$objdir" + + if test -n "$relink_command"; then + # Determine the prefix the user has applied to our future dir. + inst_prefix_dir=`$echo "$destdir" | $SED "s%$libdir\$%%"` + + # Don't allow the user to place us outside of our expected + # location b/c this prevents finding dependent libraries that + # are installed to the same prefix. + # At present, this check doesn't affect windows .dll's that + # are installed into $libdir/../bin (currently, that works fine) + # but it's something to keep an eye on. + if test "$inst_prefix_dir" = "$destdir"; then + $echo "$modename: error: cannot install \`$file' to a directory not ending in $libdir" 1>&2 + exit 1 + fi + + if test -n "$inst_prefix_dir"; then + # Stick the inst_prefix_dir data into the link command. + relink_command=`$echo "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` + else + relink_command=`$echo "$relink_command" | $SED "s%@inst_prefix_dir@%%"` + fi + + $echo "$modename: warning: relinking \`$file'" 1>&2 + $show "$relink_command" + if $run eval "$relink_command"; then : + else + $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 + exit 1 + fi + fi + + # See the names of the shared library. + set dummy $library_names + if test -n "$2"; then + realname="$2" + shift + shift + + srcname="$realname" + test -n "$relink_command" && srcname="$realname"T + + # Install the shared library and build the symlinks. + $show "$install_prog $dir/$srcname $destdir/$realname" + $run eval "$install_prog $dir/$srcname $destdir/$realname" || exit $? + if test -n "$stripme" && test -n "$striplib"; then + $show "$striplib $destdir/$realname" + $run eval "$striplib $destdir/$realname" || exit $? + fi + + if test "$#" -gt 0; then + # Delete the old symlinks, and create new ones. + for linkname + do + if test "$linkname" != "$realname"; then + $show "(cd $destdir && $rm $linkname && $LN_S $realname $linkname)" + $run eval "(cd $destdir && $rm $linkname && $LN_S $realname $linkname)" + fi + done + fi + + # Do each command in the postinstall commands. + lib="$destdir/$realname" + cmds=$postinstall_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + + # Install the pseudo-library for information purposes. + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + instname="$dir/$name"i + $show "$install_prog $instname $destdir/$name" + $run eval "$install_prog $instname $destdir/$name" || exit $? + + # Maybe install the static library, too. + test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library" + ;; + + *.lo) + # Install (i.e. copy) a libtool object. + + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + destfile="$destdir/$destfile" + fi + + # Deduce the name of the destination old-style object file. + case $destfile in + *.lo) + staticdest=`$echo "X$destfile" | $Xsed -e "$lo2o"` + ;; + *.$objext) + staticdest="$destfile" + destfile= + ;; + *) + $echo "$modename: cannot copy a libtool object to \`$destfile'" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; + esac + + # Install the libtool object if requested. + if test -n "$destfile"; then + $show "$install_prog $file $destfile" + $run eval "$install_prog $file $destfile" || exit $? + fi + + # Install the old object if enabled. + if test "$build_old_libs" = yes; then + # Deduce the name of the old-style object file. + staticobj=`$echo "X$file" | $Xsed -e "$lo2o"` + + $show "$install_prog $staticobj $staticdest" + $run eval "$install_prog \$staticobj \$staticdest" || exit $? + fi + exit 0 + ;; + + *) + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + destfile="$destdir/$destfile" + fi + + # If the file is missing, and there is a .exe on the end, strip it + # because it is most likely a libtool script we actually want to + # install + stripped_ext="" + case $file in + *.exe) + if test ! -f "$file"; then + file=`$echo $file|${SED} 's,.exe$,,'` + stripped_ext=".exe" + fi + ;; + esac + + # Do a test to see if this is really a libtool program. + case $host in + *cygwin*|*mingw*) + wrapper=`$echo $file | ${SED} -e 's,.exe$,,'` + ;; + *) + wrapper=$file + ;; + esac + if (${SED} -e '4q' $wrapper | grep "^# Generated by .*$PACKAGE")>/dev/null 2>&1; then + notinst_deplibs= + relink_command= + + # To insure that "foo" is sourced, and not "foo.exe", + # finese the cygwin/MSYS system by explicitly sourcing "foo." + # which disallows the automatic-append-.exe behavior. + case $build in + *cygwin* | *mingw*) wrapperdot=${wrapper}. ;; + *) wrapperdot=${wrapper} ;; + esac + # If there is no directory component, then add one. + case $file in + */* | *\\*) . ${wrapperdot} ;; + *) . ./${wrapperdot} ;; + esac + + # Check the variables that should have been set. + if test -z "$notinst_deplibs"; then + $echo "$modename: invalid libtool wrapper script \`$wrapper'" 1>&2 + exit 1 + fi + + finalize=yes + for lib in $notinst_deplibs; do + # Check to see that each library is installed. + libdir= + if test -f "$lib"; then + # If there is no directory component, then add one. + case $lib in + */* | *\\*) . $lib ;; + *) . ./$lib ;; + esac + fi + libfile="$libdir/"`$echo "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test + if test -n "$libdir" && test ! -f "$libfile"; then + $echo "$modename: warning: \`$lib' has not been installed in \`$libdir'" 1>&2 + finalize=no + fi + done + + relink_command= + # To insure that "foo" is sourced, and not "foo.exe", + # finese the cygwin/MSYS system by explicitly sourcing "foo." + # which disallows the automatic-append-.exe behavior. + case $build in + *cygwin* | *mingw*) wrapperdot=${wrapper}. ;; + *) wrapperdot=${wrapper} ;; + esac + # If there is no directory component, then add one. + case $file in + */* | *\\*) . ${wrapperdot} ;; + *) . ./${wrapperdot} ;; + esac + + outputname= + if test "$fast_install" = no && test -n "$relink_command"; then + if test "$finalize" = yes && test -z "$run"; then + tmpdir="/tmp" + test -n "$TMPDIR" && tmpdir="$TMPDIR" + if tmpdir=`mktemp -d $tmpdir/libtool-XXXXXX 2> /dev/null`; then : + else + tmpdir="$tmpdir/libtool-$$" + if $mkdir "$tmpdir" && chmod 700 "$tmpdir"; then : + else + $echo "$modename: error: cannot create temporary directory \`$tmpdir'" 1>&2 + continue + fi + fi + file=`$echo "X$file$stripped_ext" | $Xsed -e 's%^.*/%%'` + outputname="$tmpdir/$file" + # Replace the output file specification. + relink_command=`$echo "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'` + + $show "$relink_command" + if $run eval "$relink_command"; then : + else + $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 + ${rm}r "$tmpdir" + continue + fi + file="$outputname" + else + $echo "$modename: warning: cannot relink \`$file'" 1>&2 + fi + else + # Install the binary that we compiled earlier. + file=`$echo "X$file$stripped_ext" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"` + fi + fi + + # remove .exe since cygwin /usr/bin/install will append another + # one anyways + case $install_prog,$host in + */usr/bin/install*,*cygwin*) + case $file:$destfile in + *.exe:*.exe) + # this is ok + ;; + *.exe:*) + destfile=$destfile.exe + ;; + *:*.exe) + destfile=`$echo $destfile | ${SED} -e 's,.exe$,,'` + ;; + esac + ;; + esac + $show "$install_prog$stripme $file $destfile" + $run eval "$install_prog\$stripme \$file \$destfile" || exit $? + test -n "$outputname" && ${rm}r "$tmpdir" + ;; + esac + done + + for file in $staticlibs; do + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + + # Set up the ranlib parameters. + oldlib="$destdir/$name" + + $show "$install_prog $file $oldlib" + $run eval "$install_prog \$file \$oldlib" || exit $? + + if test -n "$stripme" && test -n "$old_striplib"; then + $show "$old_striplib $oldlib" + $run eval "$old_striplib $oldlib" || exit $? + fi + + # Do each command in the postinstall commands. + cmds=$old_postinstall_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + done + + if test -n "$future_libdirs"; then + $echo "$modename: warning: remember to run \`$progname --finish$future_libdirs'" 1>&2 + fi + + if test -n "$current_libdirs"; then + # Maybe just do a dry run. + test -n "$run" && current_libdirs=" -n$current_libdirs" + exec_cmd='$SHELL $0 $preserve_args --finish$current_libdirs' + else + exit 0 + fi + ;; + + # libtool finish mode + finish) + modename="$modename: finish" + libdirs="$nonopt" + admincmds= + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + for dir + do + libdirs="$libdirs $dir" + done + + for libdir in $libdirs; do + if test -n "$finish_cmds"; then + # Do each command in the finish commands. + cmds=$finish_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || admincmds="$admincmds + $cmd" + done + IFS="$save_ifs" + fi + if test -n "$finish_eval"; then + # Do the single finish_eval. + eval cmds=\"$finish_eval\" + $run eval "$cmds" || admincmds="$admincmds + $cmds" + fi + done + fi + + # Exit here if they wanted silent mode. + test "$show" = : && exit 0 + + $echo "----------------------------------------------------------------------" + $echo "Libraries have been installed in:" + for libdir in $libdirs; do + $echo " $libdir" + done + $echo + $echo "If you ever happen to want to link against installed libraries" + $echo "in a given directory, LIBDIR, you must either use libtool, and" + $echo "specify the full pathname of the library, or use the \`-LLIBDIR'" + $echo "flag during linking and do at least one of the following:" + if test -n "$shlibpath_var"; then + $echo " - add LIBDIR to the \`$shlibpath_var' environment variable" + $echo " during execution" + fi + if test -n "$runpath_var"; then + $echo " - add LIBDIR to the \`$runpath_var' environment variable" + $echo " during linking" + fi + if test -n "$hardcode_libdir_flag_spec"; then + libdir=LIBDIR + eval flag=\"$hardcode_libdir_flag_spec\" + + $echo " - use the \`$flag' linker flag" + fi + if test -n "$admincmds"; then + $echo " - have your system administrator run these commands:$admincmds" + fi + if test -f /etc/ld.so.conf; then + $echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" + fi + $echo + $echo "See any operating system documentation about shared libraries for" + $echo "more information, such as the ld(1) and ld.so(8) manual pages." + $echo "----------------------------------------------------------------------" + exit 0 + ;; + + # libtool execute mode + execute) + modename="$modename: execute" + + # The first argument is the command name. + cmd="$nonopt" + if test -z "$cmd"; then + $echo "$modename: you must specify a COMMAND" 1>&2 + $echo "$help" + exit 1 + fi + + # Handle -dlopen flags immediately. + for file in $execute_dlfiles; do + if test ! -f "$file"; then + $echo "$modename: \`$file' is not a file" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + dir= + case $file in + *.la) + # Check to see that this really is a libtool archive. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + else + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + # Read the libtool library. + dlname= + library_names= + + # If there is no directory component, then add one. + case $file in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Skip this library if it cannot be dlopened. + if test -z "$dlname"; then + # Warn if it was a shared library. + test -n "$library_names" && $echo "$modename: warning: \`$file' was not linked with \`-export-dynamic'" + continue + fi + + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$file" && dir=. + + if test -f "$dir/$objdir/$dlname"; then + dir="$dir/$objdir" + else + $echo "$modename: cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" 1>&2 + exit 1 + fi + ;; + + *.lo) + # Just add the directory containing the .lo file. + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$file" && dir=. + ;; + + *) + $echo "$modename: warning \`-dlopen' is ignored for non-libtool libraries and objects" 1>&2 + continue + ;; + esac + + # Get the absolute pathname. + absdir=`cd "$dir" && pwd` + test -n "$absdir" && dir="$absdir" + + # Now add the directory to shlibpath_var. + if eval "test -z \"\$$shlibpath_var\""; then + eval "$shlibpath_var=\"\$dir\"" + else + eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" + fi + done + + # This variable tells wrapper scripts just to set shlibpath_var + # rather than running their programs. + libtool_execute_magic="$magic" + + # Check if any of the arguments is a wrapper script. + args= + for file + do + case $file in + -*) ;; + *) + # Do a test to see if this is really a libtool program. + if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + # If there is no directory component, then add one. + case $file in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Transform arg to wrapped name. + file="$progdir/$program" + fi + ;; + esac + # Quote arguments (to preserve shell metacharacters). + file=`$echo "X$file" | $Xsed -e "$sed_quote_subst"` + args="$args \"$file\"" + done + + if test -z "$run"; then + if test -n "$shlibpath_var"; then + # Export the shlibpath_var. + eval "export $shlibpath_var" + fi + + # Restore saved environment variables + if test "${save_LC_ALL+set}" = set; then + LC_ALL="$save_LC_ALL"; export LC_ALL + fi + if test "${save_LANG+set}" = set; then + LANG="$save_LANG"; export LANG + fi + + # Now prepare to actually exec the command. + exec_cmd="\"\$cmd\"$args" + else + # Display what would be done. + if test -n "$shlibpath_var"; then + eval "\$echo \"\$shlibpath_var=\$$shlibpath_var\"" + $echo "export $shlibpath_var" + fi + eval \$echo \"\$cmd\"$args + exit 0 + fi + ;; + + # libtool clean and uninstall mode + clean | uninstall) + modename="$modename: $mode" + rm="$nonopt" + files= + rmforce= + exit_status=0 + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + for arg + do + case $arg in + -f) rm="$rm $arg"; rmforce=yes ;; + -*) rm="$rm $arg" ;; + *) files="$files $arg" ;; + esac + done + + if test -z "$rm"; then + $echo "$modename: you must specify an RM program" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + rmdirs= + + origobjdir="$objdir" + for file in $files; do + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + if test "X$dir" = "X$file"; then + dir=. + objdir="$origobjdir" + else + objdir="$dir/$origobjdir" + fi + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + test "$mode" = uninstall && objdir="$dir" + + # Remember objdir for removal later, being careful to avoid duplicates + if test "$mode" = clean; then + case " $rmdirs " in + *" $objdir "*) ;; + *) rmdirs="$rmdirs $objdir" ;; + esac + fi + + # Don't error if the file doesn't exist and rm -f was used. + if (test -L "$file") >/dev/null 2>&1 \ + || (test -h "$file") >/dev/null 2>&1 \ + || test -f "$file"; then + : + elif test -d "$file"; then + exit_status=1 + continue + elif test "$rmforce" = yes; then + continue + fi + + rmfiles="$file" + + case $name in + *.la) + # Possibly a libtool archive, so verify it. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + . $dir/$name + + # Delete the libtool libraries and symlinks. + for n in $library_names; do + rmfiles="$rmfiles $objdir/$n" + done + test -n "$old_library" && rmfiles="$rmfiles $objdir/$old_library" + test "$mode" = clean && rmfiles="$rmfiles $objdir/$name $objdir/${name}i" + + if test "$mode" = uninstall; then + if test -n "$library_names"; then + # Do each command in the postuninstall commands. + cmds=$postuninstall_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" + if test "$?" -ne 0 && test "$rmforce" != yes; then + exit_status=1 + fi + done + IFS="$save_ifs" + fi + + if test -n "$old_library"; then + # Do each command in the old_postuninstall commands. + cmds=$old_postuninstall_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" + if test "$?" -ne 0 && test "$rmforce" != yes; then + exit_status=1 + fi + done + IFS="$save_ifs" + fi + # FIXME: should reinstall the best remaining shared library. + fi + fi + ;; + + *.lo) + # Possibly a libtool object, so verify it. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + + # Read the .lo file + . $dir/$name + + # Add PIC object to the list of files to remove. + if test -n "$pic_object" \ + && test "$pic_object" != none; then + rmfiles="$rmfiles $dir/$pic_object" + fi + + # Add non-PIC object to the list of files to remove. + if test -n "$non_pic_object" \ + && test "$non_pic_object" != none; then + rmfiles="$rmfiles $dir/$non_pic_object" + fi + fi + ;; + + *) + if test "$mode" = clean ; then + noexename=$name + case $file in + *.exe) + file=`$echo $file|${SED} 's,.exe$,,'` + noexename=`$echo $name|${SED} 's,.exe$,,'` + # $file with .exe has already been added to rmfiles, + # add $file without .exe + rmfiles="$rmfiles $file" + ;; + esac + # Do a test to see if this is a libtool program. + if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + relink_command= + . $dir/$noexename + + # note $name still contains .exe if it was in $file originally + # as does the version of $file that was added into $rmfiles + rmfiles="$rmfiles $objdir/$name $objdir/${name}S.${objext}" + if test "$fast_install" = yes && test -n "$relink_command"; then + rmfiles="$rmfiles $objdir/lt-$name" + fi + if test "X$noexename" != "X$name" ; then + rmfiles="$rmfiles $objdir/lt-${noexename}.c" + fi + fi + fi + ;; + esac + $show "$rm $rmfiles" + $run $rm $rmfiles || exit_status=1 + done + objdir="$origobjdir" + + # Try to remove the ${objdir}s in the directories where we deleted files + for dir in $rmdirs; do + if test -d "$dir"; then + $show "rmdir $dir" + $run rmdir $dir >/dev/null 2>&1 + fi + done + + exit $exit_status + ;; + + "") + $echo "$modename: you must specify a MODE" 1>&2 + $echo "$generic_help" 1>&2 + exit 1 + ;; + esac + + if test -z "$exec_cmd"; then + $echo "$modename: invalid operation mode \`$mode'" 1>&2 + $echo "$generic_help" 1>&2 + exit 1 + fi +fi # test -z "$show_help" + +if test -n "$exec_cmd"; then + eval exec $exec_cmd + exit 1 +fi + +# We need to display help for each of the modes. +case $mode in +"") $echo \ +"Usage: $modename [OPTION]... [MODE-ARG]... + +Provide generalized library-building support services. + + --config show all configuration variables + --debug enable verbose shell tracing +-n, --dry-run display commands without modifying any files + --features display basic configuration information and exit + --finish same as \`--mode=finish' + --help display this help message and exit + --mode=MODE use operation mode MODE [default=inferred from MODE-ARGS] + --quiet same as \`--silent' + --silent don't print informational messages + --tag=TAG use configuration variables from tag TAG + --version print version information + +MODE must be one of the following: + + clean remove files from the build directory + compile compile a source file into a libtool object + execute automatically set library path, then run a program + finish complete the installation of libtool libraries + install install libraries or executables + link create a library or an executable + uninstall remove libraries from an installed directory + +MODE-ARGS vary depending on the MODE. Try \`$modename --help --mode=MODE' for +a more detailed description of MODE. + +Report bugs to ." + exit 0 + ;; + +clean) + $echo \ +"Usage: $modename [OPTION]... --mode=clean RM [RM-OPTION]... FILE... + +Remove files from the build directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, object or program, all the files associated +with it are deleted. Otherwise, only FILE itself is deleted using RM." + ;; + +compile) + $echo \ +"Usage: $modename [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE + +Compile a source file into a libtool library object. + +This mode accepts the following additional options: + + -o OUTPUT-FILE set the output file name to OUTPUT-FILE + -prefer-pic try to building PIC objects only + -prefer-non-pic try to building non-PIC objects only + -static always build a \`.o' file suitable for static linking + +COMPILE-COMMAND is a command to be used in creating a \`standard' object file +from the given SOURCEFILE. + +The output file name is determined by removing the directory component from +SOURCEFILE, then substituting the C source code suffix \`.c' with the +library object suffix, \`.lo'." + ;; + +execute) + $echo \ +"Usage: $modename [OPTION]... --mode=execute COMMAND [ARGS]... + +Automatically set library path, then run a program. + +This mode accepts the following additional options: + + -dlopen FILE add the directory containing FILE to the library path + +This mode sets the library path environment variable according to \`-dlopen' +flags. + +If any of the ARGS are libtool executable wrappers, then they are translated +into their corresponding uninstalled binary, and any of their required library +directories are added to the library path. + +Then, COMMAND is executed, with ARGS as arguments." + ;; + +finish) + $echo \ +"Usage: $modename [OPTION]... --mode=finish [LIBDIR]... + +Complete the installation of libtool libraries. + +Each LIBDIR is a directory that contains libtool libraries. + +The commands that this mode executes may require superuser privileges. Use +the \`--dry-run' option if you just want to see what would be executed." + ;; + +install) + $echo \ +"Usage: $modename [OPTION]... --mode=install INSTALL-COMMAND... + +Install executables or libraries. + +INSTALL-COMMAND is the installation command. The first component should be +either the \`install' or \`cp' program. + +The rest of the components are interpreted as arguments to that command (only +BSD-compatible install options are recognized)." + ;; + +link) + $echo \ +"Usage: $modename [OPTION]... --mode=link LINK-COMMAND... + +Link object files or libraries together to form another library, or to +create an executable program. + +LINK-COMMAND is a command using the C compiler that you would use to create +a program from several object files. + +The following components of LINK-COMMAND are treated specially: + + -all-static do not do any dynamic linking at all + -avoid-version do not add a version suffix if possible + -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime + -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols + -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) + -export-symbols SYMFILE + try to export only the symbols listed in SYMFILE + -export-symbols-regex REGEX + try to export only the symbols matching REGEX + -LLIBDIR search LIBDIR for required installed libraries + -lNAME OUTPUT-FILE requires the installed library libNAME + -module build a library that can dlopened + -no-fast-install disable the fast-install mode + -no-install link a not-installable executable + -no-undefined declare that a library does not refer to external symbols + -o OUTPUT-FILE create OUTPUT-FILE from the specified objects + -objectlist FILE Use a list of object files found in FILE to specify objects + -precious-files-regex REGEX + don't remove output files matching REGEX + -release RELEASE specify package release information + -rpath LIBDIR the created library will eventually be installed in LIBDIR + -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries + -static do not do any dynamic linking of libtool libraries + -version-info CURRENT[:REVISION[:AGE]] + specify library version info [each variable defaults to 0] + +All other options (arguments beginning with \`-') are ignored. + +Every other argument is treated as a filename. Files ending in \`.la' are +treated as uninstalled libtool libraries, other files are standard or library +object files. + +If the OUTPUT-FILE ends in \`.la', then a libtool library is created, +only library objects (\`.lo' files) may be specified, and \`-rpath' is +required, except when creating a convenience library. + +If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created +using \`ar' and \`ranlib', or on Windows using \`lib'. + +If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file +is created, otherwise an executable program is created." + ;; + +uninstall) + $echo \ +"Usage: $modename [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... + +Remove libraries from an installation directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, all the files associated with it are deleted. +Otherwise, only FILE itself is deleted using RM." + ;; + +*) + $echo "$modename: invalid operation mode \`$mode'" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; +esac + +$echo +$echo "Try \`$modename --help' for more information about other modes." + +exit 0 + +# The TAGs below are defined such that we never get into a situation +# in which we disable both kinds of libraries. Given conflicting +# choices, we go for a static library, that is the most portable, +# since we can't tell whether shared libraries were disabled because +# the user asked for that or because the platform doesn't support +# them. This is particularly important on AIX, because we don't +# support having both static and shared libraries enabled at the same +# time on that platform, so we default to a shared-only configuration. +# If a disable-shared tag is given, we'll fallback to a static-only +# configuration. But we'll never go from static-only to shared-only. + +# ### BEGIN LIBTOOL TAG CONFIG: disable-shared +build_libtool_libs=no +build_old_libs=yes +# ### END LIBTOOL TAG CONFIG: disable-shared + +# ### BEGIN LIBTOOL TAG CONFIG: disable-static +build_old_libs=`case $build_libtool_libs in yes) $echo no;; *) $echo yes;; esac` +# ### END LIBTOOL TAG CONFIG: disable-static + +# Local Variables: +# mode:shell-script +# sh-indentation:2 +# End: diff --git a/mbuni/missing b/mbuni/missing new file mode 100755 index 0000000..e7ef83a --- /dev/null +++ b/mbuni/missing @@ -0,0 +1,360 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. + +scriptversion=2003-09-02.23 + +# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003 +# Free Software Foundation, Inc. +# Originally by Fran,cois Pinard , 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +run=: + +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi + +msg="missing on your system" + +case "$1" in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + # Exit code 63 means version mismatch. This often happens + # when the user try to use an ancient version of a tool on + # a file that requires a minimum version. In this case we + # we should proceed has if the program had been absent, or + # if --run hadn't been passed. + if test $? = 63; then + run=: + msg="probably too old" + fi + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. +case "$1" in + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + tar try tar, gnutar, gtar, then tar without non-portable flags + yacc create \`y.tab.[ch]', if possible, from existing .[ch] + +Send bug reports to ." + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + + aclocal*) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acinclude.m4' or \`${configure_ac}'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`${configure_ac}'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case "$f" in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake*) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + autom4te) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is needed, but is $msg. + You might have modified some files without having the + proper tools for further handling them. + You can get \`$1' as part of \`Autoconf' from any GNU + archive site." + + file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'` + test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo "#! /bin/sh" + echo "# Created by GNU Automake missing as a replacement of" + echo "# $ $@" + echo "exit 0" + chmod +x $file + exit 1 + fi + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' $msg. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if [ ! -f y.tab.h ]; then + echo >y.tab.h + fi + if [ ! -f y.tab.c ]; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if [ ! -f lex.yy.c ]; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + help2man) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` + fi + if [ -f "$file" ]; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit 1 + fi + ;; + + makeinfo) + if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then + # We have makeinfo, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file` + fi + touch $file + ;; + + tar) + shift + if test -n "$run"; then + echo 1>&2 "ERROR: \`tar' requires --run" + exit 1 + fi + + # We have already tried tar in the generic part. + # Look for gnutar/gtar before invocation to avoid ugly error + # messages. + if (gnutar --version > /dev/null 2>&1); then + gnutar "$@" && exit 0 + fi + if (gtar --version > /dev/null 2>&1); then + gtar "$@" && exit 0 + fi + firstarg="$1" + if shift; then + case "$firstarg" in + *o*) + firstarg=`echo "$firstarg" | sed s/o//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + case "$firstarg" in + *h*) + firstarg=`echo "$firstarg" | sed s/h//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + fi + + echo 1>&2 "\ +WARNING: I can't seem to be able to run \`tar' with the given arguments. + You may want to install GNU tar or Free paxutils, or check the + command line arguments." + exit 1 + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and is $msg. + You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequisites for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/mbuni/mmlib/Makefile.am b/mbuni/mmlib/Makefile.am new file mode 100644 index 0000000..f49b58a --- /dev/null +++ b/mbuni/mmlib/Makefile.am @@ -0,0 +1,4 @@ +noinst_LIBRARIES = libmms.a +libmms_a_SOURCES = mms_billing.c mms_billing.h mms_msg.c mms_msg.h mms_queue.c mms_queue.h mms_strings.c mms_strings.h mms_uaprof.c mms_uaprof.h mms_util.c mms_util.h mms_resolve.c + + diff --git a/mbuni/mmlib/Makefile.in b/mbuni/mmlib/Makefile.in new file mode 100644 index 0000000..9390dfd --- /dev/null +++ b/mbuni/mmlib/Makefile.in @@ -0,0 +1,381 @@ +# Makefile.in generated by automake 1.9.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +SOURCES = $(libmms_a_SOURCES) + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = mmlib +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libmms_a_AR = $(AR) $(ARFLAGS) +libmms_a_LIBADD = +am_libmms_a_OBJECTS = mms_billing.$(OBJEXT) mms_msg.$(OBJEXT) \ + mms_queue.$(OBJEXT) mms_strings.$(OBJEXT) mms_uaprof.$(OBJEXT) \ + mms_util.$(OBJEXT) mms_resolve.$(OBJEXT) +libmms_a_OBJECTS = $(am_libmms_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libmms_a_SOURCES) +DIST_SOURCES = $(libmms_a_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXE_EXT = @EXE_EXT@ +GW_CONFIG = @GW_CONFIG@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host_alias = @host_alias@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +noinst_LIBRARIES = libmms.a +libmms_a_SOURCES = mms_billing.c mms_billing.h mms_msg.c mms_msg.h mms_queue.c mms_queue.h mms_strings.c mms_strings.h mms_uaprof.c mms_uaprof.h mms_util.c mms_util.h mms_resolve.c +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu mmlib/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu mmlib/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libmms.a: $(libmms_a_OBJECTS) $(libmms_a_DEPENDENCIES) + -rm -f libmms.a + $(libmms_a_AR) libmms.a $(libmms_a_OBJECTS) $(libmms_a_LIBADD) + $(RANLIB) libmms.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mms_billing.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mms_msg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mms_queue.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mms_resolve.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mms_strings.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mms_uaprof.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mms_util.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/mbuni/mmlib/mms_billing.c b/mbuni/mmlib/mms_billing.c new file mode 100644 index 0000000..8d2b51d --- /dev/null +++ b/mbuni/mmlib/mms_billing.c @@ -0,0 +1,51 @@ +#include +#include + +#include "mms_billing.h" + +/* This module provides a basic biller and CDR implementation. Totally no frills, but a basis + * for implementing a 'real' module. + * It does CDR but no billing (of course). + */ + + +static void *mms_billingmodule_init(char *settings) +{ + return fopen(settings, "a"); /* Settings is interpreted as a file name. */ +} + + +static int mms_billingmodule_fini(void *module_data) +{ + return module_data ? fclose(module_data) : -1; +} + +static double mms_billmsg(Octstr *from, List *to, unsigned long msg_size, void *module_data) +{ + return 0; +} + +static int mms_logcdr(MmsCdrStruct *cdr) +{ + if (cdr && cdr->module_data) { + char buf[CBUFSIZE]; + struct tm tm; + + localtime_r(&cdr->sdate, &tm); + gw_strftime(buf, sizeof buf, "%Y-%m-%d %H:%M:%S", &tm); + fprintf(cdr->module_data, "%s\t%.128s\t%.256s\t%.256s\t%ld\n", + buf, cdr->from, cdr->to, cdr->msgid, cdr->msg_size); + fflush(cdr->module_data); + } else + return -1; + + return 0; +} + +/* The function itself. */ +MmsBillingFuncStruct mms_billfuncs = { + mms_billingmodule_init, + mms_logcdr, + mms_billmsg, + mms_billingmodule_fini +}; diff --git a/mbuni/mmlib/mms_billing.h b/mbuni/mmlib/mms_billing.h new file mode 100644 index 0000000..f6cb7d1 --- /dev/null +++ b/mbuni/mmlib/mms_billing.h @@ -0,0 +1,42 @@ +#ifndef __MMS_BILLING_INCLUDED__ +#define __MMS_BILLING_INCLUDED__ + +#include +#include "gwlib/gwlib.h" + +/* Billing and CDR module. This file provides prototypes for all billing functions. + * The idea is that for each site a DSO will be created that the mmsglobalsender loads and gets + * from functions to do billing. This .h file provides prototypes for these functions. + * It has an attendant function to provide basic biller and CDR functions + */ +#define CBUFSIZE 256 +typedef struct MmsCdrStruct { + void *module_data; + time_t sdate; + char from[CBUFSIZE]; + char to[CBUFSIZE]; + char msgid[CBUFSIZE]; + unsigned long msg_size; +} MmsCdrStruct; + + +typedef struct MmsBillingFuncStruct { +/* This function is called once to initialise the billing module. Return a generic object, + * which is passed with each CDR. + */ + void *(*mms_billingmodule_init)(char *settings); + +/* This function logs a cdr to wherever it is logging to. */ + int (*mms_logcdr)(MmsCdrStruct *cdr); + +/* Bills a message. Returns >= 0 if billed ok, -1 if message should be rejected, + * -2 on internal (temporary) error. + */ + double (*mms_billmsg)(Octstr *from, List *to, unsigned long msg_size, void *module_data); + + int (*mms_billingmodule_fini)(void *module_data); +} MmsBillingFuncStruct; + +extern MmsBillingFuncStruct mms_billfuncs; /* The module must expose this symbol. */ + +#endif diff --git a/mbuni/mmlib/mms_msg.c b/mbuni/mmlib/mms_msg.c new file mode 100644 index 0000000..2ed41eb --- /dev/null +++ b/mbuni/mmlib/mms_msg.c @@ -0,0 +1,1285 @@ +/* MMS Message structure definition, encoder and decoder and helper functions + * + */ +#include +#include +#include "mms_msg.h" +#include "mms_util.h" + +struct MmsMsg { + int message_type; + Octstr *msgId; + List *headers; /* Of type HTTPHeader. */ + + unsigned char ismultipart; + union { + List *l; + Octstr *s; + } body; /* list of MIMEEntity (for multipart), text otherwise.*/ +}; + + +#define SIZHINT 47 + +static void mm_destroy(MIMEEntity *mx); +static void pack_short_integer(Octstr *s, int ch) +{ + unsigned char c = ch|0x80; + + octstr_append_data(s, &c, 1); +} +#if 0 +static void encode_uint(Octstr *os, unsigned int l) +{ + char xbuf[5]; + int i = 0; + + do { + xbuf[i++] = l&0x7f; + l>>=7; + } while (i < 5 && l); + + while (--i > 0) { + xbuf[i] |= 0x80; + octstr_append_data(os, &xbuf[i], 1); + } + + octstr_append_data(os, &xbuf[0], 1); +} +#endif + +int mms_validate_address(Octstr *s) +{ + + int i = octstr_search_char(s, '/', 0); + int l = octstr_len(s); + + if (octstr_search_char(s, '@', 0) > 0) + return 0; + else if (i >= 0) + if (octstr_case_search(s, octstr_imm("PLMN"), 0) + 4 == l || + octstr_case_search(s, octstr_imm("IPv4"), 0) + 4 == l || + octstr_case_search(s, octstr_imm("IPv6"), 0) + 4 == l) + return 0; + else + return -1; + else + return -1; + +} + + +static int decode_multipart(ParseContext *context, List *body) +{ + int i=0, n; + + n = parse_get_uintvar(context); + + for (i = 0; i 0 ; i++) { + int dlen, hlen; + MIMEEntity *x = gw_malloc(sizeof *x); + + Octstr *hs; + Octstr *content; + Octstr *content_type; + + memset(x, 0,sizeof *x); + + hlen = parse_get_uintvar(context); + dlen = parse_get_uintvar(context); + + parse_limit(context, hlen); + + + + hs = parse_get_octets(context, parse_octets_left(context)); + x->headers = wsp_headers_unpack(hs, 1); + + octstr_destroy(hs); + + parse_skip_to_limit(context); + parse_pop_limit(context); + + content_type = http_header_value(x->headers, octstr_imm("Content-Type")); + content = parse_get_octets(context, dlen); + + if (!content || !content_type) { + int pleft = parse_octets_left(context); + warning(0, "Parse error reading mime body [hlen=%d, dlen=%d, left=%d]!",hlen,dlen, pleft); + mime_entity_destroy(x); + if (content_type) octstr_destroy(content_type); + return -1; + } + if (octstr_case_compare(content_type, + octstr_imm("application/vnd.wap.multipart.related")) == 0 || + octstr_case_compare(content_type, + octstr_imm("application/vnd.wap.multipart.alternative")) == 0 || + octstr_case_compare(content_type, + octstr_imm("application/vnd.wap.multipart.mixed")) == 0) { /* Body is multipart. */ + ParseContext *p = parse_context_create(content); + int res; + List *ml = x->multiparts = list_create(); + + res = decode_multipart(p, x->multiparts); + octstr_destroy(content); + parse_context_destroy(p); + + if (res < 0) { + list_destroy(ml, (list_item_destructor_t *)mime_entity_destroy); + return -1; + } + } else + x->body = content; + + octstr_destroy(content_type); + + list_append(body, x); + } + return 0; +} + +static int encode_multipart(Octstr *os, List *body) +{ + int i=0, n; + + n = list_len(body); + octstr_append_uintvar(os, n); + + while (iheaders, 1, WSP_1_3); + + if (x->multiparts && + list_len(x->multiparts) > 0) /* This is a multi-part, go down further. */ + encode_multipart(mbody, x->multiparts); + else if (x->body) + octstr_append(mbody, x->body); + + octstr_append_uintvar(os, octstr_len(mhdr)); + octstr_append_uintvar(os, octstr_len(mbody)); + + octstr_append(os, mhdr); + octstr_append(os, mbody); + + octstr_destroy(mhdr); + octstr_destroy(mbody); + + i++; + } + return 0; +} + +static int decode_msgbody(ParseContext *context, MmsMsg *msg) +{ + int res = 0; + if (msg->ismultipart) { + msg->body.l = list_create(); + res = decode_multipart(context, msg->body.l); + } else + msg->body.s = parse_get_rest(context); + + return res; +} + +static void encode_msgbody(Octstr *os, MmsMsg *msg) +{ + if (msg->ismultipart) + encode_multipart(os, msg->body.l); + else + octstr_append(os, msg->body.s); +} + +/* Decodes it and returns the value, which is a pointer into the string so be careful! */ +static void mms_unpack_well_known_field(List *unpacked, int field_type, + ParseContext *context, Octstr *xfrom) +{ + int val, ret; + char *hname = NULL; + + Octstr *decoded = NULL; + char *ch = NULL; + + ret = wsp_field_value(context, &val); + + if (parse_error(context)) { + warning(0, "Faulty header [code = %d], skipping remaining headers.", field_type); + parse_skip_to_limit(context); + return; + } + + hname = mms_header_to_cstr(field_type); + + if (ret == WSP_FIELD_VALUE_NUL_STRING) + decoded = parse_get_nul_string(context); + + switch (field_type) { + + case MMS_HEADER_TO: + case MMS_HEADER_CC: + case MMS_HEADER_BCC: + + if (ret == WSP_FIELD_VALUE_DATA) { /* expect charset text. */ + long charset; /* Get it and ignore it. */ + wsp_secondary_field_value(context, &charset); + decoded = parse_get_nul_string(context); + } else if (ret != WSP_FIELD_VALUE_NUL_STRING) { + warning(0, "Faulty header value for %s!\n", hname); + decoded = octstr_imm(""); + } + + if (mms_validate_address(decoded)) + warning(0, "Faulty address [%s] format in field %s!", + octstr_get_cstr(decoded), hname); + break; + case MMS_HEADER_SUBJECT: + case MMS_HEADER_RESPONSE_TEXT: + case MMS_HEADER_RETRIEVE_TEXT: + + if (ret == WSP_FIELD_VALUE_DATA) { /* encoded string, expect charset then text. */ + long charset; /* Get it and ignore it. */ + wsp_secondary_field_value(context, &charset); + decoded = parse_get_nul_string(context); + } else if (ret != WSP_FIELD_VALUE_NUL_STRING) { + warning(0, "Faulty header value for %s!\n", hname); + decoded = octstr_imm(""); + } + break; + case MMS_HEADER_TRANSACTION_ID: + case MMS_HEADER_CONTENT_LOCATION: + case MMS_HEADER_MESSAGE_ID: + case MMS_HEADER_REPLY_CHARGING_ID: + if (ret != WSP_FIELD_VALUE_NUL_STRING) + warning(0, "Unexpected field value type %d for header %s\n", + ret, hname); + + break; + case MMS_HEADER_MMS_VERSION: + if (ret == WSP_FIELD_VALUE_ENCODED) + decoded = wsp_unpack_version_value(val); + break; + /* integer/octet values */ + case MMS_HEADER_DELIVERY_REPORT: + case MMS_HEADER_REPORT_ALLOWED: + case MMS_HEADER_READ_REPORT: + ch = mms_reports_to_cstr(val); + break; + case MMS_HEADER_MESSAGE_TYPE: + ch = mms_message_type_to_cstr(val); + break; + case MMS_HEADER_PRIORITY: + ch = mms_priority_to_cstr(val); + break; + case MMS_HEADER_READ_STATUS: + ch = mms_read_status_to_cstr(val); + break; + case MMS_HEADER_REPLY_CHARGING: + ch = mms_reply_charging_to_cstr(val); + break; + + case MMS_HEADER_RESPONSE_STATUS: + ch = mms_response_status_to_cstr(val); + break; + case MMS_HEADER_RETRIEVE_STATUS: + ch = mms_retrieve_status_to_cstr(val); + break; + case MMS_HEADER_STATUS: + ch = mms_status_to_cstr(val); + break; + case MMS_HEADER_SENDER_VISIBILITY: + ch = mms_visibility_to_cstr(val); + break; + case MMS_HEADER_MESSAGE_CLASS: + if (ret != WSP_FIELD_VALUE_NUL_STRING) + ch = mms_message_class_to_cstr(val); + break; + + case MMS_HEADER_DATE: /* Date values. */ + parse_skip(context, -1); + decoded = wsp_unpack_date_value(context); + break; + + case MMS_HEADER_MESSAGE_SIZE: + parse_skip(context, -1); + decoded = wsp_unpack_integer_value(context); + break; + + case MMS_HEADER_CONTENT_TYPE: + if (ret == WSP_FIELD_VALUE_ENCODED) + ch = wsp_content_type_to_cstr(val); + else if (ret == WSP_FIELD_VALUE_DATA) + decoded = wsp_unpack_accept_general_form(context); + + break; + + case MMS_HEADER_DELIVERY_TIME: + case MMS_HEADER_EXPIRY: + case MMS_HEADER_REPLY_CHARGING_DEADLINE: + + + if (ret != WSP_FIELD_VALUE_DATA) + warning(0, "Error in value format, field %s!", + hname); + else { + int n = parse_get_char(context); + + if (n == 0x80) /* Absolute time */ + decoded = wsp_unpack_date_value(context); + else if (n == 0x81) { /* Relative time. */ + long l = 0; + time_t t = time(NULL); + Octstr *s = wsp_unpack_integer_value(context); + + octstr_parse_long(&l, s, 0, 10); + octstr_destroy(s); + + t += l; + decoded = date_format_http(t); + } else + decoded = date_format_http(time(NULL)); /* A default. */ + } + break; + case MMS_HEADER_FROM: + if (ret != WSP_FIELD_VALUE_DATA) + warning(0, "Error in value format, field %s!",hname); + + else { + int n = parse_get_char(context); + + if (n == 0x80) { /* Address present. */ + int val = 0; + int ret2 = wsp_field_value(context, &val); + + if (ret2 == WSP_FIELD_VALUE_DATA) { /* expect charset text. */ + long charset; /* Get it and ignore it. */ + wsp_secondary_field_value(context, &charset); + decoded = parse_get_nul_string(context); + parse_skip_to_limit(context); + parse_pop_limit(context); + } else if (ret2 != WSP_FIELD_VALUE_NUL_STRING) { + warning(0, "Faulty header value for %s!\n", hname); + decoded = octstr_imm(""); + } else + decoded = parse_get_nul_string(context); + if (mms_validate_address(decoded)) + warning(0, "Faulty address [%s] format in field %s!", + octstr_get_cstr(decoded), hname); + } else /* Insert address. */ + decoded = octstr_duplicate(xfrom); + } + break; + case MMS_HEADER_PREVIOUSLY_SENT_BY: + case MMS_HEADER_PREVIOUSLY_SENT_DATE: + if (ret != WSP_FIELD_VALUE_DATA) + warning(0, "Error in value format, field %s!",hname); + else { + Octstr *t; + decoded = wsp_unpack_integer_value(context); + + if (field_type == MMS_HEADER_PREVIOUSLY_SENT_BY) { + int val = 0; + int ret2 = wsp_field_value(context, &val); + + if (ret2 == WSP_FIELD_VALUE_DATA) { /* expect charset text. */ + long charset; /* Get it and ignore it. */ + wsp_secondary_field_value(context, &charset); + t = parse_get_nul_string(context); + parse_skip_to_limit(context); + parse_pop_limit(context); + } else if (ret2 != WSP_FIELD_VALUE_NUL_STRING) { + warning(0, "Faulty header value for %s!\n", hname); + t = octstr_imm(""); + } else + t = parse_get_nul_string(context); + } else + t = wsp_unpack_date_value(context); + + octstr_append(decoded, t); + octstr_destroy(t); + } + break; + default: + warning(0, "MMS: Unknown header with code 0x%02x!", field_type); + } + + if (ret == WSP_FIELD_VALUE_DATA) { + parse_skip_to_limit(context); + parse_pop_limit(context); + } + + if (ch == NULL && decoded != NULL) + ch = octstr_get_cstr(decoded); + + if (ch == NULL) + goto value_error; + + if (!hname) { + warning(0, "Unknown header number 0x%02x.", field_type); + goto value_error; + } + + http_header_add(unpacked, hname, ch); + if (decoded) octstr_destroy(decoded); + return; + + value_error: + warning(0, "Skipping faulty header [code = %d, val=%d]!", field_type, val); + octstr_destroy(decoded); +} + +static int decode_msgheaders(ParseContext *context, MmsMsg *msg, Octstr *from) +{ + int fcont = 1; + + gw_assert(msg->headers == NULL); + msg->headers = list_create(); + + mms_strings_init(); /* Just in case. */ + + while (fcont && parse_octets_left(context) && + !parse_error(context)) { + int byte = parse_get_char(context); + + + if (byte >= 128) + mms_unpack_well_known_field(msg->headers, byte&0x7f, context, from); + else { + parse_skip(context, -1); /* Go back a bit. */ + wsp_unpack_app_header(msg->headers, context); + } + if ((byte&0x7f) == MMS_HEADER_CONTENT_TYPE) + fcont = 0; + } + + return 0; +} + + +static void mms_pack_well_known_field(Octstr *os, int field_type, Octstr *value) +{ + Octstr *encoded = octstr_create(""); + int ch = 0; + unsigned char c; + + switch (field_type) { + + case MMS_HEADER_TO: + case MMS_HEADER_CC: + case MMS_HEADER_BCC: + case MMS_HEADER_SUBJECT: + case MMS_HEADER_TRANSACTION_ID: + case MMS_HEADER_CONTENT_LOCATION: + case MMS_HEADER_MESSAGE_ID: + case MMS_HEADER_REPLY_CHARGING_ID: + case MMS_HEADER_RESPONSE_TEXT: + case MMS_HEADER_RETRIEVE_TEXT: + wsp_pack_text(os, value); + break; + case MMS_HEADER_MMS_VERSION: + wsp_pack_version_value(os, value); + break; + /* integer/octet values: Need to stream line this with better error-checking */ + case MMS_HEADER_DELIVERY_REPORT: + case MMS_HEADER_REPORT_ALLOWED: + case MMS_HEADER_READ_REPORT: + pack_short_integer(os, mms_string_to_reports(value)); + break; + case MMS_HEADER_MESSAGE_TYPE: + pack_short_integer(os, mms_string_to_message_type(value)); + break; + case MMS_HEADER_PRIORITY: + pack_short_integer(os, mms_string_to_priority(value)); + break; + case MMS_HEADER_READ_STATUS: + pack_short_integer(os, mms_string_to_read_status(value)); + break; + case MMS_HEADER_REPLY_CHARGING: + pack_short_integer(os, mms_string_to_reply_charging(value)); + break; + + case MMS_HEADER_RESPONSE_STATUS: + pack_short_integer(os, mms_string_to_response_status(value)); + break; + case MMS_HEADER_RETRIEVE_STATUS: + pack_short_integer(os, mms_string_to_retrieve_status(value)); + break; + case MMS_HEADER_STATUS: + pack_short_integer(os, mms_string_to_status(value)); + break; + case MMS_HEADER_SENDER_VISIBILITY: + pack_short_integer(os, mms_string_to_visibility(value)); + break; + case MMS_HEADER_MESSAGE_CLASS: + ch = mms_string_to_message_class(value); + if (ch < 0) + wsp_pack_text(os, value); + else + pack_short_integer(os, ch); + break; + + case MMS_HEADER_DATE: /* Date values. */ + wsp_pack_date(os, value); + break; + + case MMS_HEADER_MESSAGE_SIZE: + wsp_pack_integer_string(os, value); + break; + + case MMS_HEADER_CONTENT_TYPE: + wsp_pack_content_type(os, value); + break; + + case MMS_HEADER_DELIVERY_TIME: + case MMS_HEADER_EXPIRY: + case MMS_HEADER_REPLY_CHARGING_DEADLINE: + + if (octstr_isnum(value) == 1) { /* A delta value. */ + + long l; + + sscanf(octstr_get_cstr(value), "%ld", &l); + c = 129; + octstr_append_char(encoded, c); + wsp_pack_long_integer(encoded, l); + } else { /* Must be a date value .*/ + c = 128; + octstr_append_char(encoded, c); + wsp_pack_date(encoded, value); + } + + wsp_pack_value(os, encoded); + + break; + case MMS_HEADER_FROM: + + if (octstr_compare(octstr_imm("#insert"), value) == 0) { + c = 129; + octstr_append_data(encoded, &c, 1); + } else { + c = 128; + octstr_append_data(encoded, &c, 1); + wsp_pack_text(encoded, value); + } + wsp_pack_value(os, encoded); + + break; + case MMS_HEADER_PREVIOUSLY_SENT_BY: + case MMS_HEADER_PREVIOUSLY_SENT_DATE: + { + long i, l; + Octstr *s; + + i = octstr_parse_long(&l, value, 0, 10); + + if (i <0) { + warning(0, "Bad counter indicator for field!"); + i = 0; + } + + wsp_pack_integer_value(encoded, l); + s = octstr_copy(value, i, octstr_len(value)); + + if (field_type == MMS_HEADER_PREVIOUSLY_SENT_BY) + wsp_pack_text(encoded, s); + else + wsp_pack_date(encoded, s); + + octstr_destroy(s); + wsp_pack_value(os, encoded); + } + break; + default: + warning(0, "MMS: Unknown header with code 0x%02x!", field_type); + } + + if (encoded) octstr_destroy(encoded); + return; +} + +static int encode_msgheaders(Octstr *os, MmsMsg *msg) +{ + int fcont = 1; + List *hdrs = msg->headers; + int i, l = list_len(hdrs), mtype; + + Octstr *msgtype = NULL, *transid = NULL, *version = NULL, *ctype; + + /* First ensure that top headers are in place. */ + + version = http_header_value(hdrs, + octstr_imm("X-Mms-MMS-Version")); + transid = http_header_value(hdrs, + octstr_imm("X-Mms-Transaction-Id")); + msgtype = http_header_value(hdrs, + octstr_imm("X-Mms-Message-Type")); + + ctype = http_header_value(hdrs, + octstr_imm("Content-Type")); + + mtype = mms_string_to_message_type(msgtype); + + pack_short_integer(os, MMS_HEADER_MESSAGE_TYPE); + pack_short_integer(os, mtype); + octstr_destroy(msgtype); + + if (transid) { + pack_short_integer(os, MMS_HEADER_TRANSACTION_ID); + wsp_pack_text(os, transid); + octstr_destroy(transid); + } + pack_short_integer(os, MMS_HEADER_MMS_VERSION); + wsp_pack_version_value(os, version); + octstr_destroy(version); + + /* Now pack the rest. */ + for (i = 0; fcont && i < l; i++) { + Octstr *field = NULL, *value = NULL; + int htype; + + http_header_get(hdrs, i, &field, &value); + + htype = mms_string_to_header(field); + + if (htype == MMS_HEADER_MMS_VERSION || + htype == MMS_HEADER_MESSAGE_TYPE || + htype == MMS_HEADER_TRANSACTION_ID || + htype == MMS_HEADER_CONTENT_TYPE) + goto loop1; + + if (htype < 0) + wsp_pack_application_header(os, field, value); + else { + pack_short_integer(os, htype); + mms_pack_well_known_field(os, htype, value); + } + loop1: + if (field) octstr_destroy(field); + if (value) octstr_destroy(value); + } + + if (ctype) { + pack_short_integer(os, MMS_HEADER_CONTENT_TYPE); + wsp_pack_content_type(os, ctype); + octstr_destroy(ctype); + } else if (mtype == MMS_MSGTYPE_SEND_REQ || + mtype == MMS_MSGTYPE_RETRIEVE_CONF) + warning(0, "MMS: Content type missing in encode_headers!"); + + return 0; + +} + +/* Does basic fixups on a message. */ +static int fixup_msg(MmsMsg *m, Octstr *from) +{ + if (!m) + return -1; + + if (m->message_type == MMS_MSGTYPE_SEND_REQ) { + Octstr *s = NULL; + + /* Check for from. */ + if (from && (s = http_header_value(m->headers, octstr_imm("From"))) == NULL) + http_header_add(m->headers, "From", octstr_get_cstr(from)); + else if (s) + octstr_destroy(s); + + /* Check for date. */ + + if ((s = http_header_value(m->headers, octstr_imm("Date"))) == NULL) { + Octstr *t = date_format_http(time(NULL)); + http_header_add(m->headers, "Date", octstr_get_cstr(t)); + octstr_destroy(t); + } else + octstr_destroy(s); + + } + + return 0; +} +MmsMsg *mms_frombinary(Octstr *msg, Octstr *from) +{ + int res = 0; + MmsMsg _m = {0}, *m = NULL; + ParseContext *p = parse_context_create(msg); + + Octstr *s; + + decode_msgheaders(p, &_m, from); + + if (_m.headers == NULL || + list_len(_m.headers) == 0) + goto done; + + /* Get the message type and also set flag for whether multipart.*/ + + s = http_header_value(_m.headers, octstr_imm("Content-Type")); + if (s && + octstr_search(s, octstr_imm("application/vnd.wap.multipart"), 0) == 0) + _m.ismultipart = 1; + if (s) + octstr_destroy(s); + + s = http_header_value(_m.headers, octstr_imm("X-Mms-Message-Type")); + if (s) { + _m.message_type = mms_string_to_message_type(s); + octstr_destroy(s); + } else { + if (_m.headers) http_destroy_headers(_m.headers); + goto done; + } + s = http_header_value(_m.headers, octstr_imm("Message-ID")); + _m.msgId = s; + + if ((res = decode_msgbody(p, &_m)) < 0) { + MmsMsg *msg = &_m; + if (msg->ismultipart && msg->body.l) + list_destroy(msg->body.l, (list_item_destructor_t *)mm_destroy); + else if (msg->body.s) + octstr_destroy(msg->body.s); + } + + m = gw_malloc(sizeof (*m)); + *m = _m; + + fixup_msg(m, from); + + /* XXXX better error checking needed. */ + done: + parse_context_destroy(p); + + return m; +} + +static void _x_mime_entity_dump(MIMEEntity *x, int level, int headers_only) +{ + int i, n, ism; + + ism = (x->multiparts && list_len(x->multiparts) > 0) ? 1 : 0; + debug("part.dump", 0, "%sMultipart -> ", ism ? "" : "Not "); + http_header_dump(x->headers); + + if (ism) + for (i = 0, n = list_len(x->multiparts); imultiparts, i), level+1, headers_only); + else if (x->body && !headers_only) + octstr_dump(x->body, level); +} + +void mms_msgdump(MmsMsg *m, int headers_only) +{ + int i, n; + + http_header_dump(m->headers); + + debug("mms.dump", 0, "Dumping MMS message body (%s) [%ld parts] --> ", + m->ismultipart ? "mulitpart" : "not multipart", + m->ismultipart ? list_len(m->body.l) : 0); + + if (m->ismultipart) + for (i = 0, n = list_len(m->body.l); i< n; i++) { + MIMEEntity *x = list_get(m->body.l, i); + debug("mms.dump", 0, "--->Message part: %d --->", i); + + _x_mime_entity_dump(x,0,headers_only); + } + else if (!headers_only) + octstr_dump(m->body.s, 0); + +} + +Octstr *mms_tobinary(MmsMsg *msg) +{ + Octstr *s = octstr_create(""); + + encode_msgheaders(s, msg); + + if (msg->body.s) + encode_msgbody(s, msg); + return s; +} + +int mms_messagetype(MmsMsg *msg) +{ + return msg->message_type; +} + + +static void convert_mime_msg(MIMEEntity *m) +{ + int i, n; + Octstr *content_type, *params; + char *s = NULL; + + get_content_type(m->headers, &content_type, ¶ms); + + if (content_type) { + if (octstr_str_compare(content_type, + "application/vnd.wap.multipart.related") == 0) + s = "multipart/related"; + else if (octstr_str_compare(content_type, + "application/vnd.wap.multipart.alternative") == 0) + s = "multipart/alternative"; + else if (octstr_str_compare(content_type, + "application/vnd.wap.multipart.mixed") == 0) + s = "multipart/mixed"; + octstr_destroy(content_type); + } + if (s) { + Octstr *value; + + value = (params && octstr_len(params) > 0) ? + octstr_format("%s; %S", s, params) : octstr_create(s); + + http_header_remove_all(m->headers, "Content-Type"); + http_header_add(m->headers, "Content-Type", octstr_get_cstr(value)); + + + octstr_destroy(value); + } + + if (params) + octstr_destroy(params); + if (m->multiparts) + for (i = 0, n = list_len(m->multiparts); i < n; i++) + convert_mime_msg(list_get(m->multiparts, i)); + +} + +static void unconvert_mime_msg(MIMEEntity *m) +{ + int i, n; + Octstr *content_type, *params; + char *s = NULL; + + get_content_type(m->headers, &content_type, ¶ms); + + if (content_type) { + if (octstr_case_compare(content_type, + octstr_imm("multipart/related")) == 0) + s = "application/vnd.wap.multipart.related"; + else if (octstr_case_compare(content_type, + octstr_imm("multipart/alternative")) == 0) + s = "application/vnd.wap.multipart.alternative"; + else if (octstr_case_compare(content_type, + octstr_imm("multipart/mixed")) == 0) + s = "application/vnd.wap.multipart.mixed"; + octstr_destroy(content_type); + } + if (s) { + Octstr *value; + if (params) { + List *h = get_value_parameters(params); + Octstr *ps; + http_header_remove_all(h,"boundary"); /* We don't need the boundary param if it is there. */ + ps = make_value_parameters(h); + + value = octstr_format("%s%s%S", s, + (ps && octstr_len(ps) > 0) ? "; " : "", + ps); + octstr_destroy(ps); + http_destroy_headers(h); + } else + value = octstr_create(s); + + http_header_remove_all(m->headers, "Content-Type"); + http_header_add(m->headers, "Content-Type", octstr_get_cstr(value)); + + octstr_destroy(value); + } + + if (params) + octstr_destroy(params); + + if (m->multiparts && list_len(m->multiparts) > 0) + for (i = 0, n = list_len(m->multiparts); i < n; i++) + unconvert_mime_msg(list_get(m->multiparts, i)); + +} + +static MIMEEntity *mime_entity_duplicate(MIMEEntity *m) +{ + MIMEEntity *mx = gw_malloc(sizeof *mx); + + mx->headers = http_header_duplicate(m->headers); + if (m->multiparts && list_len(m->multiparts) > 0) { + int i, n; + mx->multiparts = list_create(); + for (i = 0, n = list_len(m->multiparts); i < n; i++) { + MIMEEntity *x = mime_entity_duplicate(list_get(m->multiparts, i)); + list_append(mx->multiparts, x); + } + mx->body = NULL; + } else { + mx->body = m->body ? octstr_duplicate(m->body) : NULL; + mx->multiparts = NULL; + } + mx->start = NULL; + + return mx; +} + +MIMEEntity *mms_tomime(MmsMsg *msg) +{ + MIMEEntity *m = gw_malloc(sizeof *m); + int i, n; + + memset(m, 0, sizeof *m); + m->body = NULL; + m->multiparts = NULL; + m->start = NULL; + m->headers = http_header_duplicate(msg->headers); + + if (!msg->ismultipart) + m->body = msg->body.s ? octstr_duplicate(msg->body.s) : NULL; + else { + m->multiparts = list_create(); + for (i = 0, n = list_len(msg->body.l); i < n; i++) { + MIMEEntity *mx = mime_entity_duplicate(list_get(msg->body.l, i)); + list_append(m->multiparts, mx); + } + } + convert_mime_msg(m); + + return m; +} + +MmsMsg *mms_frommime(MIMEEntity *mime) +{ + MmsMsg *m = gw_malloc(sizeof *m); + Octstr *s; + MIMEEntity *mx; + + memset(m, 0, sizeof *m); + + mx = mime_entity_duplicate(mime); + unconvert_mime_msg(mx); /* Fix-up content type issues. */ + unpack_mimeheaders(mx); + unbase64_mimeparts(mx); + + m->headers = mx->headers; + + if (mx->multiparts && list_len(mx->multiparts) > 0) { + m->ismultipart = 1; + m->body.l = mx->multiparts; + } else { + m->ismultipart = 0; + m->body.s = mx->body ? mx->body : octstr_imm(""); + } + + gw_free(mx); /* Because all its bits are used above. XXX not very clean! */ + /* Now check for important headers. If missing, put them in */ + m->msgId = http_header_value(m->headers, octstr_imm("Message-ID")); + + /* Default type is send */ + if ((s = http_header_value(m->headers, octstr_imm("X-Mms-Message-Type"))) == NULL) { + http_header_add(m->headers, "X-Mms-Message-Type", + mms_message_type_to_cstr(MMS_MSGTYPE_SEND_REQ)); + m->message_type = MMS_MSGTYPE_SEND_REQ; + } else { + m->message_type = mms_string_to_message_type(s); + if (m->message_type < 0) { + error(0, "Unknown message type: %s while parsing mime entity.", octstr_get_cstr(s)); + octstr_destroy(s); + goto failed; + } + octstr_destroy(s); + } + + if ((s = http_header_value(m->headers, octstr_imm("X-Mms-MMS-Version"))) == NULL) + http_header_add(m->headers, "X-Mms-MMS-Version", MMS_VERSION); + else + octstr_destroy(s); + + /* Fix-up date string: Put it in GMT format, since it might not be. */ + if ((s = http_header_value(m->headers, octstr_imm("Date"))) != NULL) { + struct tm xtm, ytm, *tm; + time_t t = time(NULL), t2; + char buf[64], *p, *q; + + http_header_remove_all(m->headers, "Date"); + localtime_r(&t, &xtm); /* Initialise it. */ + + strptime(octstr_get_cstr(s), "%a, %d %b %Y %T %z", &xtm); /* Parse date value with time zone. */ + + t2 = gw_mktime(&xtm); /* Convert to unix time... */ + tm = gmtime_r(&t2, &ytm); /* Then convert to GM time. */ + + if (!tm || asctime_r(tm, buf) == NULL) /* Then convert to ascii. If that fails...*/ + ctime_r(&t, buf); /* .. just use current time. */ + + /* Strip leading and trailing blanks. */ + for (p = buf; *p && p < buf + sizeof buf; p++) + if (!isspace(*p)) + break; + q = p + (strlen(p) - 1); + while (isspace(*q) && q > p) + *q-- = 0; + + http_header_add(m->headers, "Date", p); + octstr_destroy(s); + + + } + /* XXXX Probably ought to handle some more headers here: + * Return-Receipt-To becomes Read request is yes + * Disposition-Notification-To: becomes X-Mms-Delivery-Report = Yes + */ + + /* XXXX Also need to validate this message a bit better. */ + fixup_msg(m, octstr_imm("anon@unknown")); + + return m; + + failed: + mms_destroy(m); + return NULL; +} + +static void mm_destroy(MIMEEntity *mx) +{ + http_destroy_headers(mx->headers); + if (mx->body) + octstr_destroy(mx->body); + else if (mx->multiparts) + list_destroy(mx->multiparts, (list_item_destructor_t *)mm_destroy); + gw_free(mx); +} + +void mms_destroy(MmsMsg *msg) +{ + + if (msg->ismultipart) + list_destroy(msg->body.l, (list_item_destructor_t *)mm_destroy); + else if (msg->body.s) + octstr_destroy(msg->body.s); + http_destroy_headers(msg->headers); + octstr_destroy(msg->msgId); + gw_free(msg); +} + +List *mms_message_headers(MmsMsg *msg) +{ + + return http_header_duplicate(msg->headers); +} + +MmsMsg *mms_deliveryreport(Octstr *msgid, Octstr *to, time_t date, Octstr *status) +{ + + MmsMsg *m = gw_malloc(sizeof *m); + Octstr *s; + + + m->ismultipart = 0; + m->headers = http_create_empty_headers(); + + m->message_type = MMS_MSGTYPE_DELIVERY_IND; + m->body.s = NULL; + m->msgId = octstr_duplicate(msgid); + + /* Now append headers. */ + + http_header_add(m->headers, "X-Mms-Message-Type", "m-delivery-ind"); + http_header_add(m->headers, "X-Mms-MMS-Version", MMS_VERSION); + http_header_add(m->headers, "Message-ID", octstr_get_cstr(msgid)); + http_header_add(m->headers, "To", octstr_get_cstr(to)); + + s = date_format_http(date); + http_header_add(m->headers, "Date", octstr_get_cstr(s)); + + http_header_add(m->headers, "X-Mms-Status", octstr_get_cstr(status)); + + octstr_destroy(s); + + return m; +} + +MmsMsg *mms_notification(MmsMsg *msg, unsigned int msize, Octstr *url, + Octstr *transactionid, time_t expiryt) +{ + MmsMsg *m = gw_malloc(sizeof *m); + char buf[10]; + + time_t tnow = time(NULL); + + m->ismultipart = 0; + m->msgId = NULL; + m->body.s = NULL; + m->headers = http_create_empty_headers(); + m->message_type = MMS_MSGTYPE_NOTIFICATION_IND; + + http_header_add(m->headers, "X-Mms-Message-Type", "m-notification-ind"); + http_header_add(m->headers, "X-Mms-Transaction-ID", + octstr_get_cstr(transactionid)); + http_header_add(m->headers, "X-Mms-MMS-Version", MMS_VERSION); + + +#define HX(h,d) do {\ + Octstr *s = http_header_value(msg->headers, octstr_imm(#h)); \ + if (s) { \ + http_header_add(m->headers, #h, octstr_get_cstr(s)); \ + octstr_destroy(s); \ + } else if (d) \ + http_header_add(m->headers, #h, d); \ + } while(0) + + HX(From,NULL); + HX(Subject,NULL); + HX(X-Mms-Message-Class, "Personal"); +#undef HX + + sprintf(buf, "%d", msize); + http_header_add(m->headers, "X-Mms-Message-Size", buf); + +#define LARGET 365*24*3600 + + sprintf(buf, "%ld", expiryt ? expiryt - tnow : LARGET); + http_header_add(m->headers, "X-Mms-Expiry", buf); + + + /* No reply charge stuff for now. */ + + http_header_add(m->headers, "X-Mms-Content-Location", octstr_get_cstr(url)); + + return m; +} + +MmsMsg *mms_retrieveconf(MmsMsg *msg, Octstr *transactionid, + char *err, char *errtxt, Octstr *opt_from) +{ + MmsMsg *m; + + + if (msg) { + if (msg->message_type == MMS_MSGTYPE_RETRIEVE_CONF) + return msg; /* Unchanged. XXX should we copy it instead?!! */ + + if (msg->message_type != MMS_MSGTYPE_SEND_REQ) + return NULL; + } + + m = gw_malloc(sizeof *m); + m->msgId = msg ? octstr_duplicate(msg->msgId) : NULL; + + m->headers = http_create_empty_headers(); + m->message_type = MMS_MSGTYPE_RETRIEVE_CONF; + + http_header_add(m->headers, "X-Mms-Message-Type", "m-retrieve-conf"); + if (transactionid) + http_header_add(m->headers, "X-Mms-Transaction-ID", + octstr_get_cstr(transactionid)); + http_header_add(m->headers, "X-Mms-MMS-Version", MMS_VERSION); + + if (!msg) { + Octstr *x = date_format_http(time(NULL)); + + m->ismultipart = 0; + + http_header_add(m->headers, "Date", octstr_get_cstr(x)); + http_header_add(m->headers, "X-Mms-Retrieve-Status",err); + if (err) + http_header_add(m->headers, "X-Mms-Retrieve-Text",err); + if (opt_from) + http_header_add(m->headers, "From", octstr_get_cstr(opt_from)); + http_header_add(m->headers, "Content-Type", "text/plain"); + + if (errtxt) + m->body.s = octstr_create(errtxt); + else + m->body.s = octstr_create(" "); + + octstr_destroy(x); + } else { /* Otherwise copy from message. */ + List *h = mms_message_headers(msg); + int i, n; + +#if 0 /* Some phones do not like this header! */ + http_header_add(m->headers, "X-Mms-Retrieve-Status", "Ok"); +#endif + + http_header_combine(h, m->headers); + http_destroy_headers(m->headers); + m->headers = h; + + m->ismultipart = msg->ismultipart; + + if (!m->ismultipart) + m->body.s = octstr_duplicate(msg->body.s); + else + /* Body is a list of MIMEEntities, so recreate it. */ + + for (m->body.l = list_create(), i = 0, + n = list_len(msg->body.l); + ibody.l, + mime_entity_duplicate(list_get(msg->body.l, i))); + } + return m; +} + +int mms_remove_headers(MmsMsg *m, char *name) +{ + http_header_remove_all(m->headers, name); + + return 0; +} + +MmsMsg *mms_sendconf(char *errstr, char *msgid, char *transid, int isforward) +{ + MmsMsg *m = gw_malloc(sizeof *m); + + + m->ismultipart = 0; + m->msgId = msgid ? octstr_create(msgid) : NULL; + m->body.s = NULL; + + m->headers = http_create_empty_headers(); + + if (!isforward) { + m->message_type = MMS_MSGTYPE_SEND_CONF; + http_header_add(m->headers, "X-Mms-Message-Type", "m-send-conf"); + } else { + m->message_type = MMS_MSGTYPE_FORWARD_CONF; + http_header_add(m->headers, "X-Mms-Message-Type", "m-forward-conf"); + } + http_header_add(m->headers, "X-Mms-Transaction-ID", transid); + http_header_add(m->headers, "X-Mms-MMS-Version", MMS_VERSION); + + http_header_add(m->headers, "X-Mms-Response-Status", errstr); + + if (msgid) + http_header_add(m->headers, "Message-ID", msgid); + + return m; +} + + +int mms_replace_header_value(MmsMsg *msg, char *hname, char *value) +{ + + http_header_remove_all(msg->headers, hname); + + http_header_add(msg->headers, hname, value); + return 0; +} + +Octstr *mms_get_header_value(MmsMsg *msg, Octstr *header) +{ + + return http_header_value(msg->headers, header); + +} + + +int mms_convert_readrec2readorig(MmsMsg *msg) +{ + if (msg->message_type != MMS_MSGTYPE_READ_REC_IND) + return -1; + + mms_replace_header_value(msg, "X-Mms-Message-Type", "m-read-orig-ind"); + msg->message_type = MMS_MSGTYPE_READ_ORIG_IND; + return 0; +} diff --git a/mbuni/mmlib/mms_msg.h b/mbuni/mmlib/mms_msg.h new file mode 100644 index 0000000..86684bb --- /dev/null +++ b/mbuni/mmlib/mms_msg.h @@ -0,0 +1,57 @@ +#ifndef __MMS_MSG_INCLUDED__ +#define __MMS_MSG_INCLUDED__ +#include "gwlib/gwlib.h" +#include "wap/wsp_headers.h" +#include "gwlib/mime.h" + + +#include "mms_strings.h" + +typedef struct MmsMsg MmsMsg; /* Opaque type. */ + +extern int mms_validate_address(Octstr *s); + +/* + * mms_frombinary: Parse MMS binary representation, return a Message or NULL. + * errors are reported in errors string. + * Takes from param which is put in if insert-address is requested. + */ +extern MmsMsg *mms_frombinary(Octstr *msg, Octstr *from); + +/* + * mms_tobinary: Return binary encoded Mms message + */ +extern Octstr *mms_tobinary(MmsMsg *msg); + +extern int mms_messagetype(MmsMsg *msg); + +/* + * Convert Mms Message to standard Mime entity. + * Does not recode any of the body parts. + */ +extern MIMEEntity *mms_tomime(MmsMsg *msg); +extern MmsMsg *mms_frommime(MIMEEntity *mime); + +extern void mms_msgdump(MmsMsg *m, int headers_only); +extern void mms_destroy(MmsMsg *msg); + +/* Make a delivery report message. */ +extern MmsMsg *mms_deliveryreport(Octstr *msgid, Octstr *to, time_t date, Octstr *status); + +/* Return message headers. */ +extern List *mms_message_headers(MmsMsg *msg); + +/* Make a notification message out of this one and the url given. */ +extern MmsMsg *mms_notification(MmsMsg *msg, unsigned int msize, + Octstr *url, + Octstr *transactionid, time_t expiryt); + +MmsMsg *mms_retrieveconf(MmsMsg *msg, Octstr *transactionid, char *err, char *errtxt, Octstr *opt_from); +int mms_remove_headers(MmsMsg *m, char *name); +MmsMsg *mms_sendconf(char *errstr, char *msgid, char *transid, int isforward); +Octstr *mms_get_header_value(MmsMsg *msg, Octstr *header); +int mms_replace_header_value(MmsMsg *msg, char *hname, char *value); + +int mms_convert_readrec2readorig(MmsMsg *msg); + +#endif diff --git a/mbuni/mmlib/mms_queue.c b/mbuni/mmlib/mms_queue.c new file mode 100644 index 0000000..2df64da --- /dev/null +++ b/mbuni/mmlib/mms_queue.c @@ -0,0 +1,797 @@ +/* + * MMS Queue handler functions - P. A. Bagyenda + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "mms_queue.h" +#include "gwlib/log.h" +#include "gwlib/accesslog.h" + +#define MQF 'q' +#define MDF 'd' +#define MTF 't' + + +static int free_envelope(MmsEnvelope *e, int removefromqueue); +/* + * lockfile: tries to lock a file, returns 0 if success, errno (which could be +ve) otherwise. + * we use flock() + */ +static int lockfile(int fd, int shouldblock) +{ + int n, stop; + unsigned flg = shouldblock ? 0 : LOCK_NB; + + do { + n = flock(fd, LOCK_EX|flg); + if (n < 0) { + if (errno == EINTR) + stop = 0; + else + stop = 1; + } else + stop = 1; + } while (!stop); + + return (n == 0) ? 0 : errno; +} + +static int check_lock(int fd, char *fname) +{ + struct stat fs = {0}, ds = {0}; + + + if (fstat(fd, &ds) < 0 || + stat(fname, &fs) < 0 || + + ds.st_nlink != fs.st_nlink || + memcmp(&ds.st_dev,&fs.st_dev, sizeof ds.st_dev) != 0 || + memcmp(&ds.st_ino,&fs.st_ino, sizeof ds.st_ino) != 0 || + ds.st_uid != fs.st_uid || + ds.st_gid != fs.st_gid || + ds.st_size != fs.st_size) + return -1; + else + return 0; +} +/* Queue file structure: + * - File consists of a series of lines, each line begins with a single letter, followed by + * a parameter. Letters mean: + * T - message type (full text string -- MMS message type. + * I - message ID + * F - From address + * R - Recipient (the ones pending) for this message + * C - Time queue entry was created + * L - Time of last delivery attempt + * D - Time of (next) delivery attempt + * X - Time of expiry of message + * N - Number of delivery attempts so far + * P - Proxy who sent it to us + * V - Proxy through which this message shd be delivered (e.g. delivery report) + * S - Message size + * s - Message subject. + * f - time of last content fetch + * t - user defined token. + * b - billed amount. + * r - whether delivery receipts are required or not. + * M - Application specific data (string) + */ + + +static int _putline(int fd, char *code, char buf[]) +{ + Octstr *s = octstr_format("%s%s\n", code, buf); + int res; + + res = octstr_write_to_socket(fd, s); + octstr_destroy(s); + return res; +} + + +/* + * Attempt to read an envelope from queue file: + * - opens and locks the file. + * - if the lock succeeds, check that file hasn't changed since opening. If it has + * return NULL (i.e. file is being processed elsewhere -- race condition), otherwise read it. + * - If should block is 1, then does a potentially blocking attempt to lock the file. + */ +MmsEnvelope *mms_queue_readenvelope(char *qf, char *mms_queuedir, int shouldblock) +{ + Octstr *fname; + int fd; + Octstr *qdata, *s; + ParseContext *p; + MmsEnvelope *e; + int okfile = 0; + + fname = octstr_format( "%.128s/%s", mms_queuedir, qf); + + if ((fd = open(octstr_get_cstr(fname), O_RDONLY)) < 0) { + octstr_destroy(fname); + return NULL; + } + + if (lockfile(fd, shouldblock) != 0 || + check_lock(fd, octstr_get_cstr(fname)) != 0) { + close(fd); + octstr_destroy(fname); + return NULL; + } + + + e = gw_malloc(sizeof *e); + memset(e, 0, sizeof *e); /* Clear it all .*/ + + e->to = list_create(); + e->qf.fd = fd; + strncpy(e->qf.name, qf, sizeof e->qf.name); + strncpy(e->qf.dir, mms_queuedir, sizeof e->qf.dir); + + qdata = octstr_read_file(octstr_get_cstr(fname)); + octstr_destroy(fname); + if (qdata == NULL) + qdata = octstr_imm(""); + p = parse_context_create(qdata); + + for (s = parse_get_line(p); s; + s = parse_get_line(p)) { + char *line = octstr_get_cstr(s); + int ch = line[0]; + char *res = line + 1; + + switch (ch) { + Octstr *t; + MmsEnvelopeTo *to; + case 'T': + t = octstr_create(res); + e->msgtype = mms_string_to_message_type(t); + octstr_destroy(t); + if (e->msgtype < 0) { + e->msgtype = 0; + error(0, "mms_queueread: Unknown MMS message type (%s) in file %s/%s, skipped!\n", + res, mms_queuedir, qf); + } + break; + case 'I': + e->msgId = octstr_create(res); + break; + case 'F': + e->from = octstr_create(res); + if (mms_validate_address(e->from) != 0) + error(0, "mms_queueread: Mal-formed address %s in file %s/%s!", res, mms_queuedir, qf); + break; + case 'R': + + t = octstr_create(res); + if (mms_validate_address(t) != 0) + error(0, "mms_queueread: Mal-formed address %s in file %s/%s!", res, mms_queuedir, qf);; + to = gw_malloc(sizeof *to); + to->rcpt = t; + to->process = 1; + list_append(e->to, to); + break; + case 'C': + e->created = atol(res); + break; + case 'L': + e->lasttry = atol(res); + break; + case 'D': + e->sendt = atol(res); + break; + case 'X': + e->expiryt = atol(res); + break; + case 'N': + e->attempts = atol(res); + break; + case 'P': + e->fromproxy = octstr_create(res); + break; + case 'M': + e->mdata = octstr_create(res); + break; + case 'V': + e->viaproxy = octstr_create(res); + break; + case 'S': + e->msize = atol(res); + break; + case 's': + e->subject = octstr_create(res); + break; + case 't': + e->token = octstr_create(res); + break; + case 'f': + e->lastaccess = atol(res); + break; + case 'b': + e->bill.billed = 1; + e->bill.amt = atof(res); + break; + case 'r': + e->dlr = 1; + break; + case '.': + okfile = 1; + break; + default: + error(0, "Unknown QF header %c in file %s/%s!", ch, mms_queuedir, qf); + break; + } + octstr_destroy(s); + if (okfile) + break; /* We are done. */ + } + parse_context_destroy(p); + octstr_destroy(qdata); + + /* We should properly validate the queue file here. */ + if (!okfile) { + free_envelope(e,0); + e = NULL; + error(0, "Corrupt queue control file: %s/%s", mms_queuedir, qf); + } + return e; +} + + +/* Updates envelope to queue file: + * - opens temp file + * - writes output to temp file, if not new else writes directly. + * - renames temp file to queue file (if not new) + * This function doesn't check that this envelope is useless (i.e. no recipients) + * - If function returns -1, caller should check errno for error. + */ +static int writeenvelope(MmsEnvelope *e, int newenv) +{ + Octstr *tfname = NULL; + char *s; + char buf[16]; + int fd; + int i, n; + int res = 0; + + if (newenv) + fd = e->qf.fd; + else { + tfname = octstr_format( + "%s/%c%s", e->qf.dir, + MTF, e->qf.name + 1); + fd = open(octstr_get_cstr(tfname), + O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); + if (fd < 0 ) { + error(0, "mms_queueadd: Failed to open temp file %s: error = %s\n", + octstr_get_cstr(tfname), strerror(errno)); + + res = -1; + goto done; + } else if (lockfile(fd, 0) != 0 || + check_lock(fd, octstr_get_cstr(tfname)) != 0) { /* Lock it. */ + error(0, "mms_queueadd: Failed lock temp file %s: error = %s\n", + octstr_get_cstr(tfname), strerror(errno)); + res = -1; + goto done; + + } + } + + /* Write out. */ + + s = mms_message_type_to_cstr(e->msgtype); + if (!s) { + error(0, "mms_queuewrite: Unknown MMS message type %d! Skipped\n", e->msgtype); + s = ""; + } + _putline(fd, "T", s); + + if (e->msgId) + _putline(fd, "I", octstr_get_cstr(e->msgId)); + + _putline(fd, "F", octstr_get_cstr(e->from)); + + if (e->to) + n = list_len(e->to); + else + n = 0; + + for (i = 0; i < n; i++) { + MmsEnvelopeTo *to = list_get(e->to, i); + + if (to->process) + _putline(fd, "R", octstr_get_cstr(to->rcpt)); + } + + sprintf(buf, "%ld", e->created); + _putline(fd, "C", buf); + + if (e->lasttry) { + sprintf(buf, "%ld", e->lasttry); + _putline(fd, "L", buf); + } + + if (e->sendt) { + sprintf(buf, "%ld", e->sendt); + _putline(fd, "D", buf); + } + + if (e->expiryt) { + sprintf(buf, "%ld", e->expiryt); + _putline(fd, "X", buf); + } + + if (e->attempts) { + sprintf(buf, "%ld", e->attempts); + _putline(fd, "N", buf); + } + + if (e->lastaccess) { + sprintf(buf, "%ld", e->lastaccess); + _putline(fd, "f", buf); + } + + sprintf(buf, "%ld", e->msize); + _putline(fd, "S", buf); + + + if (e->fromproxy) + _putline(fd, "P", octstr_get_cstr(e->fromproxy)); + + + if (e->mdata) + _putline(fd, "M", octstr_get_cstr(e->mdata)); + + if (e->subject) + _putline(fd, "s", octstr_get_cstr(e->subject)); + + + if (e->viaproxy) + _putline(fd, "V", octstr_get_cstr(e->viaproxy)); + + if (e->token) + _putline(fd, "t", octstr_get_cstr(e->token)); + + + if (e->dlr) + _putline(fd, "r", "Yes"); + + if (e->bill.billed) { + sprintf(buf, "%.3f", e->bill.amt); + _putline(fd,"b", buf); + } + + _putline(fd, "", "."); + + fsync(fd); /* Sync data. */ + + if (!newenv) { /* An update */ + Octstr *qfname; + + qfname = octstr_format("%s/%s", e->qf.dir, e->qf.name); + if (rename(octstr_get_cstr(tfname), octstr_get_cstr(qfname)) < 0) { + error(0, "mms_queuewrite: Failed to rename %s to %s: error = %s\n", + octstr_get_cstr(qfname), octstr_get_cstr(tfname), strerror(errno)); + + close(fd); /* Close new one, keep old one. */ + res = -1; + } else { /* On success, new descriptor replaces old one and we close old one. */ + close(e->qf.fd); + e->qf.fd = fd; + } + octstr_destroy(qfname); + } + + done: + if (tfname) octstr_destroy(tfname); + return res; +} + + +#define MAXTRIES 10 + +/* Makes a qf file in the queue directory. + * Makes several attempts then fails (returns -1) if it can't, fd otherwise + * puts queue file name in qf (without directory name). + * It is up to the caller to lock the file descriptor if needed. + */ +static int mkqf(char qf[32], char *mms_queuedir) +{ + Octstr *tmp; + char *ctmp; + int i = 0, fd = -1; + static int ect; + + if (!mms_queuedir) + gw_panic(0, "Queue directory passed as null!"); + + do { + tmp = octstr_format("%.64s/%cf%ld.%d.x%d%ld", + mms_queuedir, MQF, + time(NULL), + ++ect, getpid(), random() % 100); + ctmp = octstr_get_cstr(tmp); + fd = open(ctmp, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); + if (fd >= 0 && + (lockfile(fd,1) != 0 || + check_lock(fd, ctmp) != 0)) { + unlink(ctmp); + close(fd); + fd = -1; + } + + if (fd < 0) { + octstr_destroy(tmp); + tmp = NULL; + } + } while (i++ < MAXTRIES && fd < 0); + + if (fd >= 0) { + char *s = strrchr(ctmp, '/'); + if (s) + strncpy(qf, s + 1, 32); + else /* ???! */ + gw_panic(0, "Queue directory name is too long!\n"); + } + + if (tmp) octstr_destroy(tmp); + return fd; +} + +static int writemmsdata(Octstr *ms, char *df, char *mms_queuedir) +{ + Octstr *dfname; + int fd, n, res = 0; + + + dfname = octstr_format("%s/%s", mms_queuedir, df); + + fd = open(octstr_get_cstr(dfname), + O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); + if (fd < 0) { + error(0, "mms_queuadd: Failed to open data file %s: error = %s\n", + octstr_get_cstr(dfname), strerror(errno)); + res = -1; + goto done; + } + + n = octstr_write_to_socket(fd, ms); + close(fd); + + if (n != 0) { + error(0, "mms_queuadd: Failed to write data file %s: error = %s\n", + octstr_get_cstr(dfname), strerror(errno)); + unlink(octstr_get_cstr(dfname)); + res = -1; + } + + done: + octstr_destroy(dfname); + return res; +} + + +Octstr *mms_queue_add(Octstr *from, List *to, Octstr *msgid, + Octstr *subject, + Octstr *fromproxy, Octstr *viaproxy, + time_t senddate, time_t expirydate, MmsMsg *m, Octstr *token, + int dlr, + char *directory) +{ + char qf[32]; + int fd, i, n; + MmsEnvelope *e; + + Octstr *ms, *res = NULL; + + fd = mkqf(qf, directory); + + if (fd < 0) { + error(0, "mms_queue_add: Failed err=%s\n", strerror(errno)); + return NULL; + } + + res = octstr_create(qf); + ms = mms_tobinary(m); /* Convert message to string. */ + + + e = gw_malloc(sizeof *e); /* Make envelope, clear it. */ + memset(e, 0, sizeof *e); + + strncpy(e->qf.name, qf, sizeof e->qf.name); + strncpy(e->qf.dir, directory, sizeof e->qf.dir); + + e->qf.fd = fd; + + e->msgtype = mms_messagetype(m); + e->from = from; + e->created = time(NULL); + e->sendt = senddate; + e->expiryt = expirydate; + e->lasttry = 0; + e->attempts = 0; + e->lastaccess = 0; + e->fromproxy = fromproxy; + e->viaproxy = viaproxy; + e->subject = subject; + e->to = list_create(); + e->msize = octstr_len(ms); + e->msgId = msgid ? msgid : res; + e->token = token; + e->dlr = dlr; + e->bill.billed = 0; + + /* Insert message ID into message if it is missing. */ + if (!msgid && mms_messagetype(m) == MMS_MSGTYPE_SEND_REQ) + mms_replace_header_value(m, "Message-ID", octstr_get_cstr(res)); + + n = to ? list_len(to) : 0; + + for (i = 0; ircpt = a; + t->process = 1; + + list_append(e->to, t); + } + + /* Write queue data. */ + if (writeenvelope(e, 1) < 0) { + octstr_destroy(res); + res = NULL; + goto done; + } + + + /* Write actual data before relinquishing lock on queue file. */ + + qf[0]= MDF; + + if (writemmsdata(ms, qf, directory) < 0) { + octstr_destroy(res); + res = NULL; + goto done; + } + + close(fd); /* Close queue file, thereby letting go of locks. */ + + + + done: + /* Free the envelope stuff since we do not need it any more, then free e */ + for (i = 0, n = list_len(e->to); ito, i); + gw_free(to); + } + list_destroy(e->to, NULL); + gw_free(e); + octstr_destroy(ms); + + return res; +} + +static int free_envelope(MmsEnvelope *e, int removefromqueue) +{ + int i, n; + + if (e->msgId) + octstr_destroy(e->msgId); + + for (i = 0, n = list_len(e->to); i < n; i++) { + MmsEnvelopeTo *x = list_get(e->to, i); + + octstr_destroy(x->rcpt); + gw_free(x); + } + list_destroy(e->to, NULL); + + if (e->from) + octstr_destroy(e->from); + + if (e->fromproxy) + octstr_destroy(e->fromproxy); + if (e->mdata) + octstr_destroy(e->mdata); + if (e->viaproxy) + octstr_destroy(e->viaproxy); + if (e->token) + octstr_destroy(e->token); + + if (e->subject) + octstr_destroy(e->subject); + + if (removefromqueue) { + char fname[2*QFNAMEMAX]; + + snprintf(fname, -1 + sizeof fname, "%s/%s", e->qf.dir, e->qf.name); + unlink(fname); + e->qf.name[0] = MDF; + snprintf(fname, -1 + sizeof fname, "%s/%s", e->qf.dir, e->qf.name); + unlink(fname); + } + close(e->qf.fd); /* close and unlock now that we have deleted it. */ + + gw_free(e); + + return 0; +} + +int mms_queue_free_env(MmsEnvelope *e) +{ + + return free_envelope(e, 0); +} +int mms_queue_update(MmsEnvelope *e) +{ + int i, n = (e && e->to) ? list_len(e->to) : 0; + int hasrcpt = 0; + MmsEnvelopeTo *x; + + if (!e) return -1; + + for (i = 0; i < n; i++) + if ((x = list_get(e->to, i)) && + x->process) { + hasrcpt = 1; + break; + } + + if (!hasrcpt) { + free_envelope(e,1); + return 1; + } else + return writeenvelope(e, 0); +} + +MmsMsg *mms_queue_getdata(MmsEnvelope *e) +{ + Octstr *fname; + Octstr *ms; + MmsMsg *m; + + if (!e) return NULL; + + fname = octstr_format("%s/%c%s", e->qf.dir, MDF, e->qf.name + 1); + + ms = octstr_read_file(octstr_get_cstr(fname)); + if (!ms) { + error(0, "mms_queue_getdata: Failed to load data file for queue entry %s in %s", + e->qf.name, e->qf.dir); + octstr_destroy(fname); + return NULL; + } + m = mms_frombinary(ms, octstr_imm("")); + if (!m) { + error(0, "mms_queue_getdata: Failed to load decode data file for queue entry %s in %s", + e->qf.name, e->qf.dir); + octstr_destroy(fname); + return NULL; + } + octstr_destroy(ms); + octstr_destroy(fname); + + return m; +} + + +struct Qthread_t { + List *l; + int (*deliver)(MmsEnvelope *e); +}; + +static void tdeliver(struct Qthread_t *qt) +{ + MmsEnvelope *e; + + while ((e = list_consume(qt->l)) != NULL) { + int res = qt->deliver(e); /* If it is on the queue, it has to be delivered. */ + + if (res != 1) /* Then delete as it wasn't deleted. */ + free_envelope(e, 0); + } + /* Consume failed, time to go away. */ + if (qt->l) + list_destroy(qt->l, NULL); + qt->l = NULL; /* Signal that we are gone. */ +} + +void mms_queue_run(char *dir, + int (*deliver)(MmsEnvelope *), + double sleepsecs, int num_threads, int *rstop) +{ + struct Qthread_t *tlist; + int i; + int qstop = 0; + + gw_assert(num_threads>0); + + tlist = gw_malloc(num_threads*sizeof tlist[0]); + + for (i = 0; id_name[0] == MQF && + dp->d_name[1] == 'f') { + MmsEnvelope *e = mms_queue_readenvelope(dp->d_name,dir, 0); + + if (!e) + continue; + + if (e->sendt <= tnow) { + int queued = 0; + int j = i; /* This is the next queue to use. Checking for cycles. */ + do { + if (tlist[i].l) { + debug("queuerun", 0, "Queued to thread %d for %s/%s", + i, dir, dp->d_name); + list_produce(tlist[i].l, e); + queued = 1; + } + i = (i+1)%num_threads; + } while (!queued && i != j); + + if (!queued) { /* A problem. There are no sender threads! */ + free_envelope(e, 0); + qstop = 1; + error(0, "mms_queue_run: No active sender queues for directory %s. Quiting.", + dir); + goto qloop; + } + } else + free_envelope(e,0); /* Let go of it. */ + + } + + if (dirp) closedir(dirp); + qsleep: + gwthread_sleep(sleepsecs); + qloop: + (void)0; + } while (!*rstop && !qstop); + + /* We are out of the queue, time to go away. */ + for (i = 0; i +#include +#include "mms_resolve.h" +#include "mms_util.h" + +static void *mms_resolvermodule_init(char *settings) +{ + return NULL; +} + +static int mms_resolvermodule_fini(void *module_data) +{ + return 0; +} + +static Octstr *mms_resolve(Octstr * phonenum, void *module_data, void *settings_p, void *proxyrelays_p) +{ + /* Most custom implementations of this library will probably just ignore the two last arguments, + * but this one needs them + */ + + MmsBoxSettings *settings = (MmsBoxSettings *) settings_p; + List *proxyrelays = (List *) proxyrelays_p; + int j, m; + + if (does_prefix_match(settings->local_prefix, phonenum)) { + return settings->hostname; + } else if (proxyrelays && list_len(proxyrelays) > 0) /* Step through proxies. */ + for (j = 0, m = list_len(proxyrelays); j < m; j++) { + MmsProxyRelay *mp = list_get(proxyrelays, j); + if (does_prefix_match(mp->allowed_prefix, phonenum) && + !does_prefix_match(mp->denied_prefix, phonenum)) { + return mp->host; + } + } + + return 0; +} + +/* The function itself. */ +MmsResolverFuncStruct mms_resolvefuncs = { + mms_resolvermodule_init, + mms_resolve, + mms_resolvermodule_fini +}; diff --git a/mbuni/mmlib/mms_resolve.h b/mbuni/mmlib/mms_resolve.h new file mode 100644 index 0000000..ae5bb3f --- /dev/null +++ b/mbuni/mmlib/mms_resolve.h @@ -0,0 +1,31 @@ +#ifndef __MMS_RESOLVE_INCLUDED__ +#define __MMS_RESOLVE_INCLUDED__ + +#include +#include "gwlib/gwlib.h" + +/* Resolver module. This file provides prototypes for all resolver functions. + * The idea is that for each site a DSO will be created that the mmsglobalsender loads and gets + * functions to resolve msisdn's to mmsc addresses. If the string returned is the same as our + * hostname, the msisdn is considered local. + */ + +typedef struct MmsRevolverFuncStruct { +/* This function is called once to initialise the resolver module. Return a generic object, + * which is passed with each resolution request.. + */ + void *(*mms_resolvermodule_init)(char *settings); + +/* Looks up the msisdn and returns the hostname of the msisdn's mmsc. If returned mmsc matches + * our hostname, the user is considered local. + * + * Return NULL on error, otherwise an Octstr + */ + Octstr *(*mms_resolve)(Octstr * phonenum, void *module_data, void *settings, void *proxyrelays); + + int (*mms_resolvermodule_fini)(void *module_data); +} MmsResolverFuncStruct; + +extern MmsResolverFuncStruct mms_resolvefuncs; /* The module must expose this symbol. */ + +#endif diff --git a/mbuni/mmlib/mms_strings.c b/mbuni/mmlib/mms_strings.c new file mode 100644 index 0000000..6292d54 --- /dev/null +++ b/mbuni/mmlib/mms_strings.c @@ -0,0 +1,242 @@ +#include "gwlib/gwlib.h" +#include "mms_strings.h" + + +/* mms_strings.c: lookup code for various tables defined by MMS standard + * Adapted from wsp_strings.c + */ + +#include "gwlib/gwlib.h" + +#include "mms_strings.h" + + +#define TABLE_SIZE(table) ((long)(sizeof(table) / sizeof(table[0]))) + +static int initialized; + +/* The arrays in a table structure are all of equal length, and their + * elements correspond. The number for string 0 is in numbers[0], etc. + * Table structures are initialized dynamically. + */ +struct table +{ + long size; /* Nr of entries in each of the tables below */ + Octstr **strings; /* Immutable octstrs */ + long *numbers; /* Assigned numbers, or NULL for linear tables */ + int *versions; /* WSP Encoding-versions, or NULL if non-versioned */ + int linear; /* True for tables defined as LINEAR */ +}; + +struct numbered_element +{ + unsigned char *str; + long number; + int version; +}; + +struct linear_element +{ + unsigned char *str; + int version; +}; + +/* Local functions */ +static Octstr *number_to_string(long number, struct table *table); +static unsigned char *number_to_cstr(long number, struct table *table); +static long string_to_number(Octstr *ostr, struct table *table); +static long string_to_versioned_number(Octstr *ostr, struct table *table, int version); + + +/* Declare the data. For each table "foo", create a foo_strings array + * to hold the data, and a (still empty) foo_table structure. */ +#define LINEAR(name, strings) \ + static const struct linear_element name##_strings[] = { strings }; \ + static struct table name##_table; +#define STRING(string) { string, 0 }, +#define VSTRING(version, string) { string, version }, +#define NUMBERED(name, strings) \ + static const struct numbered_element name##_strings[] = { strings }; \ + static struct table name##_table; +#define ASSIGN(string, number) { string, number, 0 }, +#define VASSIGN(version, string, number) { string, number, version }, +#include "mms_strings.def" + +/* Define the functions for translating number to Octstr */ +#define LINEAR(name, strings) \ +Octstr *mms_##name##_to_string(long number) { \ + return number_to_string(number, &name##_table); \ +} +#include "mms_strings.def" + +/* Define the functions for translating number to constant string */ +#define LINEAR(name, strings) \ +unsigned char *mms_##name##_to_cstr(long number) { \ + return number_to_cstr(number, &name##_table); \ +} +#include "mms_strings.def" + +#define LINEAR(name, strings) \ +long mms_string_to_##name(Octstr *ostr) { \ + return string_to_number(ostr, &name##_table); \ +} +#include "mms_strings.def" + +#define LINEAR(name, strings) \ +long mms_string_to_versioned_##name(Octstr *ostr, int version) { \ + return string_to_versioned_number(ostr, &name##_table, version); \ +} +#include "mms_strings.def" + +static Octstr *number_to_string(long number, struct table *table) +{ + long i; + + gw_assert(initialized); + + if (table->linear) { + if (number >= 0 && number < table->size) + return octstr_duplicate(table->strings[number]); + } else { + for (i = 0; i < table->size; i++) { + if (table->numbers[i] == number) + return octstr_duplicate(table->strings[i]); + } + } + return NULL; +} + +static unsigned char *number_to_cstr(long number, struct table *table) +{ + long i; + + gw_assert(initialized); + + if (table->linear) { + if (number >= 0 && number < table->size) + return octstr_get_cstr(table->strings[number]); + } else { + for (i = 0; i < table->size; i++) { + if (table->numbers[i] == number) + return octstr_get_cstr(table->strings[i]); + } + } + return NULL; +} + +/* Case-insensitive string lookup */ +static long string_to_number(Octstr *ostr, struct table *table) +{ + long i; + + gw_assert(initialized); + + for (i = 0; i < table->size; i++) { + if (octstr_case_compare(ostr, table->strings[i]) == 0) { + return table->linear ? i : table->numbers[i]; + } + } + + return -1; +} + +/* Case-insensitive string lookup according to passed WSP encoding version */ +static long string_to_versioned_number(Octstr *ostr, struct table *table, + int version) +{ + unsigned int i, ret; + + gw_assert(initialized); + + /* walk the whole table and pick the highest versioned token */ + ret = -1; + for (i = 0; i < table->size; i++) { + if (octstr_case_compare(ostr, table->strings[i]) == 0 && + table->versions[i] <= version) { + ret = table->linear ? i : table->numbers[i]; + } + } + + debug("mms.strings",0,"MMS: Mapping string `%s', MMS version 1.%d to binary " + "representation `0x%04x'.", octstr_get_cstr(ostr), version, ret); + + return ret; +} + +static void construct_linear_table(struct table *table, const struct linear_element *strings, + long size) +{ + long i; + + table->size = size; + table->strings = gw_malloc(size * (sizeof table->strings[0])); + table->numbers = NULL; + table->versions = gw_malloc(size * (sizeof table->versions[0])); + table->linear = 1; + + for (i = 0; i < size; i++) { + table->strings[i] = octstr_imm(strings[i].str); + table->versions[i] = strings[i].version; + } +} + +static void construct_numbered_table(struct table *table, const struct numbered_element *strings, + long size) +{ + long i; + + table->size = size; + table->strings = gw_malloc(size * (sizeof table->strings[0])); + table->numbers = gw_malloc(size * (sizeof table->numbers[0])); + table->versions = gw_malloc(size * (sizeof table->versions[0])); + table->linear = 0; + + for (i = 0; i < size; i++) { + table->strings[i] = octstr_imm(strings[i].str); + table->numbers[i] = strings[i].number; + table->versions[i] = strings[i].version; + } +} + +static void destroy_table(struct table *table) +{ + /* No need to call octstr_destroy on immutable octstrs */ + + gw_free(table->strings); + gw_free(table->numbers); + gw_free(table->versions); +} + +void mms_strings_init(void) +{ + wsp_strings_init(); + if (initialized > 0) { + initialized++; + return; + } + +#define LINEAR(name, strings) \ + construct_linear_table(&name##_table, \ + name##_strings, TABLE_SIZE(name##_strings)); +#define NUMBERED(name, strings) \ + construct_numbered_table(&name##_table, \ + name##_strings, TABLE_SIZE(name##_strings)); +#include "mms_strings.def" + initialized++; +} + +void mms_strings_shutdown(void) +{ + /* If we were initialized more than once, then wait for more than + * one shutdown. */ + if (initialized > 1) { + initialized--; + return; + } + +#define LINEAR(name, strings) \ + destroy_table(&name##_table); +#include "mms_strings.def" + + initialized = 0; +} diff --git a/mbuni/mmlib/mms_strings.def b/mbuni/mmlib/mms_strings.def new file mode 100644 index 0000000..b821cd4 --- /dev/null +++ b/mbuni/mmlib/mms_strings.def @@ -0,0 +1,191 @@ +/**** Preprocessor magic ****/ + +/* String table with entries starting at 0 and numbered upwards. */ +#if !defined(LINEAR) +#error "Required macro LINEAR is missing." +#endif + +/* Entry in a LINEAR table. Default is to do nothing for it. */ +#if !defined(STRING) +#define STRING +#endif + +/* String table where entries have assigned numbers. Multiple + * entries can have the same number (the first listed entry is used + * when looking up by number). */ +#if !defined(NUMBERED) +#define NUMBERED LINEAR +#endif + +/* Entry in a NUMBERED table */ +#if !defined(ASSIGN) +#define ASSIGN(string, number) STRING(string) +#endif + +/* Entry in a versioned NUMBERED table */ +#if !defined(VASSIGN) +#define VASSIGN(version, string, number) VSTRING(version, string) +#endif + +/* Just like LINEAR, but an enumerated type name##_enum is defined for + * the entries. */ +#if !defined(NAMED) +#define NAMED LINEAR +#endif + +/* Entry in a NAMED table */ +#if !defined(NSTRING) +#define NSTRING(string, name) STRING(string) +#endif + +/* Entry in a versioned NAMED table */ +#if !defined(VNSTRING) +#define VNSTRING(version,string, name) VSTRING(version,string) +#endif + +NAMED(header, +VNSTRING(MS_1_1, "Unknown",MMS_HEADER_NONE) +VNSTRING(MS_1_1, "Bcc",MMS_HEADER_BCC) +VNSTRING(MS_1_1, "Cc",MMS_HEADER_CC) +VNSTRING(MS_1_1, "X-Mms-Content-Location",MMS_HEADER_CONTENT_LOCATION) +VNSTRING(MS_1_1, "Content-Type",MMS_HEADER_CONTENT_TYPE) +VNSTRING(MS_1_1, "Date",MMS_HEADER_DATE) +VNSTRING(MS_1_1, "X-Mms-Delivery-Report",MMS_HEADER_DELIVERY_REPORT) +VNSTRING(MS_1_1, "X-Mms-Delivery-Time",MMS_HEADER_DELIVERY_TIME) +VNSTRING(MS_1_1, "X-Mms-Expiry",MMS_HEADER_EXPIRY) +VNSTRING(MS_1_1, "From",MMS_HEADER_FROM) +VNSTRING(MS_1_1, "X-Mms-Message-Class",MMS_HEADER_MESSAGE_CLASS) +VNSTRING(MS_1_1, "Message-ID",MMS_HEADER_MESSAGE_ID) +VNSTRING(MS_1_1, "X-Mms-Message-Type",MMS_HEADER_MESSAGE_TYPE) +VNSTRING(MS_1_1, "X-Mms-MMS-Version",MMS_HEADER_MMS_VERSION) +VNSTRING(MS_1_1, "X-Mms-Message-Size",MMS_HEADER_MESSAGE_SIZE) +VNSTRING(MS_1_1, "X-Mms-Priority",MMS_HEADER_PRIORITY) +VNSTRING(MS_1_1, "X-Mms-Read-Report",MMS_HEADER_READ_REPORT) +VNSTRING(MS_1_1, "X-Mms-Report-Allowed",MMS_HEADER_REPORT_ALLOWED) +VNSTRING(MS_1_1, "X-Mms-Response-Status",MMS_HEADER_RESPONSE_STATUS) +VNSTRING(MS_1_1, "X-Mms-Response-Text",MMS_HEADER_RESPONSE_TEXT) +VNSTRING(MS_1_1, "X-Mms-Sender-Visibility",MMS_HEADER_SENDER_VISIBILITY) +VNSTRING(MS_1_1, "X-Mms-Status",MMS_HEADER_STATUS) +VNSTRING(MS_1_1, "Subject",MMS_HEADER_SUBJECT) +VNSTRING(MS_1_1, "To",MMS_HEADER_TO) +VNSTRING(MS_1_1, "X-Mms-Transaction-Id",MMS_HEADER_TRANSACTION_ID) +VNSTRING(MS_1_1, "X-Mms-Retrieve-Status",MMS_HEADER_RETRIEVE_STATUS) +VNSTRING(MS_1_1, "X-Mms-Retrieve-Text",MMS_HEADER_RETRIEVE_TEXT) +VNSTRING(MS_1_1, "X-Mms-Read-Status",MMS_HEADER_READ_STATUS) +VNSTRING(MS_1_1, "X-Mms-Reply-Charging",MMS_HEADER_REPLY_CHARGING) +VNSTRING(MS_1_1, "X-Mms-Reply-Charging-Deadline",MMS_HEADER_REPLY_CHARGING_DEADLINE) +VNSTRING(MS_1_1, "X-Mms-Reply-Charging-ID",MMS_HEADER_REPLY_CHARGING_ID) +VNSTRING(MS_1_1, "X-Mms-Reply-Charging-Size",MMS_HEADER_REPLY_CHARGING_SIZE) +VNSTRING(MS_1_1, "X-Mms-Previously-Sent-By",MMS_HEADER_PREVIOUSLY_SENT_BY) +VNSTRING(MS_1_1, "X-Mms-Previously-Sent-Date",MMS_HEADER_PREVIOUSLY_SENT_DATE) +) + +LINEAR(reports, +STRING("yes") +STRING("no") +) + +NAMED(message_type, +VNSTRING(MS_1_1,"m-send-req", MMS_MSGTYPE_SEND_REQ) +VNSTRING(MS_1_1,"m-send-conf", MMS_MSGTYPE_SEND_CONF) +VNSTRING(MS_1_1,"m-notification-ind", MMS_MSGTYPE_NOTIFICATION_IND) +VNSTRING(MS_1_1,"m-notifyresp-ind", MMS_MSGTYPE_NOTIFYRESP) +VNSTRING(MS_1_1,"m-retrieve-conf", MMS_MSGTYPE_RETRIEVE_CONF) +VNSTRING(MS_1_1,"m-acknowledge-ind", MMS_MSGTYPE_ACKNOWLEDGE_IND) +VNSTRING(MS_1_1,"m-delivery-ind", MMS_MSGTYPE_DELIVERY_IND) +VNSTRING(MS_1_1,"m-read-rec-ind", MMS_MSGTYPE_READ_REC_IND) +VNSTRING(MS_1_1,"m-read-orig-ind", MMS_MSGTYPE_READ_ORIG_IND) +VNSTRING(MS_1_1,"m-forward-req", MMS_MSGTYPE_FORWARD_REQ) +VNSTRING(MS_1_1,"m-forward-conf", MMS_MSGTYPE_FORWARD_CONF) +) + +LINEAR(message_class, +STRING("Personal") +STRING("Advertisement") +STRING("Informational") +STRING("Auto") +) + + +NUMBERED(response_status, +ASSIGN("Ok",128) +ASSIGN("Error-unspecified",129) +ASSIGN("Error-service-denied",130) +ASSIGN("Error-message-format-corrupt",131) +ASSIGN("Error-sending-address-unresolved",132) +ASSIGN("Error-message-not-found",133) +ASSIGN("Error-network-problem",134) +ASSIGN("Error-content-not-accepted",135) +ASSIGN("Error-unsupported-message",136) +ASSIGN("Error-transient-failure",192) +ASSIGN("Error-transient-sending-address-unresolved",193) +ASSIGN("Error-transient-message-not-found",194) +ASSIGN("Error-transient-network-problem",195) +ASSIGN("Error-permanent-failure",224) +ASSIGN("Error-permanent-service-denied",225) +ASSIGN("Error-permanent-message-format-corrupt",226) +ASSIGN("Error-permanent-sending-address-unresolved",227) +ASSIGN("Error-permanent-message-not-found",228) +ASSIGN("Error-permanent-content-not-accepted",229) +ASSIGN("Error-permanent-reply-charging-limitations-not-met",230) +ASSIGN("Error-permanent-reply-charging-request-not-accepted",231) +ASSIGN("Error-permanent-reply-charging-forwarding-denied",232) +ASSIGN("Error-permanent-reply-charging-not-supported",233) +) + +NUMBERED(retrieve_status, +ASSIGN("Ok",128) +ASSIGN("Error-transient-failure",192) +ASSIGN("Error-transient-message-not-found",193) +ASSIGN("Error-transient-network-problem",194) +ASSIGN("Error-permanent-failure",224) +ASSIGN("Error-permanent-service-denied",225) +ASSIGN("Error-permanent-message-not-found",226) +ASSIGN("Error-permanent-content-unsupported",227) +) + + +LINEAR(priority, +STRING("Low") +STRING("Normal") +STRING("High") +) + +LINEAR(read_status, +STRING("Read") +STRING("Deleted without being read") +) + +LINEAR(visibility, +STRING("Hide") +STRING("Show") +) + +LINEAR(reply_charging, +STRING("Requested") +STRING("Requested text only") +STRING("Accepted") +STRING("Accepted text only") +) + +LINEAR(status, +STRING("Expired") +STRING("Retrieved") +STRING("Rejected") +STRING("Deferred") +STRING("Unrecognised") +STRING("Indeterminate") +STRING("Forwarded") +) + + + +#undef LINEAR +#undef STRING +#undef VSTRING +#undef NUMBERED +#undef ASSIGN +#undef VASSIGN +#undef NAMED +#undef NSTRING +#undef VNSTRING diff --git a/mbuni/mmlib/mms_strings.h b/mbuni/mmlib/mms_strings.h new file mode 100644 index 0000000..2b3a092 --- /dev/null +++ b/mbuni/mmlib/mms_strings.h @@ -0,0 +1,29 @@ +#ifndef __MMS_STRINGS_DEFINED__ +#define __MMS_STRINGS_DEFINED__ + +typedef enum { + MS_1_1 = 1 +} mms_encoding; +#define MMS_VERSION "1.0" +/* Declare the functions */ +#define LINEAR(name, strings) \ +Octstr *mms_##name##_to_string(long number); \ +unsigned char *mms_##name##_to_cstr(long number); \ +long mms_string_to_##name(Octstr *ostr); \ +long mms_string_to_versioned_##name(Octstr *ostr, int version); +#define STRING(string) +#include "mms_strings.def" + + +#define LINEAR(name,strings) +#define LINEAR(name, strings) +#define STRING(string) +#define NAMED(name, strings) enum mms_##name##_enum { strings mms_##name##_dummy }; +#define NSTRING(string, name) name, +#define VNSTRING(version, string, name) name, +#include "mms_strings.def" + +#include "wap/wsp_strings.h" + +void mms_strings_init(void); +#endif diff --git a/mbuni/mmlib/mms_uaprof.c b/mbuni/mmlib/mms_uaprof.c new file mode 100644 index 0000000..bad779f --- /dev/null +++ b/mbuni/mmlib/mms_uaprof.c @@ -0,0 +1,1313 @@ +#include +#include +#include +#include +#include + +#include "mms_uaprof.h" +#include "mms_util.h" + +struct MmsUaProfile { + List *versions; + unsigned long maxmsgsize; + struct { + long x, y; + } maxres; + struct { + unsigned char all; + unsigned char presentation; + List *content, *_hash; /* List of accepted content formats (+ hash keys for fast look-up). */ + + List *charset, *_chash; /* List of accepted charsets. */ + List *lang; /* List of accepted languages. */ + List *enc; /* List of accepted encodings. */ + } ccppaccept; +}; + +static Dict *profile_dict; /* Of MmsUaProfile *. */ +static HTTPCaller *client; + +/* Hash function -- case insensitive. */ +static unsigned long hash_key(Octstr *s) +{ + unsigned long h = 0; + int i, n; + char *x; + + if (!s) return 0; + for (x = octstr_get_cstr(s), i = 0, n = octstr_len(s); iversions) + list_destroy(prof->versions, + (list_item_destructor_t *)octstr_destroy); + + if (prof->ccppaccept.content) { + list_destroy(prof->ccppaccept.content, (list_item_destructor_t *)octstr_destroy); + list_destroy(prof->ccppaccept._hash, NULL); + } + + if (prof->ccppaccept.charset) { + list_destroy(prof->ccppaccept.charset, (list_item_destructor_t *)octstr_destroy); + list_destroy(prof->ccppaccept._chash, NULL); + } if (prof->ccppaccept.lang) + list_destroy(prof->ccppaccept.lang, (list_item_destructor_t *)octstr_destroy); + if (prof->ccppaccept.enc) + list_destroy(prof->ccppaccept.enc, (list_item_destructor_t *)octstr_destroy); + + gw_free(prof); +} + +static void dump_profile(MmsUaProfile *prof, Octstr *name) +{ + int i; + Octstr *s; + + debug("mms.uaprof", 0, "Dumping profile for %s", octstr_get_cstr(name)); + + debug("mms.uaprof", 0, "MaxMsgSize: %ld", prof->maxmsgsize); + debug("mms.uaprof", 0, "MaxRes: %ldx%ld", prof->maxres.x,prof->maxres.y); + + + s = octstr_create(""); + if (prof->ccppaccept.content) + for (i=0; iccppaccept.content); i++) + octstr_format_append(s, "%S, ", list_get(prof->ccppaccept.content,i)); + debug("mms.uaprof", 0, "Accept content: %s", octstr_get_cstr(s)); + octstr_destroy(s); + + + s = octstr_create(""); + if (prof->ccppaccept.enc) + for (i=0; iccppaccept.enc); i++) + octstr_format_append(s, "%S, ", list_get(prof->ccppaccept.enc,i)); + debug("mms.uaprof", 0, "Accept encodings: %s", octstr_get_cstr(s)); + octstr_destroy(s); + + s = octstr_create(""); + if (prof->ccppaccept.lang) + for (i=0; iccppaccept.lang); i++) + octstr_format_append(s, "%S, ", list_get(prof->ccppaccept.lang,i)); + debug("mms.uaprof", 0, "Accept language: %s", octstr_get_cstr(s)); + octstr_destroy(s); + + s = octstr_create(""); + if (prof->ccppaccept.charset) + for (i=0; iccppaccept.charset); i++) + octstr_format_append(s, "%S, ", list_get(prof->ccppaccept.charset,i)); + debug("mms.uaprof", 0, "Accept charset: %s", octstr_get_cstr(s)); + octstr_destroy(s); + + s = octstr_create(""); + if (prof->versions) + for (i=0; iversions); i++) + octstr_format_append(s, "%S, ", list_get(prof->versions,i)); + debug("mms.uaprof", 0, "Mms Version: %s", octstr_get_cstr(s)); + octstr_destroy(s); + +} + +/* Helper function: find a node. Uses breadth first search */ +static xmlNodePtr find_node(xmlNodePtr start, char *name, char *id, int level, int maxlevel) +{ + xmlNodePtr node, x, list; + + + if (level >= maxlevel) return NULL; + + /* First search at top level. */ + for (list=start; list; list=list->next) + if (list->type == XML_COMMENT_NODE) + continue; + else if (xmlStrcasecmp(list->name, (const xmlChar *)name) == 0) { + if (!id) + return list; + else { + char *s; + if ((s= xmlGetProp(list,"ID")) != NULL && + xmlStrcasecmp(s,id) == 0) { + xmlFree(s); + return list; + } + if (s) xmlFree(s); + } + } + /* Then recurse...*/ + for (list = start; list; list=list->next) + for (node = list->xmlChildrenNode; node; node = node->next) + if (xmlStrcasecmp(node->name, (const xmlChar *)name) == 0) { + if (!id) + return node; + else { + char *s; + if ((s = xmlGetProp(node,"ID")) != NULL && + xmlStrcasecmp(s,id) == 0) { + xmlFree(s); + return node; + } + if (s) xmlFree(s); + } + } else if (node->type != XML_COMMENT_NODE && + (x = find_node(node, name,id, level+1,maxlevel)) != NULL) + return x; + return NULL; +} + +MmsUaProfile *mms_make_ua_profile(List *req_headers) +{ + MmsUaProfile *prof = NULL; + Octstr *s, *ua; + List *l; + int i, n; + static int uacounter; + + /* Check cache first, if not, then construct. */ + if ((ua = http_header_value(req_headers, octstr_imm("User-Agent"))) == NULL) + ua = octstr_format("dummy-ua-%d", uacounter++); + + if ((prof = dict_get(profile_dict, ua)) != NULL) + goto done; + + prof = gw_malloc(sizeof *prof); + memset(prof, 0, sizeof *prof); + + /* Put in some defaults. then read then check accepts. */ + prof->maxres.x = 640; + prof->maxres.y = 480; + prof->maxmsgsize = 100*1024; + prof->versions = list_create(); + + + list_append(prof->versions, octstr_imm("1.0")); /* Assume 1.0 for now. */ + + /* Get accepted charsets. */ + s = http_header_value(req_headers, octstr_imm("Accept-Charset")); + + if (s && (l = http_header_split_value(s)) != NULL) { + prof->ccppaccept.charset = l; + prof->ccppaccept._chash = list_create(); + for (i = 0, n = list_len(l); iccppaccept._chash, (void *)hash_key(list_get(l, i))); + } + if (s) octstr_destroy(s); + + + /* Get accepted encodings. */ + s = http_header_value(req_headers, octstr_imm("Accept-Encoding")); + + if (s && (l = http_header_split_value(s)) != NULL) + prof->ccppaccept.enc = l; + + if (s) octstr_destroy(s); + + + + /* Get accepted language. */ + s = http_header_value(req_headers, octstr_imm("Accept-Language")); + if (s && (l = http_header_split_value(s)) != NULL) + prof->ccppaccept.lang = l; + + if (s) octstr_destroy(s); + + s = http_header_value(req_headers, octstr_imm("Accept")); + if (s && (l = http_header_split_value(s)) != NULL) { + prof->ccppaccept.content = l; + prof->ccppaccept._hash = list_create(); + + for (i = 0, n = l ? list_len(l) : 0; iccppaccept.all = 1; + else if (octstr_case_compare(x, octstr_imm(PRES_TYPE)) == 0) + prof->ccppaccept.presentation = 1; + + list_append(prof->ccppaccept._hash, (void *)hash_key(x)); + } + } + + if (s) octstr_destroy(s); + + /* Put it in with the UA string as the key. */ + if (dict_put_once(profile_dict, ua, prof) != 1) + warning(0, "mms_uaprof: Duplicate cache entry(%s)?\n", + octstr_get_cstr(ua)); + + /* Done. Dump it while debugging. */ + done: + +#if 1 + dump_profile(prof, ua ? ua : octstr_imm("")); +#endif + + if (ua) + octstr_destroy(ua); + return prof; +} + +static MmsUaProfile *parse_uaprofile(Octstr *xml) +{ + char *s = octstr_get_cstr(xml); + xmlDocPtr doc = xmlParseMemory(s, octstr_len(xml)); + xmlNodePtr node, xnode; + MmsUaProfile *prof = NULL; + + if (!doc || !doc->xmlChildrenNode) + goto done; + + node = find_node(doc->xmlChildrenNode, "Description", "MmsCharacteristics",0,3); + + prof = gw_malloc(sizeof *prof); + memset(prof, 0, sizeof *prof); + + /* Put in some defaults. then read the file. */ + prof->versions = NULL; + prof->maxres.x = 640; + prof->maxres.y = 480; + prof->maxmsgsize = 100*1024; + prof->versions = NULL; + + + if (!node) + goto done; + + for (xnode = node->xmlChildrenNode; xnode; xnode = xnode->next) { + xmlNodePtr child = xnode->xmlChildrenNode, lnode, rdfnode; + const char *xname = xnode->name; + char *childtext = xmlNodeListGetString(doc, child, 1); + List *l; + + /* If there is a Bag, get the list. */ + if ((rdfnode = find_node(xnode->xmlChildrenNode, "Bag", NULL,0,1)) != NULL) { + l = list_create(); + for (lnode = rdfnode->xmlChildrenNode; lnode; lnode = lnode->next) + if (xmlStrcasecmp(lnode->name, (const xmlChar *)"li") == 0) { + char *t = xmlNodeListGetString(doc, lnode->xmlChildrenNode,1); + if (t) { + list_append(l, octstr_create(t)); + xmlFree(t); + } + } + } else + l = NULL; + + if (xmlStrcasecmp(xname, (const xmlChar *)"MmsMaxMessageSize") == 0) + sscanf(childtext, "%ld", &prof->maxmsgsize); + else if (xmlStrcasecmp(xname, (const xmlChar *)"MmsMaxImageResolution") == 0) + sscanf(childtext, "%ldx%ld", &prof->maxres.x, &prof->maxres.y); + else if (xmlStrcasecmp(xname, (const xmlChar *)"MmsCcppAcceptCharSet") == 0 || + xmlStrcasecmp(xname, (const xmlChar *)"MmsCcppAccept-CharSet") == 0) {/* Cranky old ones! */ + int i, n; + prof->ccppaccept.charset = l; + prof->ccppaccept._chash = list_create(); + for (i = 0, n = list_len(l); iccppaccept._chash, (void *)hash_key(list_get(l, i))); + } else if (xmlStrcasecmp(xname, (const xmlChar *)"MmsCcppAcceptLanguage") == 0) + prof->ccppaccept.lang = l; + else if (xmlStrcasecmp(xname, (const xmlChar *)"MmsCcppAcceptEncoding") == 0) + prof->ccppaccept.enc = l; + else if (xmlStrcasecmp(xname, (const xmlChar *)"MmsVersion") == 0) { + if (!l && childtext) { /* SonyEriccson uses old format! */ + l = list_create(); + list_append(l, octstr_create(childtext)); + } + prof->versions = l; + } else if (xmlStrcasecmp(xname, (const xmlChar *)"MmsCcppAccept") == 0) { + int i, n; + prof->ccppaccept.content = l; + prof->ccppaccept._hash = list_create(); + + for (i = 0, n = l ? list_len(l) : 0; iccppaccept.all = 1; + else if (octstr_case_compare(x, octstr_imm(PRES_TYPE)) == 0) + prof->ccppaccept.presentation = 1; + + list_append(prof->ccppaccept._hash, (void *)hash_key(x)); + } + } + if (childtext) xmlFree(childtext); + } + + done: + if (doc) xmlFreeDoc(doc); + return prof; +} + + +static int replace_slash(int ch) +{ + return (ch == '/') ? '$' : ch; +} + +static int unreplace_slash(int ch) +{ + return (ch == '$') ? '/' : ch; +} + +#define UACOUNT_PROFILE 1023 +static void init_profiledict(void) +{ + if (!profile_dict) + profile_dict = dict_create(UACOUNT_PROFILE, (void (*)(void *))destroy_uaprof); +} + + +static int mms_load_ua_profile_cache(char *dir) +{ + + DIR *dirp; + struct dirent *dp; + dirp = opendir(dir); + + if (!dirp) { + error(0, "mms_uaprof: Failed to open UA prof cache directory %s", + dir); + return -1; + } + + while ((dp = readdir(dirp)) != NULL) { + Octstr *fname; + Octstr *xml = NULL; + MmsUaProfile *prof = NULL; + Octstr *key = NULL; + + + if (strcmp(dp->d_name, ".") == 0 || + strcmp(dp->d_name, "..") == 0 || + dp->d_type & DT_DIR) /* If a directory, keep going. */ + continue; + + fname = octstr_format("%.255s/%.254s", dir, dp->d_name); + + xml = octstr_read_file(octstr_get_cstr(fname)); + octstr_destroy(fname); + if (!xml) { + error(0, "mms_uaprof: Failed to read UA prof doc %s in %s (%s)\n", + dp->d_name, dir, strerror(errno)); + continue; + } + + prof = parse_uaprofile(xml); + if (!prof) { + error(0, "mms_uaprof: Failed to parse UA prof doc %s in %s\n", dp->d_name, dir); + goto loop; + } + + key = octstr_create(dp->d_name); + octstr_convert_range(key, 0, octstr_len(key), unreplace_slash); + + if (dict_put_once(profile_dict, key, prof) != 1) + warning(0, "mms_uaprof: Duplicate cache entry(%s)?\n", + octstr_get_cstr(key)); +#if 1 + dump_profile(prof, key); +#endif + loop: + if (xml) octstr_destroy(xml); + if (key) octstr_destroy(key); + } + closedir(dirp); + + return 0; +} + + +static void mms_profile_fetcher(char *cache_dir) +{ + Octstr *profile_url, *body; + List *h; + int status; + + /* Just keep getting the data and putting it in the cache and on file. */ + client = http_caller_create(); + + debug("mms.uaprof", 0, "Entered fetcher"); + while (http_receive_result(client, &status, &profile_url, &h, &body) != NULL) { + if (status == HTTP_OK) { + MmsUaProfile *prof = parse_uaprofile(body); + Octstr *fname; + FILE *f; + + debug("mms.uaprof", 0, "Fetcher got %s", octstr_get_cstr(profile_url)); + + if (prof) { + if (dict_put_once(profile_dict, profile_url, prof) != 1) + warning(0, "mms_uaprof: Duplicate ua profile fetched? (%s)?\n", + octstr_get_cstr(profile_url)); + } else { + error(0, "mms_uaprof: Failed to parse UA prof url=%s\n", + octstr_get_cstr(profile_url)); + goto loop; + } + octstr_convert_range(profile_url, 0, octstr_len(profile_url), replace_slash); + fname = octstr_format("%.255s/%.254s", cache_dir, octstr_get_cstr(profile_url)); + + f = fopen(octstr_get_cstr(fname), "w"); + + if (f) { + octstr_print(f, body); + fclose(f); + } else + error(0, "mms_uaprof: Failed to save profile data to cache file %s->%s\n", + octstr_get_cstr(fname), strerror(errno)); + octstr_destroy(fname); + } + loop: + if (body) octstr_destroy(body); + if (profile_url) octstr_destroy(profile_url); + if (h) http_destroy_headers(h); + } + + debug("mms.uaprof", 0, "Fetcher shutdown..."); + http_caller_destroy(client); + client = NULL; + + dict_destroy(profile_dict); + profile_dict = NULL; +} + +static void init_format_table(void); + +int mms_start_profile_engine(char *cache_dir) +{ + init_profiledict(); + init_format_table(); + mms_load_ua_profile_cache(cache_dir); + gwthread_create((gwthread_func_t *)mms_profile_fetcher, cache_dir); + + return 0; +} + +int mms_stop_profile_engine(void) +{ + if (client) + http_caller_signal_shutdown(client); + /* This will cause thread to stop and also destroy dict. */ + return 0; +} + +MmsUaProfile *mms_get_ua_profile(char *url) +{ + Octstr *s = octstr_create(url); + MmsUaProfile *prof = NULL; + List *h; + + + if (!profile_dict) { + error(0, "mms_uaprof: get_profile, cache not set!\n"); + goto done; + } + + if ((prof = dict_get(profile_dict, s)) != NULL) { + octstr_destroy(s); + goto done; + } + /* Not found, queue get and return NULL. */ + + if (client) { + h = http_create_empty_headers(); + http_header_add(h, "User-Agent", MM_NAME "/" GW_VERSION); + http_start_request(client,HTTP_METHOD_GET, s, h, NULL, 1, NULL, NULL); + } else + error(0, "mms_uaprof: get_profile, Fetch thread not started!\n"); + done: + return prof; +} + +/* Content convertors. + * content types are listed in order of preference. + * Notes: We have a concept of an intermediate format. For audio it is WAV + * (of indeterminate frequency or sample size). Each type of command should expect input on stdin + * and write output to standard out. + * New 25-01-04: For images we now use Imagemagick to convert, hence no intermediate format. + */ + +#define NELEMS(a) (sizeof a/sizeof a[0]) +struct { + char *content_type; /* Content type. */ + unsigned long chash; /* hash value. */ + + char *tostandard_cmd; /* Command to convert to standard format. */ + char *fromstandard_cmd; /* Command to convert from standard format. */ + + char *file_ext; /* Standard file extension. */ + int multi_image; /* whether this format allows for multiple images in one file. */ + enum {TIMAGE=1,TAUDIO,TTEXT,TPRES,TOTHER} t; +} cformats[] = { + /* Note: Order of listing matters: + * For images, we prefer jpeg (smaller, better support), + * For audio we prefer MP3 (smaller, well supported). + */ + {"image/jpeg", 0, "jpegtopnm", "pnmtojpeg", "jpg", 0, TIMAGE}, + {"image/png", 0, "pngtopnm ", "pnmtopng ", "png", 0, TIMAGE}, + {"image/jpg", 0, "jpegtopnm", "pnmtojpeg", "jpg", 0, TIMAGE}, + {"image/tiff", 0, "tifftopnm", "pnmtotiff", "tiff", 1, TIMAGE}, + + {"image/gif", 0, "giftopnm", "pnmquant 256 | ppmtogif", "gif", 1, TIMAGE}, + {"image/bmp", 0, "bmptopnm", "pnmquant 256 | ppmtobmp", "bmp", 1, TIMAGE}, + {"image/vnd.wap.wbmp", 0, "wbmptopbm", "ppmtopgm | pgmtopbm | pbmtowbmp", "wbmp", 0, TIMAGE}, +#if 0 + {"image/x-bmp", 0, "bmptopnm", "pnmtobmp", "bmp", 0, TIMAGE}, + {"image/x-wmf", 0, "bmptopnm", "pnmtobmp", "bmp", 0, TIMAGE}, + {"image/vnd.wap.wpng", 0, "pngtopnm", "pngtobmp", "png", 0, TIMAGE}, + {"image/x-up-wpng", 0, "pngtopnm", "pnmtopng", "png", 0, TIMAGE}, +#endif + {"audio/mpeg", 0, "mpg123 -w - -", "lame - -", "mp3",0, TAUDIO}, + {"audio/wav", 0, "cat", "cat", "wav",0, TAUDIO}, + {"audio/x-wav", 0, "cat", "cat", "wav",0, TAUDIO}, + {"audio/basic", 0, "sox -t au -r 8000 -b -c 1 - -t wav -", + "sox -t wav - -t au -b -c 1 -r 8000 - lowpass 3700", "au",0, TAUDIO}, + {"audio/amr", 0, "amrdecoder - - | sox -t raw -s -w -c 1 -r 8000 - -t wav -", + "sox -t wav - -t raw -s -w -c 1 -r 8000 - lowpass 3700 | amrencoder MR122 - -", "amr",0, TAUDIO}, + {"audio/x-amr", 0, "amrdecoder - - | sox -t raw -s -w -c 1 -r 8000 - -t wav -", + "sox -t wav - -t raw -s -w -c 1 -r 8000 - lowpass 3700 | amrencoder MR122 - -", "amr",0, TAUDIO}, + {"audio/amr-wb", 0, "amrdecoder - - | sox -t raw -s -w -c 1 -r 8000 - -t wav -", + "sox -t wav - -t raw -s -w -c 1 -r 8000 - lowpass 3700 | amrencoder MR122 - -", "amr",0, TAUDIO}, +#if 0 + {"audio/midi", 0, "cat", "cat", "mid",0, TAUDIO}, + {"audio/sp-midi", 0, "cat", "cat", "mid",0, TAUDIO}, +#endif +}; + +/* Image commands for finding resolution, scaling and conversion. */ +#define IMGRESCMD "identify -format '%%w %%h' %s:%s" +#define IMGSCALECMD "convert -scale '%ldx%ld>' %s:%s %s:-" +#define IMGCONVERTCMD "convert '%s:%s' '%s:%s'" + +static void init_format_table(void) +{ + int i; + + for (i = 0; i < NELEMS(cformats); i++) + cformats[i].chash = hash_key(octstr_imm(cformats[i].content_type)); + +} + +/* Removes an object by making it text/plain. For now not configurable. */ +static void remove_object(MIMEEntity *m, Octstr *ctype) +{ + http_header_remove_all(m->headers, "Content-Type"); + + http_header_add(m->headers, "Content-Type", "text/plain"); + + octstr_destroy(m->body); + m->body = octstr_format("Unsupported object (content type %S) removed", ctype); +} + +static void mktmpfname(char fname[]) +{ + /* We are not parameterising tmp dir yet. */ + sprintf(fname, "/tmp/t%ld.%ld.%ld", random(), (long)getpid(), (long)time(NULL)); +} + +static Octstr *mknewname(Octstr *oldname, char *ext) +{ + Octstr *s; + + int i = octstr_search_char(oldname, '.', 0); + + if (i<0) + return octstr_format("%S.%s", oldname, ext); + + s = octstr_copy(oldname, 0, i+1); + octstr_append_cstr(s,ext); + return s; +} + +static void replace_ctype(List *headers, char *newcontent_type, List *params_h) +{ + Octstr *ct; + if (list_len(params_h) > 0) { + Octstr *tmp = make_value_parameters(params_h); + ct = octstr_format("%s; %S", newcontent_type, tmp); + octstr_destroy(tmp); + } else + ct = octstr_format("%s", newcontent_type); + + http_header_remove_all(headers, "Content-Type"); + http_header_add(headers, "Content-Type", octstr_get_cstr(ct)); + octstr_destroy(ct); +} + +static void replace_body(MIMEEntity *msg, Octstr *newbody, List *params_h, + char *newcontent_type, char *file_ext, + int add_disposition_header) +{ + Octstr *part_name; + Octstr *new_partname = NULL; + + octstr_destroy(msg->body); /* Replace the body. */ + msg->body = newbody; + + if ((part_name = http_header_value(params_h, octstr_imm("name"))) != NULL) { + Octstr *tmp = mknewname(part_name, file_ext); + http_header_remove_all(params_h, "name"); + http_header_add(params_h, "name", octstr_get_cstr(tmp)); + + octstr_destroy(part_name); + new_partname = tmp; + } + + replace_ctype(msg->headers, newcontent_type, params_h); + + if (add_disposition_header) { + Octstr *tmp = octstr_format("inline; filename=\"%S\"", + new_partname ? new_partname : octstr_imm("any")); + http_header_add(msg->headers, "Content-Disposition", octstr_get_cstr(tmp)); + octstr_destroy(tmp); + } + + if (new_partname) octstr_destroy(new_partname); +} + +/* Modify the message based on the user agent profile data. Return 1 if was supported, 0 + * otherwise + */ +static int modify_msg(MIMEEntity *msg, MmsUaProfile *prof) +{ + int i, n, type, send_data; + int j,m; + Octstr *content_type = NULL, *params = NULL, *s; + List *params_h; + + unsigned long chash; + unsigned char supported = 0; + + int iindex, oindex; + + FILE *pf; + char tmpf[40], tmpf2[40]; + Octstr *cmd = NULL; + + if (!msg) return 0; + + tmpf[0] = tmpf2[0] = 0; /* Clear .*/ + + /* Get the content type, hash it. */ + + get_content_type(msg->headers, &content_type, ¶ms); + params_h = get_value_parameters(params); + +#if 0 + debug("MMS uaprof:", 0, " content_type = ### %s & %s: Header dump follows:", + octstr_get_cstr(content_type), params ? octstr_get_cstr(params) : "NULL"); + http_header_dump(params_h); +#endif + + if (msg->multiparts && list_len(msg->multiparts) > 0) { + Octstr *startp = http_header_value(params_h, octstr_imm("start")); + int sflag = 0; + + for (i = 0, n = list_len(msg->multiparts); imultiparts,i); + Octstr *cid = http_header_value(x->headers, octstr_imm("Content-ID")); + int sup; + + debug("MMS uaprof: cid =###", 0, "%s", cid ? octstr_get_cstr(cid) : "NULL"); + + sup = modify_msg(x, prof); + + if (!sup && /* not supported and is the presentation part, set flag */ + cid && octstr_compare(cid, startp) == 0) + sflag = 1; + if (cid) octstr_destroy(cid); + } + + /* If no start param but content type is other than multipart/mixed OR + * There is a start param but presentation type is not supported, + * Or presentations are not supported. + */ + if (sflag || + (!startp && + octstr_case_compare(content_type, octstr_imm("multipart/related")) == 0) || + !prof->ccppaccept.presentation) { + /* MMS conformance guide says: If presentation part is removed or unsupported, + * then change content type to multipart/mixed + */ + + http_header_remove_all(params_h, "start"); + http_header_remove_all(params_h, "type"); + + replace_ctype(msg->headers, "multipart/mixed", params_h); + } + + if (startp) octstr_destroy(startp); + + supported = 1; + goto done; + } + + + if (octstr_case_search(content_type, octstr_imm("image/"), 0) == 0) + type = TIMAGE; + else if (octstr_case_search(content_type, octstr_imm("audio/"), 0) == 0) + type = TAUDIO; + else if (octstr_case_search(content_type, octstr_imm("text/"), 0) == 0) + type = TTEXT; + else + type = TOTHER; + + + if (type == TTEXT) { /* Deal with charset issues. */ + Octstr *charset = http_header_value(params_h, octstr_imm("charset")); + char csupport = 0; + int i,n; + + if (charset == NULL || + octstr_str_compare(charset, "unknown") == 0) { + if (charset) octstr_destroy(charset); + charset = octstr_imm(DEFAULT_CHARSET); + } + + n = prof->ccppaccept.charset ? list_len(prof->ccppaccept.charset) : 0; + + /* Is this character set supported? If so do nothing. */ + for (i = 0; iccppaccept.charset,i), charset) == 0) { + csupport = 1; + break; + } + + if (!csupport) + for (i = 0; iccppaccept.charset,i); /* Don't free this! */ + Octstr *ct; + + if (charset_convert(msg->body, octstr_get_cstr(charset), + octstr_get_cstr(ncharset)) != -1) { /* using libiconv...*/ + Octstr *tmp; + + http_header_remove_all(params_h, "charset"); + http_header_add(params_h, "charset", octstr_get_cstr(ncharset)); + tmp = make_value_parameters(params_h); + + ct = octstr_format("%S; %S", content_type, tmp); + octstr_destroy(tmp); + http_header_remove_all(msg->headers, "Content-Type"); + http_header_add(msg->headers, "Content-Type", octstr_get_cstr(ct)); + octstr_destroy(ct); + + break; /* We succeeded in converting it so we shd go away. */ + } + } + + octstr_destroy(charset); + supported = 1; + goto done; /* No further processing for text/plain. */ + } + + /* find out if it is supported by the user agent. */ + chash = hash_key(content_type); + if (prof->ccppaccept.all) /* Check if it accepts all content types. */ + supported = 1; + else + for (i = 0, n = prof->ccppaccept.content ? list_len(prof->ccppaccept.content) : 0; + iccppaccept._hash,i) == chash && + octstr_case_compare(list_get(prof->ccppaccept.content,i),content_type) == 0) { + supported = 1; + break; + } + + if (supported && type != TIMAGE) + goto done; /* If it is supported, go away now. + * But for images we defer since we might have to scale the image. + */ + else if (type == TOTHER) + goto done; /* Not supported and not audio or image, will be removed at done. */ + + + /* At this point we have type = IMAGE (supported or not) OR type = AUDIO (unsuppported). */ + + + /* Find out if we have a means of converting this format to our internal format, + * and if we have means of converting from internal to a format the UA supports. + * For images this means: Can we read it? Can we write out a supported format? + */ + + iindex = -1; + for (i = 0; i < NELEMS(cformats); i++) + if (cformats[i].chash == chash && + octstr_case_compare(content_type, octstr_imm(cformats[i].content_type)) == 0) { + if (cformats[i].tostandard_cmd) + iindex = i; /* Only if the cmd exists actually. */ + break; + } + + oindex = -1; + for (i = 0; i < NELEMS(cformats); i++) + if (cformats[i].fromstandard_cmd) /* Check only ones we can convert from. */ + for (j = 0, m = list_len(prof->ccppaccept.content); jccppaccept._hash,j) == cformats[i].chash && + cformats[i].t == type && /* Convert to like type ! */ + octstr_case_compare(list_get(prof->ccppaccept.content,j), + octstr_imm(cformats[i].content_type)) == 0){ + oindex = i; + i = NELEMS(cformats); /* So the other loop breaks too. */ + break; + } + + + if (iindex < 0 || oindex < 0) { /* We don't know how to convert this one fully, so... */ + if (!supported) + remove_object(msg, content_type); + goto done; + } + + /* Whatever we have (audio or image) we know how to convert it fully, so ... */ + mktmpfname(tmpf); + if (type == TIMAGE) { + FILE *pf; + long x = 640, y = 480; + Octstr *icmd; + char *selector; + + mktmpfname(tmpf2); + pf = fopen(tmpf2, "w"); + if (!pf) + goto done; + + n = octstr_print(pf, msg->body); + m = fclose(pf); + + if (n < 0 || m != 0) + goto done; /* error .*/ + + /* Get the image dimensions, see if we need to modify it. */ + icmd = octstr_format(IMGRESCMD, + cformats[iindex].file_ext, tmpf2); + + pf = popen(octstr_get_cstr(icmd), "r"); + octstr_destroy(icmd); + + if (!pf) + goto done; + fscanf(pf, "%ld %ld", &x, &y); + pclose(pf); + + icmd = NULL; /* Reset to NULL. */ + if (x > prof->maxres.x || + y > prof->maxres.y) + icmd = octstr_format(IMGSCALECMD, prof->maxres.x, prof->maxres.y, + cformats[iindex].file_ext, + tmpf2, + cformats[iindex].file_ext); + else if (supported) /* A supported image format and no need to scale, so go away. */ + goto done; + + /* If the first image is multi but the second isn't, then add selector. */ + if (cformats[iindex].multi_image && + !cformats[oindex].multi_image) + selector = "-[0]"; + else + selector = "-"; + /* We have an unsupported image now. time to convert it. */ + if (icmd) { + cmd = octstr_format("%S | " IMGCONVERTCMD, + icmd, cformats[iindex].file_ext, selector, + cformats[oindex].file_ext, tmpf); + octstr_destroy(icmd); + } else + cmd = octstr_format("cat %S | " IMGCONVERTCMD, + tmpf2, cformats[iindex].file_ext, selector, + cformats[oindex].file_ext, tmpf); + + send_data = 0; + } else {/* Type is audio. */ + cmd = octstr_format("%s | %s > %s", + cformats[iindex].tostandard_cmd, + cformats[oindex].fromstandard_cmd, tmpf); + send_data = 1; + } + + pf = popen(octstr_get_cstr(cmd), "w"); + + if (!pf) + goto done; + + if (send_data) /* If this is not set then write the content... */ + n = octstr_print(pf, msg->body); + else + n = 0; + m = pclose(pf); + + if (n < 0 || m != 0) + goto done; /* Error -- finish up. */ + + s = octstr_read_file(tmpf); + if (s) + replace_body(msg, s, params_h, + cformats[oindex].content_type, + cformats[oindex].file_ext,0); + else + goto done; + + supported = 1; /* Means we have sorted it out. */ + + done: + if (!supported) + remove_object(msg, content_type); + + if (content_type) + octstr_destroy(content_type); + if (params) + octstr_destroy(params); + if (params_h) + http_destroy_headers(params_h); + if (tmpf[0]) + unlink(tmpf); + + if (tmpf2[0]) + unlink(tmpf2); + + if (cmd) + octstr_destroy(cmd); + return supported; +} + + +int mms_transform_msg(MmsMsg *inmsg, MmsUaProfile *prof, MmsMsg **outmsg) +{ + MIMEEntity *m; + Octstr *s; + + if (!prof) + return -1; + else if (!prof->ccppaccept.content) + return -2; + + m = mms_tomime(inmsg); + +#if 0 + mms_msgdump(inmsg); + debug("MMS UA PROF", 0, "### in ua prof"); + mime_entity_dump(m); +#endif + + modify_msg(m, prof); + + *outmsg = mms_frommime(m); + + mime_entity_destroy(m); + + s = mms_tobinary(*outmsg); + + if (octstr_len(s) > prof->maxmsgsize) { + mms_destroy(*outmsg); + *outmsg = NULL; + } + + octstr_destroy(s); + + /* Don't free profile! It is cached. */ + return 0; +} + +/* XXX Needs some work: Ensure we don't mod SMIL other than that specified in the start param. + * Also may be we should only mod the SMIL if content type of entity is multipart/related + * Also if the start param is missing we should perhaps stick it in. + */ + +static int format_special(MIMEEntity *m, + int trans_smil, char *txtmsg, char *htmlmsg, int *counter) +{ + int type; + Octstr *content_type = NULL, *params = NULL, *s; + int i, n; + unsigned long chash; + FILE *pf; + char tmpf[40]; + Octstr *cmd = NULL; + List *params_h; + + tmpf[0] = 0; + get_content_type(m->headers, &content_type, ¶ms); + params_h = get_value_parameters(params); + + if (m->multiparts && list_len(m->multiparts) > 0) { + MIMEEntity *pres = NULL; + int presindex = -1; + Octstr *presbody; + Octstr *start = http_header_value(params_h, octstr_imm("start")); + + for (i = 0, n = list_len(m->multiparts); imultiparts,i); + Octstr *ctype = NULL, *charset = NULL; + Octstr *cid = http_header_value(x->headers, octstr_imm("Content-ID")); + + http_header_get_content_type(x->headers, &ctype, &charset); + + /* Find presentation part: If we have start param, and it matches + * this one, and this one is SMIL, then... + */ + if (start && cid && octstr_compare(cid, start) == 0 && + octstr_case_compare(ctype, octstr_imm(PRES_TYPE)) == 0) { + pres = x; + presindex = i; + } + + if (ctype) octstr_destroy(ctype); + if (charset) octstr_destroy(charset); + if (cid) octstr_destroy(cid); + + format_special(x, trans_smil, txtmsg, htmlmsg, counter); + + } + + if (start) octstr_destroy(start); + + if (trans_smil && pres) { /* Reformat. */ + MIMEEntity *x; + Octstr *btxt = octstr_create(""); + + /* Remove type & start param from top level. */ + http_header_remove_all(params_h, "type"); + http_header_remove_all(params_h, "start"); + replace_ctype(m->headers, "multipart/related", params_h); + + + /* Put content ids on all siblings, and build html. */ + for (i = 0, n = list_len(m->multiparts); imultiparts,i); + Octstr *cid, *pname; + Octstr *ctype = NULL, *cparams = NULL; + Octstr *y, *loc, *cidurl; + List *cparamsl; + + if (x == pres) continue; /* Skip the presentation param. */ + + cid = http_header_value(x->headers, octstr_imm("Content-ID")); + if (cid == NULL) { + time_t t = time(NULL); + char ch[2]; + + ch[0] = ((t%2) ? 'A' : 'a') + (t%25); /* Make them a bit more unique. */ + ch[1] = 0; + + cid = octstr_format("", ch, *counter, (t%99989)); + http_header_add(x->headers, "Content-ID", octstr_get_cstr(cid)); + + ++*counter; + } + y = octstr_copy(cid, 1, octstr_len(cid) - 2); + loc = http_header_value(x->headers, octstr_imm("Content-Location")); + + cidurl = octstr_duplicate(y); + octstr_url_encode(cidurl); + + get_content_type(x->headers, &ctype, &cparams); /* Get type of object. */ + + if (cparams) { /* Get its name. */ + if ((cparamsl = get_value_parameters(cparams)) != NULL) { + pname = http_header_value(cparamsl, octstr_imm("name")); + http_destroy_headers(cparamsl); + } else + pname = octstr_duplicate(loc ? loc : y); + octstr_destroy(cparams); + } else + pname = octstr_imm("unknown"); + + if (octstr_case_search(ctype, octstr_imm("image/"), 0) == 0) + octstr_format_append(btxt, + "\n\"%S\"

\n", + cidurl, pname); + else if (octstr_case_search(ctype, octstr_imm("text/"), 0) == 0) + octstr_format_append(btxt, "%S

\n", + x->body ? x->body : octstr_imm("")); +#if 0 + else if (octstr_case_search(ctype, octstr_imm("audio/"), 0) == 0) + octstr_format_append(btxt, + "%S

\n", + cidurl, ctype, pname); +#endif + else + octstr_format_append(btxt, + "%S

\n", + ctype, cidurl, pname); + + http_header_remove_all(x->headers, "Content-Location"); + + if (y) octstr_destroy(y); + if (loc) octstr_destroy(loc); + if (ctype) octstr_destroy(ctype); + if (cidurl) octstr_destroy(cidurl); + if (pname) octstr_destroy(pname); + + octstr_destroy(cid); + } + + + http_header_remove_all(pres->headers, "Content-Type"); + http_header_add(pres->headers, "Content-Type", "multipart/alternative"); + + presbody = pres->body; + pres->body = NULL; + if (!pres->multiparts) + pres->multiparts = list_create(); + + + /* first the text part ... */ + x = mime_entity_create(); + http_header_add(x->headers, "Content-Type", "text/plain"); + x->body = octstr_create(txtmsg ? txtmsg : ""); + list_append(pres->multiparts, x); + + /* Lets also leave the pres part in there, just in case somebody knows how to handle it... */ + x = mime_entity_create(); + http_header_add(x->headers, "Content-Type", PRES_TYPE); + x->body = presbody; + list_append(pres->multiparts, x); + + + /* then the html part. */ + x = mime_entity_create(); + http_header_add(x->headers, "Content-Type", "text/html"); + x->body = octstr_format("
%s

%S
\n", + htmlmsg ? htmlmsg : "", btxt); + list_append(pres->multiparts, x); + + list_delete(m->multiparts, presindex, 1); /* Put it at the beginning. */ + list_insert(m->multiparts, 0, pres); +#if 0 + http_header_remove_all(pres->headers, "Content-ID"); /* Not needed anymore. */ +#endif + octstr_destroy(btxt); + + + } + goto done; + } + + + + if (octstr_case_search(content_type, octstr_imm("image/"), 0) == 0) + type = TIMAGE; + else if (octstr_case_search(content_type, octstr_imm("audio/"), 0) == 0) + type = TAUDIO; + else if (octstr_case_search(content_type, octstr_imm("text/"), 0) == 0) + type = TTEXT; + else + type = TOTHER; + + chash = hash_key(content_type); + + if (type == TAUDIO) { /* Find the preferred format (audio only, leave images alone!), + * assuming we can support this one. + */ + int ipref = -1, iindex = -1, o; + + for (i = 0; i < NELEMS(cformats); i++) { + if (ipref < 0 && cformats[i].t == type && + cformats[i].fromstandard_cmd) + ipref = i; + + if (cformats[i].chash == chash && + octstr_case_compare(content_type, + octstr_imm(cformats[i].content_type)) == 0) { + if (cformats[i].tostandard_cmd) + iindex = i; /* Only if the cmd exists actually. */ + break; + } + } + + if (iindex < 0 || ipref < 0) /* Can't change it.... */ + goto done; + + mktmpfname(tmpf); + if (type == TAUDIO) + cmd = octstr_format("%s | %s > %s", + cformats[iindex].tostandard_cmd, + cformats[ipref].fromstandard_cmd, tmpf); + else + cmd = octstr_format(IMGCONVERTCMD, + cformats[iindex].file_ext, "-", + cformats[ipref].file_ext, tmpf); + + pf = popen(octstr_get_cstr(cmd), "w"); + + if (!pf) + goto done; + n = octstr_print(pf, m->body); + o = pclose(pf); + + if (n < 0 || o != 0) + goto done; /* Error -- finish up. */ + + s = octstr_read_file(tmpf); + if (s) + replace_body(m, s, params_h, + cformats[ipref].content_type, + cformats[ipref].file_ext,1); + else + goto done; + + } else if (type == TTEXT) {/* change to default charset. */ + Octstr *charset = http_header_value(params_h, octstr_imm("charset")); + if (charset == NULL || + octstr_case_compare(charset, octstr_imm("unknown")) == 0 || + octstr_case_compare(charset, octstr_imm(DEFAULT_CHARSET)) == 0) { + if (charset) octstr_destroy(charset); + goto done; /* Nothing more to do here. */ + } + + if (charset_convert(m->body, octstr_get_cstr(charset), + DEFAULT_CHARSET) != -1) { /* using libiconv...*/ + Octstr *tmp, *ct; + + http_header_remove_all(params_h, "charset"); + http_header_add(params_h, "charset", DEFAULT_CHARSET); + tmp = make_value_parameters(params_h); + + ct = octstr_format("%S; %S", content_type, tmp); + octstr_destroy(tmp); + http_header_remove_all(m->headers, "Content-Type"); + http_header_add(m->headers, "Content-Type", octstr_get_cstr(ct)); + octstr_destroy(ct); + + } /* Else goto done. */ + + } /* Else do nothing. */ + + + + done: + + if (content_type) + octstr_destroy(content_type); + if (params_h) + http_destroy_headers(params_h); + if (cmd) + octstr_destroy(cmd); + + if (tmpf[0]) + unlink(tmpf); + + return 0; +} + +int mms_format_special(MmsMsg *inmsg, + int trans_smil, + char *txtmsg, + char *htmlmsg, MIMEEntity **outmsg) +{ + MIMEEntity *m = mms_tomime(inmsg); + int ct = 0; + + if (!m) + return -1; + + format_special(m, trans_smil, txtmsg, htmlmsg, &ct); + + *outmsg = m; + + return 0; + +} diff --git a/mbuni/mmlib/mms_uaprof.h b/mbuni/mmlib/mms_uaprof.h new file mode 100644 index 0000000..24208ea --- /dev/null +++ b/mbuni/mmlib/mms_uaprof.h @@ -0,0 +1,41 @@ +#ifndef __MMS_UAPROFILE_INCLUDED__ +#define __MMS_UAPROFILE_INCLUDED__ +#include "gwlib/gwlib.h" +#include "mms_msg.h" + +#define PRES_TYPE "application/smil" + +typedef struct MmsUaProfile MmsUaProfile; + +/* Start/stop profile engine. + * Starts a thread. Loads cache contents first. + */ +extern int mms_start_profile_engine(char *cache_dir); +extern int mms_stop_profile_engine(void); + +/* Gets the profile. Returns NULL if not cached or not existent. */ +extern MmsUaProfile *mms_get_ua_profile(char *url); + +/* Make a UA Profile out of the Accept HTTP headers. */ +extern MmsUaProfile *mms_make_ua_profile(List *req_headers); + +/* Transform Mms Message using the profile supplied. + * Returns 0 on success (and sets outmsg accordingly) + * -1 if profile cannot be fetched (e.g. temporarily) + * -2 if profile exists and UA does not support MMS. + * If returns 0 and sets outmsg to NULL then transformed message was + * too large for recipient (as per profile). + */ +extern int mms_transform_msg(MmsMsg *inmsg, MmsUaProfile *prof, MmsMsg **outmsg); + +/* Transforms the mms into a more 'readable' format: + * SMIL is turned into html (with an alternative of text) if trans_smil is set, + * image and audio changed to email preferred formats. + */ +extern int mms_format_special(MmsMsg *inmsg, + int trans_smil, + char *txtmsg, + char *htmlmsg, MIMEEntity **outmsg); + +#define DEFAULT_CHARSET "UTF-8" +#endif diff --git a/mbuni/mmlib/mms_util.c b/mbuni/mmlib/mms_util.c new file mode 100644 index 0000000..9a37fc0 --- /dev/null +++ b/mbuni/mmlib/mms_util.c @@ -0,0 +1,905 @@ +#include +#include +#include +#include +#include +#include "mms_util.h" +#include "mms_uaprof.h" + +#define MAXQTRIES 100 +#define BACKOFF_FACTOR 5*60 /* In seconds */ +#define QUEUERUN_INTERVAL 15*60 /* 15 minutes. */ +#define DEFAULT_EXPIRE 3600*24*7 /* One week */ +#define MMS_PORT 8191 /* Default content fetch port. */ + +static Octstr *cfg_getx(CfgGroup *grp, Octstr *item) +{ + Octstr *v = cfg_get(grp, item); + + + return v ? v : octstr_create(""); +} + + +static void *load_module(CfgGroup *grp, char *config_key, char *symbolname) +{ + Octstr *s; + void *retval = NULL; + + s = cfg_get(grp, octstr_imm(config_key)); + if (s) { + void *x = dlopen(octstr_get_cstr(s), RTLD_LAZY); + void *y = NULL; + + if (x == NULL || (y = dlsym(x, symbolname)) == NULL) + + panic(0, + "Error, unable to load dynamic libary (%s) for billing: " + "libhandle is %s, billing funcs is %s, err=%s", + octstr_get_cstr(s), + x ? "OK" : "Not OK", y ? "OK" : "Not OK", dlerror()); + else + retval = y; + + octstr_destroy(s); + } + return retval; +} + +MmsBoxSettings *mms_load_mmsbox_settings(Cfg *cfg) +{ + Octstr *s; + List *l; + CfgGroup *grp = cfg_get_single_group(cfg, octstr_imm("mmsbox")); + MmsBoxSettings *m = gw_malloc(sizeof *m); + long port = -1; + Octstr *user, *pass, *from; + + memset(m, 0, sizeof *m); + + s = cfg_getx(grp, octstr_imm("local-mmsc-domains")); + + if (s) { + l = octstr_split(s, octstr_imm(",")); + octstr_destroy(s); + } else + l = list_create(); + m->local_domains = l; + + if (cfg_get_integer(&m->maxthreads, grp, octstr_imm("max-send-threads")) == -1) + m->maxthreads = 10; + + m->unified_prefix = cfg_getx(grp, octstr_imm("unified-prefix")); + m->local_prefix = cfg_getx(grp, octstr_imm("local-prefixes")); + m->hostname = cfg_getx(grp, octstr_imm("hostname")); + + if (m->hostname == NULL || octstr_len(m->hostname) == 0) + m->hostname = octstr_create("localhost"); + + m->name = cfg_getx(grp, octstr_imm("name")); + m->host_alias = cfg_getx(grp, octstr_imm("host-alias")); + + m->sendmail = cfg_getx(grp, octstr_imm("send-mail-prog")); + + m->global_queuedir = cfg_getx(grp, octstr_imm("send-queue-directory")); + m->mm1_queuedir = cfg_getx(grp, octstr_imm("mm1-queue-directory")); + m->mm4_queuedir = cfg_getx(grp, octstr_imm("mm4-queue-directory")); + + if (cfg_get_integer(&m->maxsendattempts, grp, octstr_imm("maximum-send-attempts")) == -1) + m->maxsendattempts = MAXQTRIES; + + if (cfg_get_integer(&m->default_msgexpiry, grp, octstr_imm("default-message-expiry")) == -1) + m->default_msgexpiry = DEFAULT_EXPIRE; + + s = cfg_getx(grp, octstr_imm("queue-run-interval")); + if (!s || (m->queue_interval = atof(octstr_get_cstr(s))) <= 0) + m->queue_interval = QUEUERUN_INTERVAL; + + if (cfg_get_integer(&m->send_back_off, grp, octstr_imm("send-attempt-back-off")) == -1) + m->send_back_off = BACKOFF_FACTOR; + + /* Make send sms url. */ + m->sendsms_url = cfg_getx(grp, octstr_imm("sendsms-url")); + + user = cfg_getx(grp, octstr_imm("sendsms-username")); + pass = cfg_getx(grp, octstr_imm("sendsms-password")); + from = cfg_get(grp, octstr_imm("sendsms-global-sender")); + + if (user && octstr_len(user) > 1) + octstr_url_encode(user); + if (pass && octstr_len(pass) > 1) + octstr_url_encode(pass); + if (from && octstr_len(from) > 1) + octstr_url_encode(from); + + + octstr_format_append(m->sendsms_url, + (from && octstr_len(from) > 1) ? + "?username=%S&password=%S&from=%S" : + "?username=%S&password=%S", + user, + pass,from); + + m->system_user = octstr_format("system-user@%S", + m->hostname); + octstr_destroy(user); + octstr_destroy(pass); + if (from) octstr_destroy(from); + + cfg_get_integer(&port, grp, octstr_imm("mms-port")); + + m->port = (port > 0) ? port : MMS_PORT; + + m->allow_ip = cfg_getx(grp, octstr_imm("allow-ip")); + m->deny_ip = cfg_getx(grp, octstr_imm("deny-ip")); + + m->email2mmsrelay_prefixes = cfg_getx(grp, + octstr_imm("email2mms-relay-prefixes")); + m->ua_profile_cache_dir = cfg_getx(grp, + octstr_imm("ua-profile-cache-directory")); + + m->prov_notify = cfg_getx(grp,octstr_imm("prov-server-notify-script")); + m->prov_notify_arg = cfg_getx(grp,octstr_imm("prov-server-notify-script-arg")); + + m->prov_getstatus = cfg_getx(grp,octstr_imm("prov-server-sub-status-script")); + m->mms_notify_txt = cfg_getx(grp, octstr_imm("mms-notify-text")); + m->mms_notify_unprov_txt = cfg_getx(grp, octstr_imm("mms-notify-unprovisioned-text")); + m->mmbox_host = cfg_getx(grp, octstr_imm("mm-box-host")); + + m->mms_email_txt = cfg_getx(grp, octstr_imm("mms-to-email-txt")); + m->mms_email_html = cfg_getx(grp, octstr_imm("mms-to-email-html")); + + m->mms_toolarge = cfg_getx(grp, octstr_imm("mms-message-too-large-txt")); + + m->wap_gw_msisdn_header = cfg_get(grp, octstr_imm("mms-client-msisdn-header")); + if (!m->wap_gw_msisdn_header) m->wap_gw_msisdn_header = octstr_imm(XMSISDN_HEADER); + + m->wap_gw_ip_header = cfg_get(grp, octstr_imm("mms-client-ip-header")); + if (!m->wap_gw_ip_header) m->wap_gw_ip_header = octstr_imm(XIP_HEADER); + + cfg_get_bool(&m->notify_unprovisioned, grp, octstr_imm("notify-unprovisioned")); + + m->billing_params = cfg_getx(grp, + octstr_imm("billing-module-parameters")); + /* Get and load the billing lib if any. */ + + if ((m->mms_billfuncs = load_module(grp, "billing-library", "mms_billfuncs"))) { + if (m->mms_billfuncs->mms_billingmodule_init == NULL || + m->mms_billfuncs->mms_billmsg == NULL || + m->mms_billfuncs->mms_billingmodule_fini == NULL || + m->mms_billfuncs->mms_logcdr == NULL) + panic(0, "Missing or NULL functions in billing module!"); + } else + m->mms_billfuncs = &mms_billfuncs; /* The default one. */ + + m->mms_bill_module_data = m->mms_billfuncs->mms_billingmodule_init(octstr_get_cstr(m->billing_params)); + + m->resolver_params = cfg_getx(grp, + octstr_imm("billing-module-parameters")); + + /* Get and load the resolver lib if any. */ + if ((m->mms_resolvefuncs = load_module(grp, "billing-library", "mms_billfuncs"))) { + if (m->mms_resolvefuncs->mms_resolvermodule_init == NULL || + m->mms_resolvefuncs->mms_resolve == NULL || + m->mms_resolvefuncs->mms_resolvermodule_fini == NULL) + panic(0, "Missing or NULL functions in resolver module!"); + } else + m->mms_resolvefuncs = &mms_resolvefuncs; /* The default one. */ + + m->mms_resolver_module_data = m->mms_resolvefuncs->mms_resolvermodule_init(octstr_get_cstr(m->resolver_params)); + + return m; +} + +List *mms_proxy_relays(Cfg *cfg) +{ + List *gl = cfg_get_multi_group(cfg, octstr_imm("mmsproxy")); + int i, n; + List *l = list_create(); + + for (i = 0, n = list_len(gl); i < n; i++) { + CfgGroup *grp = list_get(gl, i); + MmsProxyRelay *m = gw_malloc(sizeof *m); + + m->host = cfg_getx(grp, octstr_imm("host")); + m->name = cfg_getx(grp, octstr_imm("name")); + m->allowed_prefix = cfg_getx(grp, octstr_imm("allowed-prefix")); + m->denied_prefix = cfg_getx(grp, octstr_imm("denied-prefix")); + + list_append(l, m); + } + + list_destroy(gl, NULL); + + return l; +} + + +Octstr *mms_makefetchurl(char *qf, Octstr *token, + MmsBoxSettings *settings) +{ + Octstr *url = octstr_create(""); + Octstr *host_alias = settings->host_alias; + Octstr *hstr; + + if (host_alias && octstr_len(host_alias) > 0) + hstr = octstr_duplicate(host_alias); + else + hstr = octstr_format("%S:%d", + settings->hostname, settings->port); + + octstr_format_append(url, "http://%S/%s/%S", + hstr, + qf, + token ? token : octstr_imm("x")); + octstr_destroy(hstr); + return url; +} + + +Octstr *mms_find_sender_msisdn(Octstr **send_url, List *request_hdrs, Octstr *msisdn_header) +{ + /* Either we have a WAP gateway header as defined, or we look for + * last part of url as our number. + */ + Octstr *xsend_url = *send_url; + + Octstr *phonenum = http_header_value(request_hdrs, + msisdn_header); + + if (!phonenum || octstr_len(phonenum) == 0) { + List *l = octstr_split(xsend_url, octstr_imm("/")); + + if (l && list_len(l) > 1) { + int i, n = list_len(l); + Octstr *s; + phonenum = octstr_duplicate(list_get(l, list_len(l) - 1)); + + /* After getting it, remove it from the end... */ + for (i = 0, s = octstr_create(""); i < n-1; i++) { + Octstr *p = list_get(l, i); + if (octstr_len(p) > 0) + octstr_format_append(s, "/%S", p); + } + octstr_destroy(xsend_url); + *send_url = s; + } + if (l) + list_destroy(l, (list_item_destructor_t *)octstr_destroy); + } + + return phonenum; +} + +Octstr *mms_find_sender_ip(List *request_hdrs, Octstr *ip_header, Octstr *ip, int *isv6) +{ + Octstr *xip; + /* Look in the headers, if none is defined, return actual IP */ + Octstr *client_ip = http_header_value(request_hdrs, ip_header); + char *s; + + xip = client_ip ? client_ip : ip; + + s = octstr_get_cstr(xip); + + /* Crude test for ipv6 */ + *isv6 = (index(s, ':') >= 0); + return xip; +} + +int mms_decodefetchurl(Octstr *fetch_url, + Octstr **qf, Octstr **token) +{ + Octstr *xfurl = octstr_duplicate(fetch_url); + int i, j, n; + char *s, *p; + + for (i = 0, n = 0, s = octstr_get_cstr(xfurl); + i < octstr_len(xfurl); i++) + if (s[i] == '/') + n++; + if (n < 2) /* We need at least two slashes. */ + octstr_append_char(xfurl, '/'); + + i = 0; + n = octstr_len(xfurl); + s = octstr_get_cstr(xfurl); + + p = strrchr(s, '/'); /* Find last slash. */ + if (p) + i = (p - s) - 1; + else + i = n-1; + + if (i < 0) + i = 0; + + while (i>0 && s[i] != '/') + i--; /* Go back, find first slash */ + if (i>=0 && s[i] == '/') + i++; + + /* Now we have qf, find its end. */ + + j = i; + while (jheaders); iheaders, i, &header, &value); + + if (header == NULL || + octstr_str_compare(header, "X-Unknown") == 0 || + octstr_search_chars(header, octstr_imm(" \n\t"), 0) >= 0) /* Don't allow space in the name. */ + goto loop; + + if (octstr_case_compare(header, octstr_imm("Cc")) == 0 || + octstr_case_compare(header, octstr_imm("To")) == 0 || + octstr_case_compare(header, octstr_imm("Bcc")) == 0) + skip = 0; + else + skip = 1; + /* XXX This may not be safe. Need to skip over quotes. */ + if (!skip && octstr_search_char(value, ',', 0) > 0 && + (l = http_header_split_value(value)) != NULL && + list_len(l) > 1) + for (j = 0, m = list_len(l); jheaders); + mm->headers = h; +} + + +/* Undo base64 content coding for mime entities that need it. */ +void unbase64_mimeparts(MIMEEntity *m) +{ + int i, n; + + if (m->multiparts && list_len(m->multiparts) > 0) + for (i = 0, n = list_len(m->multiparts); imultiparts, i)); + else { /* A non-multipart message .*/ + Octstr *ctype = http_header_value(m->headers, octstr_imm("Content-Type")); + Octstr *te = http_header_value(m->headers, octstr_imm("Content-Transfer-Encoding")); + + if (ctype && te && + octstr_case_compare(te,octstr_imm("base64")) == 0) { + octstr_base64_to_binary(m->body); + http_header_remove_all(m->headers, "Content-Transfer-Encoding"); + } + if (ctype) + octstr_destroy(ctype); + if (te) + octstr_destroy(te); + } +} +#if 1 +static int gw_isprint(int c) +{ + return isprint(c) || isspace(c); +} +#endif + +/* Change content coding for mime entities that need it. */ +void base64_mimeparts(MIMEEntity *m) +{ + int i, n; + + if (m->multiparts && list_len(m->multiparts) > 0) + for (i = 0, n = list_len(m->multiparts); imultiparts, i)); + else { /* A non-multipart message .*/ + Octstr *ctype = http_header_value(m->headers, octstr_imm("Content-Type")); + Octstr *te = http_header_value(m->headers, octstr_imm("Content-Transfer-Encoding")); + + if (ctype && !te +#if 1 +&& + octstr_check_range(m->body, 0, octstr_len(m->body), gw_isprint) == 0 +#endif + ) { + octstr_binary_to_base64(m->body); + http_header_add(m->headers, "Content-Transfer-Encoding", "base64"); + } + if (ctype) + octstr_destroy(ctype); + if (te) + octstr_destroy(te); + } +} + +void notify_prov_server(char *cmd, char *from, char *event, char *arg) +{ + Octstr *s; + + if (cmd == NULL || cmd[0] == '\0') + return; + s = octstr_format("%s '%s' '%s' '%s'", cmd, event, from, arg); + if (s) { + system(octstr_get_cstr(s)); + octstr_destroy(s); + } +} + +int mms_ind_send(Octstr *prov_cmd, Octstr *to) +{ + + Octstr *s; + int res = 1; + + if (prov_cmd == NULL || + octstr_len(prov_cmd) == 0) + return 1; + + s = octstr_format("%S %S", prov_cmd, to); + + if (s) { + int x = system(octstr_get_cstr(s)); + int y = WEXITSTATUS(x); + + if (x < 0) { + error(0, "Checking MMS Ind.Send: Failed to run command %s!", + octstr_get_cstr(s)); + res = 1; + } else if (y != 0 && y != 1) + res = -1; + else + res = y; + octstr_destroy(s); + } else + warning(0, "Checking MMS Ind.Send: Failed call to compose command [%s] ", + octstr_get_cstr(prov_cmd)); + + + return res; +} + + +static void addmmscname(Octstr *s, Octstr *myhostname) +{ + int j; + int len = octstr_len(s); + + if (octstr_search_char(s, '@', 0) >= 0) + return; /* Nothing to do. */ + + j = octstr_case_search(s, octstr_imm("/TYPE=PLMN"), 0); + if (j > 0 && j - 1 + sizeof "/TYPE=PLMN" == len) { /* A proper number. */ + octstr_delete(s, j, -1 + sizeof "/TYPE=PLMN"); /* XXX We strip off /TYPE=PLMN, should we ? */ + octstr_format_append(s, "@%S", myhostname); + } + +} +static int send2email(Octstr *to, Octstr *from, Octstr *subject, + Octstr *msgid, + MIMEEntity *m, int append_hostname, Octstr **error, + char *sendmail_cmd, Octstr *myhostname) +{ + Octstr *s; + FILE *f; + int ret = MMS_SEND_OK, i; + Octstr *cmd = octstr_create(""); + + if (append_hostname) { /* Add our hostname to all phone numbers. */ + int i, n; + List *l = http_create_empty_headers(); + Octstr *xfrom = http_header_value(m->headers, octstr_imm("From")); + List *lto = http_header_find_all(m->headers, "To"); + List *lcc = http_header_find_all(m->headers, "Cc"); + + if (xfrom) { + addmmscname(xfrom, myhostname); + http_header_add(l, "From", octstr_get_cstr(xfrom)); + octstr_destroy(xfrom); + } + http_header_remove_all(m->headers, "From"); + + for (i = 0, n = list_len(lto); i < n; i++) { + Octstr *name, *value; + + http_header_get(lto, i, &name, &value); + + if (!value || !name || + octstr_case_compare(name, octstr_imm("To")) != 0) + goto loop; + + addmmscname(value, myhostname); + http_header_add(l, "To", octstr_get_cstr(value)); + loop: + if (value) octstr_destroy(value); + if (name) octstr_destroy(name); + } + + http_destroy_headers(lto); + http_header_remove_all(m->headers, "To"); + + for (i = 0, n = list_len(lcc); i < n; i++) { + Octstr *name, *value; + + http_header_get(lcc, i, &name, &value); + + if (!value || !name || + octstr_case_compare(name, octstr_imm("Cc")) != 0) + goto loop2; + + addmmscname(value, myhostname); + http_header_add(l, "Cc", octstr_get_cstr(value)); + loop2: + if (value) octstr_destroy(value); + if (name) octstr_destroy(name); + } + + http_destroy_headers(lcc); + http_header_remove_all(m->headers, "Cc"); + + http_append_headers(m->headers, l); /* combine old with new. */ + http_destroy_headers(l); + } + + /* Pack headers, get string rep of mime entity. */ + http_header_pack(m->headers); + s = mime_entity_to_octstr(m); + + /* + * Make the command: Transpose % formatting characters: + * f - from address + * t - recipient + * s - subject + * m - message id + */ + + i = 0; + for (;;) { + while (sendmail_cmd[i]) { + char c = sendmail_cmd[i]; + if (c == '%' && sendmail_cmd[i + 1]) + break; + octstr_append_char(cmd, c); + i++; + } + if (!sendmail_cmd[i]) + break; + + switch(sendmail_cmd[i+1]) { + case 't': + octstr_append(cmd, to); + break; + case 'f': + if (append_hostname) { + Octstr *xfrom = octstr_duplicate(from); + addmmscname(xfrom, myhostname); + octstr_append(cmd, xfrom); + octstr_destroy(xfrom); + } else + octstr_append(cmd, from); + break; + case 's': + octstr_append(cmd, subject); + break; + case 'm': + octstr_append(cmd, msgid); + break; + case '%': + octstr_format_append(cmd, "%%"); + break; + default: + octstr_format_append(cmd, "%%%c", sendmail_cmd[i+1]); + break; + } + i += 2; + } + + + debug("mms.sendtoemail", 0, "preparing to execute %s to send to email: ", octstr_get_cstr(cmd)); + + if ((f = popen(octstr_get_cstr(cmd), "w")) == NULL) { + *error = octstr_format("popen failed for %S: %d: %s", + cmd, errno, strerror(errno)); + ret = MMS_SEND_ERROR_TRANSIENT; + goto done; + } + + if (octstr_print(f, s) < 0) { + *error = octstr_format("send email failed in octstr_print %d: %s", + errno, strerror(errno)); + pclose(f); + ret = MMS_SEND_ERROR_TRANSIENT; + goto done; + } + + if ((ret = pclose(f)) != 0) { + *error = octstr_format("Send email command returned non-zero %d: errno=%s", + ret, strerror(errno)); + ret = MMS_SEND_ERROR_TRANSIENT; + } else + ret = MMS_SEND_OK; + + done: + octstr_destroy(cmd); + octstr_destroy(s); + return ret; +} + + +/* Send this message to email recipient. */ +int mms_sendtoemail(Octstr *from, Octstr *to, + Octstr *subject, Octstr *msgid, + MmsMsg *msg, int dlr, Octstr **error, char *sendmail_cmd, + Octstr *myhostname, + int trans_msg, + int trans_smil, char *txt, char *html, + int append_hostname) +{ + + MIMEEntity *m = NULL; + + List *newhdrs = http_create_empty_headers(); + int ret; + + if (!to || + octstr_search_char(to, '@', 0) < 0) { + *error = octstr_format("Invalid email address %S!", to); + return MMS_SEND_ERROR_FATAL; + } + + if (!trans_msg) + m = mms_tomime(msg); + else + if ((ret = mms_format_special(msg, trans_smil, txt, html, &m)) < 0 || + m == NULL) { + warning(0, "MMS: send2email failed to format message (msg=%s,ret=%d)", + m ? "OK" : "Not transformed",ret); + return -ret; + } + + base64_mimeparts(m); + + /* Before we send it, we insert some email friendly headers if they are missing. */ + http_header_add(newhdrs, "Subject", subject ? octstr_get_cstr(subject) : "MMS Message"); + http_header_add(newhdrs, "From", octstr_get_cstr(from)); + http_header_add(newhdrs, "To", octstr_get_cstr(to)); + http_header_add(newhdrs, "Message-ID", msgid ? octstr_get_cstr(msgid) : ""); + http_header_combine(newhdrs, m->headers); + http_destroy_headers(m->headers); + m->headers = newhdrs; + + + ret = send2email(to, from, subject, msgid, m, append_hostname, error, sendmail_cmd, myhostname); + mime_entity_destroy(m); + + return ret; +} + +void mms_log2(char *logmsg, Octstr *from, Octstr *to, + int msize, Octstr *msgid, + Octstr *acct, + Octstr *viaproxy, + char *interface, Octstr *ua) +{ + List *l; + if (to) { + l = list_create(); + list_append(l, to); + } else + l = NULL; + + mms_log(logmsg, from,l,msize,msgid,acct,viaproxy,interface,ua); + + if (l) + list_destroy(l, NULL); +} + +void mms_log(char *logmsg, Octstr *from, List *to, + int msize, Octstr *msgid, + Octstr *acct, + Octstr *viaproxy, + char *interface, Octstr *ua) +{ + Octstr *xto = octstr_create(""); + int i, n = to ? list_len(to) : 0; + + for (i = 0; i < n; i++) + octstr_format_append(xto, + "%s%S", + (i == 0) ? "" : ", ", + list_get(to,i)); + + alog("%s MMS [INT:%s] [ACT:%s] [MMSC:%s] [from:%s] [to:%s] [msgid:%s] [size=%d] [UA:%s]", + logmsg, interface, + acct ? octstr_get_cstr(acct) : "", + viaproxy ? octstr_get_cstr(viaproxy) : "", + from ? octstr_get_cstr(from) : "", + octstr_get_cstr(xto), + msgid ? octstr_get_cstr(msgid) : "", + msize, + ua ? octstr_get_cstr(ua) : ""); + + octstr_destroy(xto); +} + diff --git a/mbuni/mmlib/mms_util.h b/mbuni/mmlib/mms_util.h new file mode 100644 index 0000000..506311f --- /dev/null +++ b/mbuni/mmlib/mms_util.h @@ -0,0 +1,159 @@ +#ifndef __MMS_GLOBAL__INCLUDED__ +#define __MMS_GLOBAL__INCLUDED__ + +#include "gwlib/gwlib.h" +#include "gwlib/mime.h" + +#include "config.h" +#include "mms_strings.h" +#include "mms_msg.h" +#include "mms_billing.h" +#include "mms_resolve.h" + +/* Send errors */ +#define MMS_SEND_OK 0 +#define MMS_SEND_ERROR_TRANSIENT -1 +#define MMS_SEND_ERROR_FATAL -2 + +#define SEND_ERROR_STR(e) ((e) == MMS_SEND_OK ? "Sent" : (e) == MMS_SEND_ERROR_TRANSIENT ? "Retry later" : "Failed") +/* Useful headers. */ +#define XMSISDN_HEADER "X-WAP-Network-Client-MSISDN" +#define XIP_HEADER "X-WAP-Network-Client-Address" +#define MM_NAME "DS_MM" + +typedef struct MmsProxyRelay { + Octstr *host; + Octstr *name; + Octstr *allowed_prefix; + Octstr *denied_prefix; +} MmsProxyRelay; + +typedef struct MmsBoxSettings { + Octstr *system_user; + Octstr *name, *hostname, *host_alias; + List *local_domains; + Octstr *unified_prefix, *local_prefix; + Octstr *sendmail; + Octstr *global_queuedir, *mm1_queuedir, *mm4_queuedir; + + Octstr *ua_profile_cache_dir; + + long maxthreads; + long maxsendattempts; + long default_msgexpiry; + double queue_interval; + long send_back_off; + + long port; + + Octstr *allow_ip; + Octstr *deny_ip; + + Octstr *email2mmsrelay_prefixes; + Octstr *sendsms_url; +#if 0 + Octstr *sendsms_user, *sendsms_pass, *sendsms_globalsender; +#endif + Octstr *billing_params; + + MmsBillingFuncStruct *mms_billfuncs; /* Link to billing funcs. */ + void *mms_bill_module_data; + + Octstr *resolver_params; + MmsResolverFuncStruct *mms_resolvefuncs; /* Link to resolver funcs. */ + void *mms_resolver_module_data; + + Octstr *prov_notify; + Octstr *prov_notify_arg; + Octstr *prov_getstatus; + int notify_unprovisioned; + Octstr *mms_notify_txt; + Octstr *mms_notify_unprov_txt; + Octstr *mms_toolarge; + Octstr *mmbox_host; + + Octstr *mms_email_txt; + Octstr *mms_email_html; + Octstr *wap_gw_msisdn_header; + Octstr *wap_gw_ip_header; +} MmsBoxSettings; + +/* Global variables and shared code used by all modules. */ + + +extern char *mms_hostname; /* Our hostname. */ +#define MMSC_VERSION VERSION +#define MMS_NAME PACKAGE + +#if 0 +#define EMAILPROFILE "http://localhost/email.xml" /* For now we hard code it. */ +#define MMBOXPROFILE "http://localhost/mmbox.xml" +#endif + +/* Returns mmsbox settings. */ +MmsBoxSettings *mms_load_mmsbox_settings(Cfg *cfg); + + +/* Returns list of MmsProxyRelay */ +extern List *mms_proxy_relays(Cfg *cfg); + +extern Octstr *mms_makefetchurl(char *qf, Octstr *token, + MmsBoxSettings *settings); + +extern Octstr *mms_maketransid(char *qf, Octstr *mmscname); + +extern Octstr *mms_getqf_fromtransid(Octstr *transid); + +extern int mms_decodefetchurl(Octstr *fetch_url, + Octstr **qf, Octstr **token); + +Octstr *mms_find_sender_msisdn(Octstr **send_url, List *request_hdrs, Octstr *msisdn_header); +Octstr *mms_find_sender_ip(List *request_hdrs, Octstr *ip_header, Octstr *ip, int *isv6); + +extern Octstr *mms_isodate(time_t t); +void mms_lib_init(void); + +/* get content type while stripping params. If any param is null, fails. */ +int get_content_type(List *hdrs, Octstr **type, Octstr **params); + +/* Returns a list of parameters as http_headers given the semi-comma delimited string.*/ +List *get_value_parameters(Octstr *params); + +Octstr *make_value_parameters(List *params); + +/* Where value is comma-separated, make separate header items. */ +void unpack_mimeheaders(MIMEEntity *m); + +/* Where element has base64 encoding, decode. */ +void unbase64_mimeparts(MIMEEntity *m); + +/* Where element contains binary data, encode it base64. */ +void base64_mimeparts(MIMEEntity *m); + +void notify_prov_server(char *cmd, char *from, char *event, char *arg); +int mms_ind_send(Octstr *prov_cmd, Octstr *to); + + +/* Send this message to email recipient: Returns 0 on success 1 or 2 on profile error + * (negate to get right one), -ve on some other error + */ +int mms_sendtoemail(Octstr *from, Octstr *to, + Octstr *subject, Octstr *msgid, + MmsMsg *msg, int dlr, Octstr **error, + char *sendmail_cmd, Octstr *myhostname, + int trans_msg, + int trans_smil, char *txt, char *html, int append_hostname); + +/* log to access log. */ +void mms_log(char *logmsg, Octstr *from, List *to, + int msize, Octstr *msgid, + Octstr *acct, + Octstr *viaproxy, + char *interface, Octstr *ua); + +void mms_log2(char *logmsg, Octstr *from, Octstr *to, + int msize, Octstr *msgid, + Octstr *acct, + Octstr *viaproxy, + char *interface, Octstr *ua); +#endif diff --git a/mbuni/mmsc/Makefile.am b/mbuni/mmsc/Makefile.am new file mode 100644 index 0000000..f0e0626 --- /dev/null +++ b/mbuni/mmsc/Makefile.am @@ -0,0 +1,2 @@ +bin_PROGRAMS = mmsglobalsender mmsproxy mmsmobilesender mmsfromemail mmssend + diff --git a/mbuni/mmsc/Makefile.in b/mbuni/mmsc/Makefile.in new file mode 100644 index 0000000..7a164ad --- /dev/null +++ b/mbuni/mmsc/Makefile.in @@ -0,0 +1,425 @@ +# Makefile.in generated by automake 1.9.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +SOURCES = mmsfromemail.c mmsglobalsender.c mmsmobilesender.c mmsproxy.c mmssend.c + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +bin_PROGRAMS = mmsglobalsender$(EXEEXT) mmsproxy$(EXEEXT) \ + mmsmobilesender$(EXEEXT) mmsfromemail$(EXEEXT) \ + mmssend$(EXEEXT) +subdir = mmsc +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(bin_PROGRAMS) +mmsfromemail_SOURCES = mmsfromemail.c +mmsfromemail_OBJECTS = mmsfromemail.$(OBJEXT) +mmsfromemail_LDADD = $(LDADD) +mmsglobalsender_SOURCES = mmsglobalsender.c +mmsglobalsender_OBJECTS = mmsglobalsender.$(OBJEXT) +mmsglobalsender_LDADD = $(LDADD) +mmsmobilesender_SOURCES = mmsmobilesender.c +mmsmobilesender_OBJECTS = mmsmobilesender.$(OBJEXT) +mmsmobilesender_LDADD = $(LDADD) +mmsproxy_SOURCES = mmsproxy.c +mmsproxy_OBJECTS = mmsproxy.$(OBJEXT) +mmsproxy_LDADD = $(LDADD) +mmssend_SOURCES = mmssend.c +mmssend_OBJECTS = mmssend.$(OBJEXT) +mmssend_LDADD = $(LDADD) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = mmsfromemail.c mmsglobalsender.c mmsmobilesender.c \ + mmsproxy.c mmssend.c +DIST_SOURCES = mmsfromemail.c mmsglobalsender.c mmsmobilesender.c \ + mmsproxy.c mmssend.c +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXE_EXT = @EXE_EXT@ +GW_CONFIG = @GW_CONFIG@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host_alias = @host_alias@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu mmsc/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu mmsc/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ + rm -f "$(DESTDIR)$(bindir)/$$f"; \ + done + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +mmsfromemail$(EXEEXT): $(mmsfromemail_OBJECTS) $(mmsfromemail_DEPENDENCIES) + @rm -f mmsfromemail$(EXEEXT) + $(LINK) $(mmsfromemail_LDFLAGS) $(mmsfromemail_OBJECTS) $(mmsfromemail_LDADD) $(LIBS) +mmsglobalsender$(EXEEXT): $(mmsglobalsender_OBJECTS) $(mmsglobalsender_DEPENDENCIES) + @rm -f mmsglobalsender$(EXEEXT) + $(LINK) $(mmsglobalsender_LDFLAGS) $(mmsglobalsender_OBJECTS) $(mmsglobalsender_LDADD) $(LIBS) +mmsmobilesender$(EXEEXT): $(mmsmobilesender_OBJECTS) $(mmsmobilesender_DEPENDENCIES) + @rm -f mmsmobilesender$(EXEEXT) + $(LINK) $(mmsmobilesender_LDFLAGS) $(mmsmobilesender_OBJECTS) $(mmsmobilesender_LDADD) $(LIBS) +mmsproxy$(EXEEXT): $(mmsproxy_OBJECTS) $(mmsproxy_DEPENDENCIES) + @rm -f mmsproxy$(EXEEXT) + $(LINK) $(mmsproxy_LDFLAGS) $(mmsproxy_OBJECTS) $(mmsproxy_LDADD) $(LIBS) +mmssend$(EXEEXT): $(mmssend_OBJECTS) $(mmssend_DEPENDENCIES) + @rm -f mmssend$(EXEEXT) + $(LINK) $(mmssend_LDFLAGS) $(mmssend_OBJECTS) $(mmssend_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmsfromemail.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmsglobalsender.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmsmobilesender.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmsproxy.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmssend.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: install-binPROGRAMS + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-info-am + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-binPROGRAMS uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/mbuni/mmsc/mmsfromemail.c b/mbuni/mmsc/mmsfromemail.c new file mode 100644 index 0000000..c3e9d26 --- /dev/null +++ b/mbuni/mmsc/mmsfromemail.c @@ -0,0 +1,397 @@ +/* + * The Email2MMS interface. Receives email and queues it to MMS. This program + * also implements the MM4 interface by detecting MMS headers in the email. + */ + +#include +#include "mms_queue.h" +#include "mms_util.h" + +static MmsBoxSettings *settings; + +static Octstr *xfrom; +static Octstr *xto; +static Octstr *xproxy; + +enum {TPLMN, TEMAIL, TOTHER} ttype; + +static int find_own(int i, int argc, char *argv[]); +static int isphonenum(Octstr *s); +static void fixup_recipient(void); +static void fixup_sender(void); + +static Cfg *cfg; +static List *proxyrelays; +int main(int argc, char *argv[]) +{ + int cfidx; + Octstr *fname; + CfgGroup *grp; + Octstr *log, *alog; + long loglevel; + MIMEEntity *mm; + MmsMsg *msg; + + Octstr *email; + + mms_lib_init(); + + srandom(time(NULL)); + + cfidx = get_and_set_debugs(argc, argv, find_own); + + if (argv[cfidx] == NULL) + fname = octstr_imm("mmsc.conf"); + else + fname = octstr_create(argv[cfidx]); + + cfg = cfg_create(fname); + + if (cfg_read(cfg) == -1) + panic(0, "Couldn't read configuration from '%s'.", octstr_get_cstr(fname)); + + octstr_destroy(fname); + + info(0, "----------------------------------------"); + info(0, " MMSC Email2MMS Tool version %s starting", MMSC_VERSION); + + grp = cfg_get_single_group(cfg, octstr_imm("core")); + log = cfg_get(grp, octstr_imm("log-file")); + if (log != NULL) { + if (cfg_get_integer(&loglevel, grp, octstr_imm("log-level")) == -1) + loglevel = 0; + log_open(octstr_get_cstr(log), loglevel, GW_NON_EXCL); + octstr_destroy(log); + } + + + /* Get access log and open it. */ + alog = cfg_get(grp, octstr_imm("access-log")); + if (alog) { + alog_open(octstr_get_cstr(alog), 1, 1); + octstr_destroy(alog); + } + + /* Load proxy relays. */ + + proxyrelays = mms_proxy_relays(cfg); + + /* Load settings. */ + settings = mms_load_mmsbox_settings(cfg); + + if (!settings) + panic(0, "No global MMSC configuration!"); + + if (!xto || !xfrom) + panic(0, "usage: %s -f from -t to!", argv[0]); + + /* normalize recipient address, then if phone number, + * check whether we are allowed to process. + */ + fixup_recipient(); + fixup_sender(); + + if (!xto || + (ttype == TPLMN && !does_prefix_match(settings->email2mmsrelay_prefixes, xto))) { + error(0, " Not allowed to send to this recipient %s!", + xto ? octstr_get_cstr(xto) : "(null)"); + return -1; + } + + + email = octstr_read_pipe(stdin); + + if (!email || octstr_len(email) == 0) { + error(0, "Empty email message!"); + return -1; + } + + if ((mm = mime_octstr_to_entity(email)) == NULL) { + error(0, "Unable to decode mime entity!"); + return -1; + } + octstr_destroy(email); + + /* Take the entity, recode it --> remove base64 stuff, split headers. */ + unbase64_mimeparts(mm); + unpack_mimeheaders(mm); + + /* Now convert from mime to MMS message. */ + msg = mms_frommime(mm); + mime_entity_destroy(mm); + + if (!msg) { + error(0, "Unable to create MM!"); + return -1; + } + + switch(mms_messagetype(msg)) { + case MMS_MSGTYPE_SEND_REQ: + if (ttype != TPLMN ||/* We only send to phones from this interface */ + !does_prefix_match(settings->email2mmsrelay_prefixes, + xto)) { + error(0, "Not allowed to send to %s!", octstr_get_cstr(xto)); + } else { + List *lto = list_create(); + Octstr *qf; + Octstr *msgid = mms_get_header_value(msg, octstr_imm("Message-ID")); + Octstr *transid = mms_get_header_value(msg, octstr_imm("X-Mms-Transaction-ID")); + Octstr *dreport = mms_get_header_value(msg, + octstr_imm("X-Mms-Delivery-Report")); + char *err; + Octstr *rto; + int dlr; + + octstr_format_append(xto, "/TYPE=PLMN"); + list_append(lto, xto); + + if (dreport && + octstr_case_compare(dreport, octstr_imm("Yes")) == 0) + dlr = 1; + else + dlr = 0; + + qf = mms_queue_add(xfrom, lto, msgid, NULL, xproxy, NULL, + 0, time(NULL) + settings->default_msgexpiry, msg, NULL, dlr, + octstr_get_cstr(settings->global_queuedir)); + if (qf) { + + info(0, "Email2MMS Queued message to %s from %s (via %s) => %s", + octstr_get_cstr(xto), octstr_get_cstr(xfrom), + xproxy ? octstr_get_cstr(xproxy) : "(None)", octstr_get_cstr(qf)); + octstr_destroy(qf); + + /* Queue our response to the chap. */ + err = "Ok"; + } else + err = "Error-transient-failure"; + if (xproxy) { + MmsMsg *mresp; + List *xlto; + mresp = mms_sendconf(err, octstr_get_cstr(msgid), + transid ? octstr_get_cstr(transid) : "001", + 0); + + rto = octstr_format("system-user@%S", xproxy); + xlto = list_create(); + list_append(xlto, rto); + + qf = mms_queue_add(settings->system_user, xlto, msgid, NULL, + xproxy, NULL, + 0, time(NULL) + settings->default_msgexpiry, + mresp, NULL, 0, + octstr_get_cstr(settings->global_queuedir)); + + list_destroy(xlto, (list_item_destructor_t *)octstr_destroy); + mms_destroy(mresp); + octstr_destroy(qf); + } + + mms_log("Received", xfrom, lto, + -1, msgid, NULL, xproxy, xproxy ? "MM4" : "MM3", NULL); + + list_destroy(lto,NULL); + octstr_destroy(transid); + octstr_destroy(msgid); + if (dreport) + octstr_destroy(dreport); + + } + break; + + case MMS_MSGTYPE_SEND_CONF: + { + Octstr *transid = mms_get_header_value(msg, octstr_imm("X-Mms-Transaction-ID")); + Octstr *qf = mms_getqf_fromtransid(transid); + + octstr_destroy(transid); + if (qf) { + MmsEnvelope *e = mms_queue_readenvelope(octstr_get_cstr(qf), + octstr_get_cstr(settings->mm4_queuedir), + 1); + if (!e) + warning(0, "Received confirm MMS but cannot find message %s [%s] in queue!", + octstr_get_cstr(transid), + octstr_get_cstr(qf)); + else { + MmsEnvelopeTo *t; + int i, n; + + for (i = 0, n = list_len(e->to); ito, i)) != NULL) + t->process = 0; /* Should make it go away. */ + mms_queue_update(e); + info(0, "Email2MMS received send conf from proxy %s to %s from %s => %s", + octstr_get_cstr(xproxy), octstr_get_cstr(xto), + octstr_get_cstr(xfrom), + octstr_get_cstr(qf)); + } + + } else + warning(0, "Received confirm MMS but cannot find message %s in queue!", + octstr_get_cstr(transid)); + } + break; + case MMS_MSGTYPE_DELIVERY_IND: + if (ttype != TPLMN ||/* We only send to phones from this interface */ + !does_prefix_match(settings->email2mmsrelay_prefixes, + xto)) { + error(0, "Not allowed to send to %s!", octstr_get_cstr(xto)); + } else { + List *lto = list_create(); + Octstr *qf; + Octstr *msgid = mms_get_header_value(msg, octstr_imm("Message-ID")); + + octstr_format_append(xto, "/TYPE=PLMN"); + list_append(lto, xto); + qf = mms_queue_add(xfrom, lto, msgid, NULL, + xproxy, NULL, + 0, time(NULL) + settings->default_msgexpiry, msg, NULL, 0, + octstr_get_cstr(settings->global_queuedir)); + list_destroy(lto, NULL); + if (qf) { + info(0, "Email2MMS Queued DLR from proxy %s to %s from %s => %s", + octstr_get_cstr(xproxy), octstr_get_cstr(xto), octstr_get_cstr(xfrom), + octstr_get_cstr(qf)); + + octstr_destroy(qf); + } + + octstr_destroy(msgid); + list_destroy(lto, NULL); + } + break; + + case MMS_MSGTYPE_READ_REC_IND: + + mms_convert_readrec2readorig(msg); + + /* Fall through. */ + case MMS_MSGTYPE_READ_ORIG_IND: + if (ttype != TPLMN ||/* We only send to phones from this interface */ + !does_prefix_match(settings->email2mmsrelay_prefixes, + xto)) { + error(0, "Not allowed to send to %s!", octstr_get_cstr(xto)); + } else { + List *lto = list_create(); + Octstr *qf; + Octstr *msgid = mms_get_header_value(msg, octstr_imm("Message-ID")); + + octstr_format_append(xto, "/TYPE=PLMN"); + list_append(lto, xto); + qf = mms_queue_add(xfrom, lto, msgid, NULL, + xproxy, NULL, + 0, time(NULL) + settings->default_msgexpiry, msg, NULL,0, + octstr_get_cstr(settings->global_queuedir)); + list_destroy(lto, NULL); + if (qf) { + info(0, "Email2MMS Queued read report from proxy %s to %s from %s => %s", + octstr_get_cstr(xproxy), octstr_get_cstr(xto), octstr_get_cstr(xfrom), + octstr_get_cstr(qf)); + + octstr_destroy(qf); + } + + octstr_destroy(msgid); + list_destroy(lto, NULL); + } + default: + { + int x = mms_messagetype(msg); + warning(0, "Unexpected message type: %d=>%s", + x, + mms_message_type_to_cstr(x)); + break; + } + } + mms_destroy(msg); + + return 0; + +} + + +static int find_own(int i, int argc, char *argv[]) +{ + if (argv[i][1] == 'f') + if (i + 1 < argc) { + xfrom = octstr_create(argv[i+1]); + return 1; + } else + return -1; + else if (argv[i][1] == 't') /* recipient. */ + if (i + 1 < argc) { + xto = octstr_create(argv[i+1]); + return 1; + } else + return -1; + else if (argv[i][1] == 'p') /* Proxy name if any. */ + if (i + 1 < argc) { + xproxy = octstr_create(argv[i+1]); + return 1; + } else + return -1; + else + return -1; +} + +static int isphonenum(Octstr *s) +{ + int i = 0, n = octstr_len(s); + char *cs; + + if (s && octstr_len(s) >= 1 && + octstr_get_cstr(s)[0] == '+') + i++; + for ( cs = octstr_get_cstr(s); i0) + octstr_delete(xto, i, octstr_len(xto)); + + i = octstr_search(xto, octstr_imm("/TYPE="), 0); + if (i > 0) { + typ = octstr_copy(xto, i+1, octstr_len(xto)); + octstr_delete(xto, i, octstr_len(xto)); + } + + if (isphonenum(xto) && + (!typ || octstr_str_compare(typ, "TYPE=PLMN") == 0)) { /* A phone number. */ + normalize_number(octstr_get_cstr(settings->unified_prefix), &xto); + ttype = TPLMN; + } else { /* For now everything else is email. */ + ttype = TEMAIL; + } + octstr_destroy(typ); +} + +static void fixup_sender(void) +{ + int i; + + + if (!xfrom) xfrom = octstr_imm(""); + i = octstr_search_char(xfrom, '@', 0); + if (i>0) + return; + + i = octstr_search(xfrom, octstr_imm("/TYPE="), 0); + if (i > 0) + return; + + if (isphonenum(xfrom)) + octstr_append(xfrom, octstr_imm("/TYPE=PLMN")); + else + octstr_append(xfrom, octstr_imm("@unknown")); +} diff --git a/mbuni/mmsc/mmsglobalsender.c b/mbuni/mmsc/mmsglobalsender.c new file mode 100644 index 0000000..41c559e --- /dev/null +++ b/mbuni/mmsc/mmsglobalsender.c @@ -0,0 +1,488 @@ +/* + * The global queue runner, dispatches messages to email, mobile and to other proxies. + */ + +#include +#include +#include +#include "mms_queue.h" +#include "mms_uaprof.h" +#include "mms_util.h" + +#define NMAX 256 +static char mobile_qdir[NMAX]; +static char mm4_qdir[NMAX]; +static char sendmail_cmd[NMAX]; + + +/* Set the queue directory for messages going to mobile. */ +static int mms_setmobile_queuedir(char *mqdir); + +/* Set the queue directory for messages going to remote proxies. */ +static int mms_setmm4_queuedir(char *mqdir); + +/* Send command for sending mail. It will be called as with + * headers and message on stdin. + * + * The following % formatting characters are allowed: + * f - from address + * t - recipient + * s - subject + * m - message id + * + */ +static int mms_setsendmail_cmd(char *sendmail); + +/* Queue this message for delivery to mobile terminal. */ +static int mms_sendtomobile(Octstr *from, Octstr *to, + Octstr *subject, Octstr *fromproxy, + Octstr *msgid, time_t expires, MmsMsg *m, int dlr, Octstr **error); + +/* Send this message via an intermediate proxy (MM4 interface). + * The caller must modify the MmsMsg sender and recipient address if necessary. + */ +static int mms_sendtoproxy(Octstr *from, Octstr *to, + Octstr *subject, Octstr *proxy, + Octstr *msgid, time_t expires, MmsMsg *m, int dlr, Octstr **error); + +/* Send errors */ +#define MMS_SEND_OK 0 +#define MMS_SEND_ERROR_TRANSIENT -1 +#define MMS_SEND_ERROR_FATAL -2 + + +#define NMAX 256 +static char qdir[NMAX]; + +static Cfg *cfg; +static MmsBoxSettings *settings; +static List *proxyrelays; + + +static List *cdr_list; /* List for cdr as used by cdr consumer thread. */ + +static int rstop = 0; /* Set to 1 to stop queue runner. */ + +static int sendMsg(MmsEnvelope *e) +{ + int i, n; + MmsMsg *msg = NULL; + + + if (!e->bill.billed) { /* Attempt to bill. */ + List *l = list_create(); + double amt; + + for (i = 0, n = list_len(e->to); i < n; i++) { + MmsEnvelopeTo *to = list_get(e->to, i); + list_append(l, to->rcpt); + } + + amt = settings->mms_billfuncs->mms_billmsg(e->from, l, + e->msize, + settings->mms_bill_module_data); + list_destroy(l, NULL); + + info(0, "Global Queue MMS Bill: From %s, to_count=%ld, msgid=%s, msgsize=%ld: returned=%.2f", + octstr_get_cstr(e->from), list_len(e->to), e->msgId ? octstr_get_cstr(e->msgId) : "", + e->msize, amt); + + if (amt == -1) { /* Delete message. */ + for (i = 0, n = list_len(e->to); i < n; i++) { + MmsEnvelopeTo *to = list_get(e->to, i); + to->process = 0; + } + } else if (amt >= 0) { + e->bill.billed = 1; + e->bill.amt = amt; + } + + if (amt >= -1) + if (mms_queue_update(e) == 1) /* Write queue just in case we crash. */ + e = NULL; + + if (e == NULL || + !e->bill.billed) + goto done2; /* If queue is gone, or we didn't manage to bill, go away */ + } + + + + msg = mms_queue_getdata(e); +#if 0 + if (msg) mms_msgdump(msg,1); +#endif + + + for (i = 0, n = list_len(e->to); i < n; i++) { + Octstr *err = NULL; + int res = MMS_SEND_OK, m; + MmsEnvelopeTo *to = list_get(e->to, i); + time_t tnow = time(NULL); + + + if (!to || !to->process) /* Already processed. */ + continue; + + if (e->expiryt != 0 && /* Handle message expiry. */ + e->expiryt < tnow) { + err = octstr_format("MMSC error: Message expired while sending to %S!", to->rcpt); + res = MMS_SEND_ERROR_FATAL; + + goto done; + } else if (e->attempts >= settings->maxsendattempts) { + err = octstr_format("MMSC error: Failed to deliver to %S after %ld attempts!", + to->rcpt, e->attempts); + res = MMS_SEND_ERROR_FATAL; + goto done; + } + + /* first check if it is an email address */ + if (octstr_search_char(to->rcpt, '@', 0) > 0) { + res = mms_sendtoemail(e->from, to->rcpt, + e->subject, + e->msgId, msg, e->dlr, &err, sendmail_cmd, + settings->hostname, 1, 1, octstr_get_cstr(settings->mms_email_txt), + octstr_get_cstr(settings->mms_email_html), 1); + mms_log2("Sent", e->from, to->rcpt, + -1, e->msgId, NULL, NULL, "MM3", NULL); + + } else if (e->viaproxy && octstr_len(e->viaproxy) > 0) /* If proxy to send through is already set, use it. */ + res = mms_sendtoproxy(e->from, + to->rcpt, e->subject, e->viaproxy, + e->msgId, e->expiryt, msg, e->dlr, &err); + else { + int j = octstr_case_search(to->rcpt, octstr_imm("/TYPE=PLMN"), 0); + int k = octstr_case_search(to->rcpt, octstr_imm("/TYPE=IPv"), 0); + int len = octstr_len(to->rcpt); + Octstr *phonenum = NULL; + Octstr *mmsc; + int sent = 0; + + if (j > 0 && j - 1 + sizeof "/TYPE=PLMN" == len) /* A proper number. */ + phonenum = octstr_copy(to->rcpt, 0, j); + else if (k > 0 && k + sizeof "/TYPE=IPv" == len) { + res = mms_sendtomobile(e->from, + to->rcpt, e->subject, e->fromproxy, + e->msgId, e->expiryt, msg, e->dlr, &err); + sent = 1; + goto done; + } else { + /* We don't handle other types for now. */ + err = octstr_format("MMSC error: Unsupported recipient type %S", to->rcpt); + res = MMS_SEND_ERROR_FATAL; + + goto done; + } + + /* Normalise the number, then match against local prefixes. */ + normalize_number(octstr_get_cstr(settings->unified_prefix), &phonenum); + + if ((mmsc = settings->mms_resolvefuncs->mms_resolve(phonenum, + settings->mms_resolver_module_data, settings, proxyrelays))) { + info(0, "mmsc for \"%s\" resolved to: \"%s\"", + octstr_get_cstr(phonenum), octstr_get_cstr(mmsc)); + + if (octstr_compare(mmsc, settings->hostname) == 0) { + res = + mms_sendtomobile(e->from, to->rcpt, + e->subject, e->fromproxy, + e->msgId, e->expiryt, msg, e->dlr, + &err); + sent = 1; + } else if (proxyrelays && list_len(proxyrelays) > 0) + /* Step through proxies. */ + for (j = 0, m = list_len(proxyrelays); jhost, mmsc)) { + res = mms_sendtoproxy(e->from, to->rcpt, + e->subject, mp->host, + e->msgId, e->expiryt, msg, + e->dlr, &err); + sent = 1; + break; + } + } + } + if (!sent) { + res = MMS_SEND_ERROR_FATAL; + err = octstr_format("MMSC error: Don't know how to deliver to %S !", to->rcpt); + + } + if (phonenum) octstr_destroy(phonenum); + } + + done: + if (res == MMS_SEND_OK) + to->process = 0; + else { /* If there was a report request, queue it. */ + + if (e->dlr) { + + MmsMsg *m = mms_deliveryreport(e->msgId, to->rcpt, tnow, + (e->expiryt != 0 && e->expiryt < tnow) ? + octstr_imm("Expired") : octstr_imm("Rejected")); + + + List *l = list_create(); + + list_append(l, octstr_duplicate(e->from)); + + /* Add to queue, switch via proxy to be from proxy. */ + mms_queue_add(settings->system_user, l, e->msgId, err, NULL, e->fromproxy, + tnow, tnow+settings->default_msgexpiry, m, NULL, 0, + qdir); + list_destroy(l, NULL); + + mms_destroy(m); + } + if (res == MMS_SEND_ERROR_FATAL) + to->process = 0; /* No more attempts. */ + } + + /* Write to log */ + info(0, "%s Global Queue MMS Send: From %s, to %s, msgsize=%ld: err=%s", + SEND_ERROR_STR(res), + octstr_get_cstr(e->from), octstr_get_cstr(to->rcpt), e->msize, + err ? octstr_get_cstr(err) : "(null)"); + + if (res == MMS_SEND_OK) { /* Do CDR writing. */ + MmsCdrStruct *cdr = gw_malloc(sizeof *cdr); + + cdr->module_data = settings->mms_bill_module_data; + cdr->sdate = e->created; + strncpy(cdr->from, octstr_get_cstr(e->from), sizeof cdr->from); + strncpy(cdr->to, octstr_get_cstr(to->rcpt), sizeof cdr->to); + strncpy(cdr->msgid, e->msgId ? octstr_get_cstr(e->msgId) : "", sizeof cdr->msgid); + cdr->msg_size = e->msize; + + list_produce(cdr_list, cdr); /* Put it on list so sending thread sends it. */ + } + + e->lasttry = tnow; + e->attempts++; + e->sendt = e->lasttry + settings->send_back_off * e->attempts; + + if (mms_queue_update(e) == 1) { + e = NULL; + break; /* Queue entry gone. */ + } + } + + done2: + if (msg) + mms_destroy(msg); + if (e) + mms_queue_free_env(e); + + return 1; /* Always deletes the queue entry. */ +} + + +static void quit_now(int notused) +{ + rstop = 1; +} + +static void cdr_thread(void *unused) +{ + MmsCdrStruct *cdr; + + while ((cdr = list_consume(cdr_list)) != NULL) { + settings->mms_billfuncs->mms_logcdr(cdr); + /* We should probably write to log here... */ + gw_free(cdr); + } +} + +int main(int argc, char *argv[]) +{ + int cfidx; + Octstr *fname; + CfgGroup *grp; + Octstr *log, *alog; + long loglevel; + + mms_lib_init(); + + srandom(time(NULL)); + + cfidx = get_and_set_debugs(argc, argv, NULL); + + if (argv[cfidx] == NULL) + fname = octstr_imm("mmsc.conf"); + else + fname = octstr_create(argv[cfidx]); + + cfg = cfg_create(fname); + + if (cfg_read(cfg) == -1) + panic(0, "Couldn't read configuration from '%s'.", octstr_get_cstr(fname)); + + octstr_destroy(fname); + + info(0, "----------------------------------------"); + info(0, " MMSC Global queue runner version %s starting", MMSC_VERSION); + + grp = cfg_get_single_group(cfg, octstr_imm("core")); + log = cfg_get(grp, octstr_imm("log-file")); + if (log != NULL) { + if (cfg_get_integer(&loglevel, grp, octstr_imm("log-level")) == -1) + loglevel = 0; + log_open(octstr_get_cstr(log), loglevel, GW_NON_EXCL); + octstr_destroy(log); + } + + /* Get access log and open it. */ + alog = cfg_get(grp, octstr_imm("access-log")); + if (alog) { + alog_open(octstr_get_cstr(alog), 1, 1); + octstr_destroy(alog); + } + + /* Load proxy relays. */ + + proxyrelays = mms_proxy_relays(cfg); + /* Load settings. */ + + settings = mms_load_mmsbox_settings(cfg); + + if (!settings) + panic(0, "No global MMSC configuration!"); + + mms_start_profile_engine(octstr_get_cstr(settings->ua_profile_cache_dir)); + + mms_setmobile_queuedir(octstr_get_cstr(settings->mm1_queuedir)); + mms_setmm4_queuedir(octstr_get_cstr(settings->mm4_queuedir)); + + mms_setsendmail_cmd(octstr_get_cstr(settings->sendmail)); + + strncpy(qdir, octstr_get_cstr(settings->global_queuedir), sizeof qdir); + + + /* Start the thread for CDR */ + cdr_list = list_create(); + list_add_producer(cdr_list); + gwthread_create(cdr_thread, NULL); + + + signal(SIGHUP, quit_now); + signal(SIGTERM, quit_now); + signal(SIGPIPE,SIG_IGN); /* Ignore pipe errors. They kill us sometimes for nothing*/ + mms_queue_run(qdir, sendMsg, settings->queue_interval, settings->maxthreads, &rstop); + mms_stop_profile_engine(); /* Stop profile stuff. */ + sleep(2); + list_remove_producer(cdr_list); /* Stop CDR thread. */ + sleep(2); /* Wait for them to die. */ + return 0; +} + + + +static int mms_setmobile_queuedir(char *mqdir) +{ + strncpy(mobile_qdir, mqdir, sizeof mobile_qdir); + return 0; +} + +static int mms_setmm4_queuedir(char *mqdir) +{ + strncpy(mm4_qdir, mqdir, sizeof mm4_qdir); + return 0; +} + +static int mms_setsendmail_cmd(char *sendmail) +{ + strncpy(sendmail_cmd, sendmail, -1 + sizeof sendmail_cmd); + return 0; +} + +/* + * Queue this message for delivery to mobile terminal. + * A queue thread will handle sending of notifications to phone. + * When a deliver is received, another thread will remove the queue entry. + */ +int mms_sendtomobile(Octstr *from, Octstr *to, + Octstr *subject, Octstr *fromproxy, + Octstr *msgid, time_t expires, MmsMsg *m, int dlr, Octstr **error) +{ + + Octstr *ret, *x; + List *l = list_create(); + char tokenstr[128]; + + list_append(l, to); + + /* We generate a special token that will be added to message ID to make + * stealing messages a bit harder. + */ + snprintf(tokenstr, -1 + sizeof tokenstr, + "wx%ld", + random() % 100); + + x = octstr_create(tokenstr); + + ret = mms_queue_add(from, l, msgid, subject, fromproxy, NULL, 0, expires, m, + x, dlr, mobile_qdir); + octstr_destroy(x); + + list_destroy(l, NULL); + octstr_destroy(ret); + if (ret == NULL) + return MMS_SEND_ERROR_TRANSIENT; + else + return MMS_SEND_OK; +} + + +/* Send this message via an intermediate proxy (MM4 interface). + * The way it works: We email the message to the proxy but we also keep a local copy in the queue + * so we can handle delivery receipts and such. + */ +static int mms_sendtoproxy(Octstr *from, Octstr *to, + Octstr *subject, Octstr *proxy, + Octstr *msgid, time_t expires, MmsMsg *msg, int dlr, Octstr **error) +{ + + Octstr *pto; + + + int x; + + + if (!to || + octstr_search_char(to, '@', 0) >= 0) { + *error = octstr_format("Bad recipient address sent to MM4 interface, addresss is %S!", to); + return MMS_SEND_ERROR_FATAL; + } + + if (mms_messagetype(msg) == MMS_MSGTYPE_SEND_REQ) { /* Only queue these ones for future response. */ + List *l = list_create(); + Octstr *ret; + list_append(l, to); + ret = mms_queue_add(from, l, msgid, subject, NULL, proxy, 0, expires, msg,NULL, dlr, mm4_qdir); + list_destroy(l, NULL); + + if (ret == NULL) { + *error = octstr_format("MM4: Failed to queue message to %S for future tracking. ", to); + return MMS_SEND_ERROR_TRANSIENT; + } + octstr_destroy(ret); + } + + + pto = octstr_format("%S@%S", to, proxy); + + + x = mms_sendtoemail(from, pto, subject, msgid, msg, 0, + error, sendmail_cmd, + settings->hostname, 0, 0,NULL,NULL,0); + + mms_log2("Sent", from, to, + -1, msgid, NULL, proxy, "MM4", NULL); + + octstr_destroy(pto); + + return x; +} diff --git a/mbuni/mmsc/mmsmobilesender.c b/mbuni/mmsc/mmsmobilesender.c new file mode 100644 index 0000000..a4dde57 --- /dev/null +++ b/mbuni/mmsc/mmsmobilesender.c @@ -0,0 +1,574 @@ +/* + * The queue runner for dispatching notifications to mobiles via WAP PUSH. + */ +#include +#include +#include + + +#include "mms_queue.h" +#include "mms_uaprof.h" +#include "mms_util.h" + +#define WAPPUSH_PORT 2948 + +static MmsBoxSettings *settings; +static HTTPCaller *httpcaller; +static int rstop = 0; +static MmsEnvelope edummy; + + +static MmsEnvelope *update_env_success(MmsEnvelope *env, MmsEnvelopeTo *xto) +{ + time_t tnow = time(NULL); + + if (xto && env->msgtype != MMS_MSGTYPE_SEND_REQ) + xto->process = 0; /* No more processing. */ + else { + env->lasttry = tnow; + env->attempts++; + env->sendt = env->lasttry + settings->send_back_off * env->attempts; + } + + if (mms_queue_update(env) == 1) + env = NULL; + return env; +} + +static MmsEnvelope *update_env_failed(MmsEnvelope *env) +{ + time_t tnow = time(NULL); + if (env && env != &edummy) { + env->sendt = tnow + settings->send_back_off; + env->lasttry = tnow; + + if (mms_queue_update(env) == 1) + env = NULL; + } + return env; +} + +static void start_push(Octstr *rcpt_to, int isphonenum, MmsEnvelope *e, MmsMsg *msg) +{ + List *pheaders; + static unsigned char ct; /* Transaction counter -- do we need it? */ + Octstr *to = NULL; + + Octstr *pduhdr = octstr_create(""); + + Octstr *s = NULL; + + + info(0, "mms2mobile.startpush: notification to %s\n", octstr_get_cstr(rcpt_to)); + + if (!rcpt_to) { + error(0, "mobilesender: Queue entry %s has no recipient address!", e->qf.name); + goto done; + } else + to = octstr_duplicate(rcpt_to); + + + ct++; + octstr_append_char(pduhdr, ct); + octstr_append_char(pduhdr, 0x06); /* Pushd id */ + + +#if 1 + octstr_append_char(pduhdr, 1 + 1 + 1); + octstr_append_char(pduhdr, 0xbe); /* content type. */ +#else + octstr_append_char(pduhdr, + 1 + 1 + sizeof "application/vnd.wap.mms-message"); /*header length. */ + octstr_append_cstr(pduhdr, "application/vnd.wap.mms-message"); + octstr_append_char(pduhdr, 0x0); /* string terminator. */ +#endif + octstr_append_char(pduhdr, 0xaf); /* charset. */ + octstr_append_char(pduhdr, 0x84); /* ... */ + + s = mms_tobinary(msg); + + if (isphonenum) { + Octstr *url; + + octstr_url_encode(to); + octstr_url_encode(s); + + octstr_url_encode(pduhdr); + + url = octstr_format("%S&text=%S%S&to=%S&udh=%%06%%05%%04%%0B%%84%%23%%F0", + settings->sendsms_url, pduhdr, s, to); + + pheaders = http_create_empty_headers(); + http_header_add(pheaders, "Connection", "close"); + http_header_add(pheaders, "User-Agent", MM_NAME "/" GW_VERSION); + + http_start_request(httpcaller, HTTP_METHOD_GET, url, + pheaders, NULL, 0, e, NULL); + + http_destroy_headers(pheaders); + octstr_destroy(url); + } else { /* An IP Address: Send packet, forget. */ + Octstr *addr = udp_create_address(to, WAPPUSH_PORT); + int sock = udp_client_socket(); + + if (sock > 0) { + MmsEnvelopeTo *xto = list_get(e->to,0); + octstr_append(pduhdr, s); + udp_sendto(sock, pduhdr, addr); + close(sock); /* ?? */ + mms_log2("Notify", octstr_imm("system"), to, + -1, e ? e->msgId : NULL, + NULL, NULL, "MM1", NULL); + e = update_env_success(e, xto); + } else { + e = update_env_failed(e); + error(0, "push to %s:%d failed, no reason found", octstr_get_cstr(to), WAPPUSH_PORT); + } + octstr_destroy(addr); + if (e) + mms_queue_free_env(e); + } + done: + if (to) octstr_destroy(to); + if (pduhdr) octstr_destroy(pduhdr); + if (s) octstr_destroy(s); +} + + +static int receive_push_reply(HTTPCaller *caller) +{ + int http_status; + List *reply_headers; + Octstr *final_url, + *reply_body; + + MmsEnvelope *env; + + http_status = HTTP_UNAUTHORIZED; + + while ((env = http_receive_result(caller, &http_status, &final_url, &reply_headers, + &reply_body)) != NULL) { + MmsEnvelopeTo *xto; + Octstr *to = NULL; + + if (http_status == -1 || final_url == NULL) { + error(0, "push failed, no reason found"); + goto push_failed; + } + + if (env == &edummy) /* Skip this one it is a dummy. */ + goto push_free_env; + xto = list_get(env->to, 0); + if (xto) + to = xto->rcpt; + else { + error(0, "mobilesender: Queue entry %s has no recipient address!", env->qf.name); + goto push_failed; + } + + info(0, "send2mobile.push_reply[%s]: From %s, to %s => %d", + env->qf.name, + octstr_get_cstr(env->from), octstr_get_cstr(to), http_status); + + if (http_status == HTTP_UNAUTHORIZED || + http_status == HTTP_NOT_FOUND || + http_status == HTTP_FORBIDDEN) { /* This is a temporary system error + * do not increase attempts, count, + * merely reschedule + * for a minute or so later. + */ + + error(0, "Deffered notification, WAP Push failed for " + "msgid %s to %s, http error: %d!", octstr_get_cstr(env->msgId), + octstr_get_cstr(to), http_status); + goto push_failed; + } + + + debug("mobilesender.push", 0, "Push reply headers were"); + http_header_dump(reply_headers); + + mms_log2("Notify", octstr_imm("system"), to, + -1, env ? env->msgId : NULL, NULL, NULL, "MM1", NULL); + + if (update_env_success(env, xto) != NULL) + goto push_free_env; + + /* Fall through. */ + push_failed: + octstr_destroy(final_url); + octstr_destroy(reply_body); + http_destroy_headers(reply_headers); + + env = update_env_failed(env); + push_free_env: + if (env && env != &edummy) + mms_queue_free_env(env); + } + + return 0; +} + +static int sendNotify(MmsEnvelope *e) +{ + Octstr *to; + MmsMsg *msg, *smsg = NULL; + MmsEnvelopeTo *xto = list_get(e->to, 0); + Octstr *err = NULL; + time_t tnow = time(NULL); + int j, k, len; + Octstr *phonenum = NULL, *rcpt_ip = NULL, *msgId, *from, *fromproxy; + int mtype, msize; + int res = MMS_SEND_OK, dlr; + time_t expiryt; + char *prov_notify_event = NULL; + char *rtype = NULL; + + + if (e->lastaccess != 0) { /* This message has been fetched at least once, no more signals. */ + e->sendt = e->expiryt + 3600*24*30*12; + return mms_queue_update(e); + } + + if (!xto) { + error(0, "mobilesender: Queue entry %s with no recipients!", + e->qf.name); + return 0; + } + + msg = mms_queue_getdata(e); + to = octstr_duplicate(xto->rcpt); + expiryt = e->expiryt; + msgId = e->msgId ? octstr_duplicate(e->msgId) : NULL; + from = octstr_duplicate(e->from); + fromproxy = e->fromproxy ? octstr_duplicate(e->fromproxy) : NULL; + msize = e->msize; + dlr = e->dlr; + + if (e->expiryt != 0 && /* Handle message expiry. */ + e->expiryt < tnow) { + err = octstr_format("MMSC error: Message expired while sending to %S!", to); + res = MMS_SEND_ERROR_FATAL; + prov_notify_event = "failedfetch"; + rtype = "Expired"; + goto done; + } else if (e->attempts >= settings->maxsendattempts) { + err = octstr_format("MMSC error: Failed to deliver to %S after %ld attempts!", + to, e->attempts); + res = MMS_SEND_ERROR_FATAL; + + prov_notify_event = "failedfetch"; + rtype = "Expired"; + goto done; + } + + j = octstr_case_search(to, octstr_imm("/TYPE=PLMN"), 0); + k = octstr_case_search(to, octstr_imm("/TYPE=IPv"), 0); + len = octstr_len(to); + + if (j > 0 && j - 1 + sizeof "/TYPE=PLMN" == len) { /* A proper number. */ + phonenum = octstr_copy(to, 0, j); + normalize_number(octstr_get_cstr(settings->unified_prefix), &phonenum); + } else if (k > 0 && k + sizeof "/TYPE=IPv" == len) + rcpt_ip = octstr_copy(to, 0, k); + else { + /* We only handle phone numbers here. */ + err = octstr_format("Unexpected recipient %s in MT queue!", octstr_get_cstr(to)); + res = MMS_SEND_ERROR_FATAL; + goto done; + } + + mtype = mms_messagetype(msg); + + /* For phone, getting here means the message can be delivered. So: + * - Check whether the recipient is provisioned, if not, wait (script called will queue creation req) + * - Send to MMBox if not yet done. + * - If the recipient can't take MMS, then send SMS. + */ + + /* We handle two types of requests: send and delivery/read notifications. + * other types of messages cannot possibly be in this queue! + */ + + if (mtype == MMS_MSGTYPE_SEND_REQ || + mtype == MMS_MSGTYPE_RETRIEVE_CONF) { + Octstr *url, *transid; + + if (phonenum) { + int send_ind = mms_ind_send(settings->prov_getstatus, phonenum); + + if (send_ind < 0) { /* That is, recipient is not (yet) provisioned. */ + res = MMS_SEND_ERROR_TRANSIENT; + err = octstr_format("%S is not provisioned for MMS reception, delivery deferred!", + phonenum); + + /* Do not increase delivery attempts counter. */ + e->lasttry = tnow; + e->sendt = e->lasttry + settings->send_back_off * (1 + e->attempts); + + if (mms_queue_update(e) == 1) + e = NULL; /* Queue entry gone. */ + else + mms_queue_free_env(e); + + goto done; + } else if (e->mdata == NULL) { /* Not yet sent to MMBox etc, but provisioned. */ + + int x; + if (settings->mmbox_host && + octstr_len(settings->mmbox_host) > 0) { /* Only if mmbox is configured. */ + Octstr *mto = octstr_format("%S@%S", phonenum, settings->mmbox_host); + x = mms_sendtoemail(from, mto, e->subject, e->msgId, msg, 0, &err, + octstr_get_cstr(settings->sendmail), settings->hostname, 1, + 1, octstr_get_cstr(settings->mms_email_txt), + octstr_get_cstr(settings->mms_email_html), 1); + octstr_destroy(mto); + } else + x = 0; + + if (x != 0) { /* Failed to send to MMBox...*/ + res = MMS_SEND_ERROR_TRANSIENT; + err = octstr_format("Failed to send to MMBox %d", x); + e->attempts++; + e->lasttry = tnow; + e->sendt = e->lasttry + settings->send_back_off * e->attempts; + + if (mms_queue_update(e) == 1) + e = NULL; /* Queue entry gone. */ + else + mms_queue_free_env(e); + goto done; + } + + /* else Send to MMBox succeeded or non-provisioned. + * But if phone cannot handle MMS-Ind, then send it sms. + */ + if (send_ind == 0) { + Octstr *s = octstr_format(octstr_get_cstr(settings->mms_notify_txt), + from); + List *pheaders; + + Octstr *xto = octstr_duplicate(phonenum); + + octstr_url_encode(s); + octstr_url_encode(xto); + + url = octstr_format("%S&text=%S&to=%S",settings->sendsms_url,s, xto); + pheaders = http_create_empty_headers(); + http_header_add(pheaders, "Connection", "close"); + http_header_add(pheaders, "User-Agent", MM_NAME "/" GW_VERSION); + + http_start_request(httpcaller, HTTP_METHOD_GET, url, + pheaders, NULL, 0, &edummy, NULL); + + + http_destroy_headers(pheaders); + octstr_destroy(url); + octstr_destroy(s); + octstr_destroy(xto); + + } + e->mdata = octstr_imm("MMSent"); /* Mark as sent to MMBox. */ + if (mms_queue_update(e) == 1) { + e = NULL; + goto done; + } + } + + if (send_ind == 0) { /* No MMS Ind, go away but first update queue entry. */ + res = MMS_SEND_OK; + err = octstr_imm("No MMS Ind support, sent SMS instead"); + + xto->process = 0; /* No more processing. */ + if (mms_queue_update(e) == 1) + e = NULL; + else + mms_queue_free_env(e); + goto done; + } + } + + /* To get here means we can send Ind. */ + url = mms_makefetchurl(e->qf.name, e->token, + settings); + transid = mms_maketransid(e->qf.name, settings->host_alias); + + smsg = mms_notification(msg, e->msize, url, transid, + e->expiryt ? e->expiryt : + tnow + settings->default_msgexpiry); + octstr_destroy(transid); + octstr_destroy(url); + } else if (mtype == MMS_MSGTYPE_DELIVERY_IND || + mtype == MMS_MSGTYPE_READ_ORIG_IND) + smsg = msg; + else { + error(0, "Unexpected message type %s for %s found in MT queue!", + mms_message_type_to_cstr(mtype), octstr_get_cstr(to)); + res = MMS_SEND_ERROR_FATAL; + goto done; + } + + if (smsg) + start_push(phonenum ? phonenum : rcpt_ip, + phonenum ? 1 : 0, + e, smsg); /* Send the message. + * Don't touch 'e' after this point! + * It may be freed by receive thread. + */ + + if (smsg != msg && smsg) + mms_destroy(smsg); + + done: + if (err != NULL && + res != MMS_SEND_ERROR_TRANSIENT) { /* If there was a report request and this is a legit error + * queue it. + */ + + if (dlr) { + MmsMsg *m = mms_deliveryreport(msgId, to, tnow, + rtype ? octstr_imm(rtype) : + octstr_imm("Indeterminate")); + + List *l = list_create(); + Octstr *res; + list_append(l, from); + + /* Add to queue, switch via proxy to be from proxy. */ + res = mms_queue_add(settings->system_user, l, msgId, err, + NULL, fromproxy, + tnow, tnow+settings->default_msgexpiry, m, NULL, 0, + octstr_get_cstr(settings->mm1_queuedir)); + list_destroy(l, NULL); + mms_destroy(m); + octstr_destroy(res); + } + + } + + /* Write to log */ + info(0, "%s Mobile Queue MMS Send Notify: From=%s, to=%s, msgsize=%d, reason=%s", + SEND_ERROR_STR(res), + octstr_get_cstr(from), octstr_get_cstr(to), msize, + err ? octstr_get_cstr(err) : ""); + + + if (res == MMS_SEND_ERROR_FATAL) { + xto->process = 0; /* No more attempts to deliver, delete this. */ + if (mms_queue_update(e) == 1) + e = NULL; /* Queue entry gone. */ + else + mms_queue_free_env(e); + } /* Else queue will be updated/freed elsewhere. */ + + + if (prov_notify_event) + notify_prov_server(octstr_get_cstr(settings->prov_notify), + to ? octstr_get_cstr(to) : "unknown", + prov_notify_event, + octstr_get_cstr(settings->prov_notify_arg)); + + if (msg) mms_destroy(msg); + if (phonenum) + octstr_destroy(phonenum); + if (rcpt_ip) + octstr_destroy(rcpt_ip); + octstr_destroy(to); + if (msgId) octstr_destroy(msgId); + if (fromproxy) octstr_destroy(fromproxy); + octstr_destroy(from); + if (err) octstr_destroy(err); + + return 1; +} + +static void quit_now(int notused) +{ + rstop = 1; +} + +static Cfg *cfg; +static List *proxyrelays; +int main(int argc, char *argv[]) +{ + int cfidx; + Octstr *fname; + CfgGroup *grp; + Octstr *log, *alog; + long loglevel; + + mms_lib_init(); + srandom(time(NULL)); + + cfidx = get_and_set_debugs(argc, argv, NULL); + + if (argv[cfidx] == NULL) + fname = octstr_imm("mmsc.conf"); + else + fname = octstr_create(argv[cfidx]); + + cfg = cfg_create(fname); + + if (cfg_read(cfg) == -1) + panic(0, "Couldn't read configuration from '%s'.", octstr_get_cstr(fname)); + + octstr_destroy(fname); + + info(0, "----------------------------------------"); + info(0, " MMSC Mobile queue runner version %s starting", MMSC_VERSION); + + grp = cfg_get_single_group(cfg, octstr_imm("core")); + log = cfg_get(grp, octstr_imm("log-file")); + if (log != NULL) { + if (cfg_get_integer(&loglevel, grp, octstr_imm("log-level")) == -1) + loglevel = 0; + log_open(octstr_get_cstr(log), loglevel, GW_NON_EXCL); + octstr_destroy(log); + } + + /* Get access log and open it. */ + alog = cfg_get(grp, octstr_imm("access-log")); + if (alog) { + alog_open(octstr_get_cstr(alog), 1, 1); + octstr_destroy(alog); + } + + /* Load proxy relays. */ + + proxyrelays = mms_proxy_relays(cfg); + + /* Load settings. */ + settings = mms_load_mmsbox_settings(cfg); + + if (!settings) + panic(0, "No global MMSC configuration!"); + + + + + mms_start_profile_engine(octstr_get_cstr(settings->ua_profile_cache_dir)); + + httpcaller = http_caller_create(); + if (gwthread_create((gwthread_func_t *)receive_push_reply, httpcaller) < 0) { /* Listener thread. */ + error(0, "Mobile sender: Failed to create push reply thread: %d: %s!", + errno, strerror(errno)); + return -1; + } + + signal(SIGHUP, quit_now); + signal(SIGTERM, quit_now); + signal(SIGPIPE,SIG_IGN); /* Ignore pipe errors. They kill us sometimes for nothing*/ + + mms_queue_run(octstr_get_cstr(settings->mm1_queuedir), + sendNotify, settings->queue_interval, settings->maxthreads, &rstop); + mms_stop_profile_engine(); + sleep(2); /* Wait for it to die. */ + http_caller_signal_shutdown(httpcaller); + sleep(2); + http_caller_destroy(httpcaller); + + + return 0; +} + + diff --git a/mbuni/mmsc/mmsproxy.c b/mbuni/mmsc/mmsproxy.c new file mode 100644 index 0000000..dddfbd5 --- /dev/null +++ b/mbuni/mmsc/mmsproxy.c @@ -0,0 +1,897 @@ +/* + * MMS proxy gateway: Receives MMS messages from clients, dispatches to + * delivery queues. Also provides interface for collecting messages. + */ + +#include +#include +#include + +#include "mms_msg.h" +#include "mms_queue.h" + +#include "mms_uaprof.h" +#include "mms_util.h" + +static Cfg *cfg; +static List *proxyrelays; +static MmsBoxSettings *settings; + +static int rstop = 0; +static void quit_now(int notused) +{ + rstop = 1; + if (settings) + http_close_port(settings->port); +} + +typedef struct MmsHTTPClientInfo { + HTTPClient *client; + Octstr *ua; + Octstr *ip; + List *headers; + Octstr *url; + Octstr *body; + List *cgivars; + MmsUaProfile *prof; + Octstr *base_client_addr; + Octstr *client_addr; +} MmsHTTPClientInfo; + +static void fetchmms_proxy(MmsHTTPClientInfo *h); +static void sendmms_proxy(MmsHTTPClientInfo *h); + +int main(int argc, char *argv[]) +{ + int cfidx; + Octstr *fname; + CfgGroup *grp; + Octstr *log, *alog; + long loglevel; + + MmsHTTPClientInfo h; + + mms_lib_init(); + srandom(time(NULL)); + + cfidx = get_and_set_debugs(argc, argv, NULL); + + if (argv[cfidx] == NULL) + fname = octstr_imm("mmsc.conf"); + else + fname = octstr_create(argv[cfidx]); + + cfg = cfg_create(fname); + + if (cfg_read(cfg) == -1) + panic(0, "Couldn't read configuration from '%s'.", octstr_get_cstr(fname)); + + octstr_destroy(fname); + + info(0, "----------------------------------------"); + info(0, " MMSC Proxy Relay server version %s starting", MMSC_VERSION); + + grp = cfg_get_single_group(cfg, octstr_imm("core")); + log = cfg_get(grp, octstr_imm("log-file")); + if (log != NULL) { + if (cfg_get_integer(&loglevel, grp, octstr_imm("log-level")) == -1) + loglevel = 0; + log_open(octstr_get_cstr(log), loglevel, GW_NON_EXCL); + octstr_destroy(log); + } + + /* Get access log and open it. */ + alog = cfg_get(grp, octstr_imm("access-log")); + if (alog) { + alog_open(octstr_get_cstr(alog), 1, 1); + octstr_destroy(alog); + } + /* Load proxy relays. */ + + proxyrelays = mms_proxy_relays(cfg); + + /* Load settings. */ + settings = mms_load_mmsbox_settings(cfg); + + if (!settings) + panic(0, "No global MMSC configuration!"); + + + signal(SIGHUP, quit_now); + signal(SIGTERM, quit_now); + + /* Start cache engine. */ + mms_start_profile_engine(octstr_get_cstr(settings->ua_profile_cache_dir)); + + /* Now open port and start dispatching requests. */ + + if (http_open_port(settings->port, 0) < 0) { + error(0, "MMS Proxy: Failed to start http server: %d => %s!", + errno, strerror(errno)); + return -1; + } + + while(rstop == 0 && (h.client = http_accept_request(settings->port, + &h.ip, &h.url, &h.headers, + &h.body, &h.cgivars)) != NULL) + if (is_allowed_ip(settings->deny_ip, settings->allow_ip, h.ip)) { + MmsHTTPClientInfo *hx = gw_malloc(sizeof *hx); + Octstr *profile_url; + + h.ua = http_header_value(h.headers, octstr_imm("User-Agent")); + + /* Get the profile URL and store it. Has effect of fetching if missing. */ + if ((profile_url = http_header_value(h.headers, + octstr_imm("X-Wap-Profile"))) == NULL) + profile_url = http_header_value(h.headers, octstr_imm("Profile")); + + if (profile_url) { + octstr_strip_nonalphanums(profile_url); + h.prof = mms_get_ua_profile(octstr_get_cstr(profile_url)); + octstr_destroy(profile_url); + } else + + h.prof = mms_make_ua_profile(h.headers); + + /* Get the sender address. */ + + h.base_client_addr = mms_find_sender_msisdn(&h.url, h.headers, settings->wap_gw_msisdn_header); + + if (!h.base_client_addr) { /* Set to IP sender... XXXX assumes ipv4 only for now*/ + int ipv6 = 0; + h.base_client_addr = mms_find_sender_ip(h.headers, + settings->wap_gw_ip_header, + h.ip, &ipv6); + h.client_addr = octstr_format("%S/TYPE=IPv%s", + h.ip, ipv6 ? "6" : "4"); + } else { + normalize_number(octstr_get_cstr(settings->unified_prefix), &h.base_client_addr); + h.client_addr = octstr_format("%S/TYPE=PLMN", h.base_client_addr); + } + + /* Dump headers, url etc. */ +#if 0 + http_header_dump(h.headers); + if (h.body) octstr_dump(h.body, 0); + if (h.ip) octstr_dump(h.ip, 0); +#endif + /* Determine if this is the fetch interface or the send interface being used, + * by checking if http request has a body. + * then call relevant function in a thread (use threads because these functions + * can block) + */ + *hx = h; /* Copy it all over. */ + if (hx->body == NULL || octstr_len(hx->body) == 0) + gwthread_create((gwthread_func_t *)fetchmms_proxy, hx); + else + gwthread_create((gwthread_func_t *)sendmms_proxy, hx); + + } else { + octstr_destroy(h.ip); + octstr_destroy(h.url); + if (h.ua) octstr_destroy(h.ua); + if (h.base_client_addr) + octstr_destroy(h.base_client_addr); + octstr_destroy(h.client_addr); + octstr_destroy(h.body); + http_destroy_headers(h.headers); + http_destroy_headers(h.cgivars); + + http_close_client(h.client); + } + + debug("proxy", 0, "Shutdown requested"); + http_close_all_ports(); + debug("proxy", 0, "Port closed"); + sleep(2); /* Give them time to shut down. */ + mms_stop_profile_engine(); + sleep(2); /* Give them time to shut down. */ + return 0; +} + + +void fetchmms_proxy(MmsHTTPClientInfo *h) +{ + Octstr *qf = NULL, *token = NULL, *s = NULL, *transid = NULL; + MmsEnvelope *e = NULL; + MmsMsg *m = NULL, *mr = NULL; + List *rh; + + debug("proxy.fetchinterface", 0, " ---> Entered fetch interface <---"); + + rh = http_create_empty_headers(); + http_header_add(rh, "Pragma", "no-cache"); + http_header_add(rh, "Cache-Control", "no-cache"); + + if (mms_decodefetchurl(h->url, &qf, &token) != 0) { + error(0, "MMS Fetch interface: failed to decode request url (%s) from %s!", + octstr_get_cstr(h->url), + octstr_get_cstr(h->ip)); + goto failed; + } + e = mms_queue_readenvelope(octstr_get_cstr(qf), + octstr_get_cstr(settings->mm1_queuedir), 1); + + if (!e || + (m = mms_queue_getdata(e)) == NULL) { + error(0, "MMS Fetch interface: failed to find envelope/data %s for request url (%s) from %s (e=%s)!", + octstr_get_cstr(qf), octstr_get_cstr(h->url), octstr_get_cstr(h->ip), + (e)? "found" : "not found"); + + mr = mms_retrieveconf(NULL, NULL, "Error-permanent-message-not-found", "Message not found", + settings->system_user); + s = mms_tobinary(mr); + goto failed; + } + + /* Adapt content. */ + transid = mms_maketransid(e->qf.name, settings->host_alias); + if (h->prof) { + MmsMsg *outmsg = NULL; + int x = mms_transform_msg(m, h->prof, &outmsg); + + if (x == -1) { /* Temporary failure, we need to fetch profile. */ + mr = mms_retrieveconf(NULL, transid, "Error-transient-failure", "Mobile MMS Settings not found", + settings->system_user); + s = mms_tobinary(mr); + goto failed; + } else if (x < 0) { /* Doesn't support MMS */ + mr = mms_retrieveconf(NULL, transid, "Error-permanent-content-unsupported", "No MMS Support", + settings->system_user); + s = mms_tobinary(mr); + goto failed; + } else if (x == 0) { + if (outmsg == NULL) { /* Too large so truncated. */ + Octstr *xx = octstr_format(octstr_get_cstr(settings->mms_toolarge), e->from); + + mr = mms_retrieveconf(NULL, transid, +#if 0 + "Error-permanent-content-unsupported", +#else + "Ok", +#endif + octstr_get_cstr(xx), + settings->system_user); + octstr_destroy(xx); + + s = mms_tobinary(mr); + goto failed; + } else { + mms_destroy(m); + m = outmsg; + } + } + } + + mr = mms_retrieveconf(m, transid, "Error-permanent-message-not-found", NULL, e->from); + s = mms_tobinary(mr); + + if ((token == NULL && e->token != NULL) || + (e->token == NULL && token != NULL) || + (octstr_compare(e->token, token) != 0)) { + error(0, "MMS Fetch interface: token mismatch, did client mod the fetch url?? " + " env=%s for request url (%s) from %s!", + octstr_get_cstr(qf), octstr_get_cstr(h->url), octstr_get_cstr(h->ip)); + goto failed; + } + + + if (!m) { + error(0, "MMS Fetch interface: Failed to get message for request url (%s) from %s!", + octstr_get_cstr(h->url), octstr_get_cstr(h->ip)); + goto failed; + } + + if (!s) { + error(0, "MMS Fetch interface: Failed to convert message to binary for " + "request url (%s) from %s!", + octstr_get_cstr(h->url), octstr_get_cstr(h->ip)); + goto failed; + } + + + + e->lastaccess = time(NULL); /* No more notifications requests. */ + e->sendt = e->expiryt + 3600*24*30*12; + mms_queue_update(e); + + + http_header_add(rh, "Content-Type", "application/vnd.wap.mms-message"); + http_send_reply(h->client, HTTP_OK, rh, s); + +#if 1 + debug("proxy.fetchinterface", 0, + " $$$$$$ fetch message replying with [type=%s,content_len=%ld]: ", + mr ? mms_message_type_to_cstr(mms_messagetype(mr)) : (unsigned char *)"none", + s ? octstr_len(s) : 0); + if (mr) + mms_msgdump(mr,1); +#endif + + /* Send to access log with success. */ + mms_log2("Fetched", e ? e->from : NULL, h->client_addr, + e ? e->msize : 0, e ? e->msgId : NULL, NULL, NULL, "MM1", h->ua); + + goto free_stuff; /* Skip to end. */ + + failed: +#if 1 + debug("proxy.fetchinterface", 0, + " $$$$$$ fetch message [fail] replying with [type=%s,content_len=%ld]: ", + mr ? mms_message_type_to_cstr(mms_messagetype(mr)) : (unsigned char *)"none", + s ? octstr_len(s) : 0); + if (mr) + mms_msgdump(mr,1); +#endif + + /* Send to access log on failure?? */ + mms_log2("Failed Fetch", e ? e->from : NULL, h->client_addr, + e ? e->msize : 0, e ? e->msgId : NULL, NULL, NULL, "MM1", h->ua); + + if (!s) { + http_header_add(rh, "Content-Type", "text/plain"); + http_send_reply(h->client, HTTP_NOT_FOUND, rh, octstr_imm("Not found")); + } else { + http_header_add(rh, "Content-Type", "application/vnd.wap.mms-message"); + http_send_reply(h->client, HTTP_OK, rh, s); + } + + free_stuff: + + http_destroy_headers(rh); + if (e) mms_queue_free_env(e); + + if (s) octstr_destroy(s); + if (m) mms_destroy(m); + if (mr) mms_destroy(mr); + + if (qf) octstr_destroy(qf); + if (token) octstr_destroy(token); + if (transid) octstr_destroy(transid); + + octstr_destroy(h->ip); + octstr_destroy(h->url); + if (h->ua) octstr_destroy(h->ua); + if (h->body) octstr_destroy(h->body); + + if (h->base_client_addr) + octstr_destroy(h->base_client_addr); + octstr_destroy(h->client_addr); + + http_destroy_cgiargs(h->cgivars); + http_destroy_headers(h->headers); + + gw_free(h); + +} + +/* Make list of recipients and also sender. */ +static void collect_senddata(List *mh, List **xto, + Octstr **from, Octstr **subject, + Octstr **otransid, time_t *expiryt, time_t *deliveryt) +{ + + Octstr *s; + List *l = http_header_find_all(mh, "To"); + if (l) { + int i, n; + for (i = 0, n = list_len(l); idefault_msgexpiry; + } + + if (deliveryt) { + s = http_header_value(mh, octstr_imm("X-Mms-Delivery-Time")); + if (s) { + *deliveryt = date_parse_http(s); + octstr_destroy(s); + } else + *deliveryt = 0; + } + if (subject) + *subject = http_header_value(mh, octstr_imm("Subject")); + + if (otransid) + *otransid = http_header_value(mh, octstr_imm("X-Mms-Transaction-ID")); + +} + +static void sendmms_proxy(MmsHTTPClientInfo *h) +{ + List *rh = http_create_empty_headers(); + MmsMsg *m, *mresp = NULL; + Octstr *reply_body = NULL; + int ctype_set = 0; + int mtype = 0; + int hstatus = HTTP_OK; + char *notify_cmd = NULL; + int msize = h->body ? octstr_len(h->body) : 0; + + debug("proxy.sendinterface", 0, + " --> Enterred sendmms interface, blen=%d <--- ", + msize); + + http_header_add(rh, "Pragma", "no-cache"); + http_header_add(rh, "Cache-Control", "no-cache"); + + + + if (!h->body) { /* A body is required. */ + http_header_add(rh, "Content-Type", "text/plain"); + hstatus = HTTP_FORBIDDEN; + reply_body = octstr_format("Unexpected MMS message[%s], no body?", + mms_message_type_to_cstr(mtype)); + + goto done; + } + + m = mms_frombinary(h->body, h->client_addr); + + if (!m) { + http_header_add(rh, "Content-Type", "text/plain"); + ctype_set = 1; + hstatus = HTTP_BAD_REQUEST; + reply_body = octstr_format("Malformed MMS message"); + debug("proxy.sendinterface", 0, " Parse error on incoming message."); + goto done; + } + + debug("proxy.sendinterface", 0, " Client sent us: "); + +#if 1 + mms_msgdump(m,1); + /* octstr_dump(h->body, 0); */ +#endif + mtype = mms_messagetype(m); + + switch(mtype) { + + case MMS_MSGTYPE_SEND_REQ: + { + Octstr *qf; + List *mh = mms_message_headers(m); + Octstr *from; + List *to = list_create(); + Octstr *subject; + time_t expiryt, deliveryt; + + Octstr *otransid, *value; + int dlr; + + + collect_senddata(mh, &to, &from, &subject, &otransid, &expiryt, &deliveryt); + + /*Delete some headers that must be sent on. */ + mms_remove_headers(m, "Bcc"); + mms_remove_headers(m, "X-Mms-Delivery-Time"); + mms_remove_headers(m, "X-Mms-Expiry"); + mms_remove_headers(m, "X-Mms-Sender-Visibility"); + + + value = http_header_value(mh, octstr_imm("X-Mms-Delivery-Report")); + if (value && + octstr_case_compare(value, octstr_imm("Yes")) == 0) + dlr = 1; + else + dlr = 0; + + + qf = mms_queue_add(from, to, NULL, subject, + NULL, NULL, deliveryt, expiryt, m, NULL, dlr, + octstr_get_cstr(settings->global_queuedir)); + + if (!qf) + mresp = mms_sendconf("Error-transient-failure", "None", octstr_get_cstr(otransid),0); + else { + Octstr *transid = mms_maketransid(octstr_get_cstr(qf), settings->host_alias); + mresp = mms_sendconf("Ok", octstr_get_cstr(transid), octstr_get_cstr(otransid),0); + + /* Log to access log */ + mms_log("Received", from, to, msize, transid, NULL, NULL, "MM1", h->ua); + + octstr_destroy(transid); + octstr_destroy(qf); + } + + if (otransid) + octstr_destroy(otransid); + if (value) + octstr_destroy(value); + octstr_destroy(from); + octstr_destroy(subject); + http_destroy_headers(mh); + list_destroy(to, (list_item_destructor_t *)octstr_destroy); + + notify_cmd = "sent"; + reply_body = mms_tobinary(mresp); + } + break; + case MMS_MSGTYPE_FORWARD_REQ: + { + Octstr *qf = NULL, *token = NULL; + List *mh = mms_message_headers(m); + Octstr *from; + List *to = list_create(); + Octstr *subject; + time_t expiryt, deliveryt; + MmsMsg *mfwd = NULL; + MmsEnvelope *e = NULL; + + Octstr *otransid; + Octstr *url = http_header_value(mh, octstr_imm("X-Mms-Content-Location")); + Octstr *read_report = http_header_value(mh, octstr_imm("X-Mms-Read-Report")); + Octstr *delivery_report = http_header_value(mh, + octstr_imm("X-Mms-Delivery-Report")); + Octstr *allow_report = http_header_value(mh, + octstr_imm("X-Mms-Report-Allowed")); + int dlr; + + collect_senddata(mh, &to, &from, &subject, &otransid, &expiryt, &deliveryt); + + if (mms_decodefetchurl(url, &qf, &token) != 0) { + error(0, "MMS Send interface: failed to decode forward url (%s) from %s!", + octstr_get_cstr(url), + octstr_get_cstr(h->ip)); + mresp = mms_sendconf("Error-permanent-message-not-found", "None", + octstr_get_cstr(otransid),1); + goto forward_done; + } + + e = mms_queue_readenvelope(octstr_get_cstr(qf), + octstr_get_cstr(settings->mm1_queuedir), 1); + + if (!e || + (mfwd = mms_queue_getdata(e)) == NULL) { + error(0, + "MMS Send interface: failed to find envelope/data %s for forward url " + "(%s) from %s (e=%s)!", + octstr_get_cstr(qf), octstr_get_cstr(url), octstr_get_cstr(h->ip), + (e) ? "found" : "not found"); + + mresp = mms_sendconf("Error-permanent-message-not-found", "None", + octstr_get_cstr(otransid),1); + goto forward_done; + } else { /* Found it, etc. */ + + Octstr *transid; + Octstr *pfrom = mms_get_header_value(mfwd, octstr_imm("From")); + Octstr *pdate = mms_get_header_value(mfwd, octstr_imm("Date")); + Octstr *pdelivery_report = mms_get_header_value(mfwd, + octstr_imm("X-Mms-Delivery-Report")); + Octstr *pmsgid = mms_get_header_value(mfwd, octstr_imm("Message-ID")); + Octstr *s; + Octstr *qf2; + int n = 0; + + + /* Modify the message before sending on as per spec. */ + mms_replace_header_value(mfwd, "From", octstr_get_cstr(from)); + + mms_remove_headers(mfwd, "X-Mms-Read-Report"); + if (read_report) + mms_replace_header_value(mfwd, "X-Mms-Read-Report", + octstr_get_cstr(read_report)); + + mms_remove_headers(mfwd, "X-Mms-Delivery-Report"); + if (delivery_report) + mms_replace_header_value(mfwd, "X-Mms-Delivery-Report", + octstr_get_cstr(delivery_report)); + + + if ((s = mms_get_header_value(mfwd, octstr_imm("X-Mms-Previously-Sent-By"))) != NULL) { + sscanf(octstr_get_cstr(s), "%d", &n); + octstr_destroy(s); + } + s = octstr_format("%d %S", n+1, pfrom); + mms_replace_header_value(mfwd, "X-Mms-Previously-Sent-By", octstr_get_cstr(s)); + + + if ((s = mms_get_header_value(mfwd, + octstr_imm("X-Mms-Previously-Sent-Date"))) + != NULL) { + sscanf(octstr_get_cstr(s), "%d", &n); + octstr_destroy(s); + } + s = octstr_format("%d %S", n+1, pdate); + mms_replace_header_value(mfwd, + "X-Mms-Previously-Sent-Date", + octstr_get_cstr(s)); + octstr_destroy(pdate); + + if (delivery_report && + octstr_case_compare(delivery_report, octstr_imm("Yes")) == 0) + dlr = 1; + else + dlr = 0; + /* Message to forward is now ready, write it to queue. */ + qf2 = mms_queue_add(from, to, NULL, subject, + NULL, NULL, deliveryt, expiryt, mfwd, NULL, + dlr, + octstr_get_cstr(settings->global_queuedir)); + + + if (!qf2) + mresp = mms_sendconf("Error-transient-failure", + "None", octstr_get_cstr(otransid),1); + else { + transid = mms_maketransid(octstr_get_cstr(qf), settings->host_alias); + mresp = mms_sendconf("Ok", + octstr_get_cstr(transid), + octstr_get_cstr(otransid),1); + + /* Log to access log */ + mms_log("Forwarded", h->client_addr, to, msize, transid, NULL, NULL, "MM1", h->ua); + + octstr_destroy(transid); + octstr_destroy(qf2); + } + + /* You have queued it, now check if the original sender asked for a delivery notify. + * if so and this forward has not refused it, then send a notify and we are done for now. + */ + + if ((!allow_report || + octstr_case_compare(allow_report, octstr_imm("Yes")) == 0) && + (pdelivery_report && octstr_case_compare(pdelivery_report, + octstr_imm("Yes")))) { + + MmsMsg *mrep = mms_deliveryreport(pmsgid, h->client_addr, time(NULL), octstr_imm("Forwarded")); + Octstr *x; + List *l = list_create(); + list_append(l, pfrom); + + x = mms_queue_add(settings->system_user, l, NULL, NULL, NULL, NULL, 0, + time(NULL) + settings->default_msgexpiry, + mrep, NULL,0, + octstr_get_cstr(settings->global_queuedir)); + octstr_destroy(x); + + list_destroy(l, (list_item_destructor_t *)octstr_destroy); + mms_destroy(mrep); + } + octstr_destroy(pfrom); + octstr_destroy(pdelivery_report); + octstr_destroy(pmsgid); + + } + + forward_done: + + if (mfwd) + mms_destroy(mfwd); + + if (e) { /* Update the message queue and go. */ + e->lastaccess = time(NULL); + if (mms_queue_update(e) != 1) /* Should be freed. */ + mms_queue_free_env(e); + e = NULL; + } + + if (qf) + octstr_destroy(qf); + if (token) + octstr_destroy(token); + if (from) + octstr_destroy(from); + if (subject) + octstr_destroy(subject); + if (mh) + http_destroy_headers(mh); + if (to) + list_destroy(to, (list_item_destructor_t *)octstr_destroy); + if (otransid) + octstr_destroy(otransid); + if (url) + octstr_destroy(url); + + if (read_report) + octstr_destroy(read_report); + if (allow_report) + octstr_destroy(allow_report); + if (delivery_report) + octstr_destroy(delivery_report); + + reply_body = mms_tobinary(mresp); + notify_cmd = "fetched"; + } + break; + case MMS_MSGTYPE_NOTIFYRESP: + case MMS_MSGTYPE_ACKNOWLEDGE_IND: + { + Octstr *transid = mms_get_header_value(m, octstr_imm("X-Mms-Transaction-ID")); + Octstr *allow_report = mms_get_header_value(m, octstr_imm("X-Mms-Report-Allowed")); + + Octstr *qf = mms_getqf_fromtransid(transid); + MmsEnvelope *e = mms_queue_readenvelope(octstr_get_cstr(qf), + octstr_get_cstr(settings->mm1_queuedir), 1); + Octstr *status; + + MmsMsg *mrpt; + + + if (mtype == MMS_MSGTYPE_NOTIFYRESP) + status = mms_get_header_value(m, octstr_imm("X-Mms-Status")); + else /* This (acknowledge-ind) is the same as notify_resp with status=retrieved. */ + status = octstr_imm("Retrieved"); + + if (!e) { + error(0, "MMS Send interface: Received notification type=%s " + "[url=%s, transid=%s, qf=%s] but could not find queue entry!\n", + mms_message_type_to_cstr(mtype), octstr_get_cstr(h->url), + octstr_get_cstr(transid), octstr_get_cstr(qf)); + goto mdone; + } + + if (octstr_str_compare(status, "Retrieved") == 0) { + MmsEnvelopeTo *t = list_get(e->to, 0); + if (t) + t->process = 0; + } else + e->lastaccess = time(NULL); /* Note now that it has been touched. */ + + /* If the allow report header is missing (default is Yes) + * or it is there and has said we must send report, + * and sender requested a report, then queue a report. + */ + if ((allow_report == NULL + || octstr_case_compare(allow_report, octstr_imm("Yes")) == 0) && + e->dlr) { + Octstr *x; + List *l = list_create(); + + mrpt = mms_deliveryreport(e->msgId, h->client_addr, time(NULL), status); + list_append(l, octstr_duplicate(e->from)); + + x = mms_queue_add(settings->system_user, l, NULL, NULL, NULL, NULL, 0, + time(NULL) + settings->default_msgexpiry, mrpt, NULL, 0, + octstr_get_cstr(settings->global_queuedir)); + octstr_destroy(x); + + list_destroy(l, (list_item_destructor_t *)octstr_destroy); + mms_destroy(mrpt); + } + + mdone: + + /* Log to access log */ + mms_log2("NotifyResp", h->client_addr, NULL, msize, transid, NULL, NULL, "MM1", h->ua); + + if (e && + mms_queue_update(e) != 1) /* Should be freed. */ + mms_queue_free_env(e); + + octstr_destroy(qf); + octstr_destroy(transid); + octstr_destroy(allow_report); + octstr_destroy(status); + http_header_add(rh, "Content-Type", "text/plain"); + ctype_set = 1; + reply_body = octstr_imm("Received"); + + notify_cmd = "fetched"; + } + break; + + case MMS_MSGTYPE_READ_REC_IND: + { + List *mh = mms_message_headers(m); + Octstr *from; + List *to = list_create(); + Octstr *x; + + if (mms_convert_readrec2readorig(m) < 0) + goto mdone2; + + collect_senddata(mh, &to, &from, NULL, NULL, NULL, NULL); + + x = mms_queue_add(from, to, NULL, NULL, NULL, + NULL, time(NULL), time(NULL) + settings->default_msgexpiry, + m, NULL, 0, octstr_get_cstr(settings->global_queuedir)); + + /* Log to access log */ + mms_log("ReadReport", h->client_addr, NULL, msize, NULL, NULL, NULL, "MM1", h->ua); + + octstr_destroy(x); + octstr_destroy(from); + http_destroy_headers(mh); + mdone2: + + list_destroy(to, (list_item_destructor_t *)octstr_destroy); + http_header_add(rh, "Content-Type", "text/plain"); + ctype_set = 1; + reply_body = octstr_imm("Received"); + + notify_cmd = "fetched"; + break; + } + default: + http_header_add(rh, "Content-Type", "text/plain"); + ctype_set = 1; + hstatus = HTTP_FORBIDDEN; + reply_body = octstr_format("Unexpected MMS message type %s", mms_message_type_to_cstr(mtype)); + break; + } + + + if (notify_cmd) /* Inform provisioning server */ + notify_prov_server(octstr_get_cstr(settings->prov_notify), + h->base_client_addr ? octstr_get_cstr(h->base_client_addr) : "unknown", + notify_cmd, + octstr_get_cstr(settings->prov_notify_arg)); + + mms_destroy(m); + /* Send reply. */ + + done: + + if (mresp) + mms_destroy(mresp); + if (!ctype_set) + http_header_add(rh, "Content-Type", "application/vnd.wap.mms-message"); + + http_send_reply(h->client, hstatus, rh, reply_body); + http_destroy_headers(rh); + octstr_destroy(reply_body); + + octstr_destroy(h->ip); + octstr_destroy(h->url); + + if (h->base_client_addr) + octstr_destroy(h->base_client_addr); + octstr_destroy(h->client_addr); + + if (h->ua) octstr_destroy(h->ua); + if (h->body) octstr_destroy(h->body); + http_destroy_cgiargs(h->cgivars); + http_destroy_headers(h->headers); + + gw_free(h); + +} diff --git a/mbuni/mmsc/mmssend.c b/mbuni/mmsc/mmssend.c new file mode 100644 index 0000000..e360418 --- /dev/null +++ b/mbuni/mmsc/mmssend.c @@ -0,0 +1,135 @@ +#include +#include +#include "mms_queue.h" +#include "mms_uaprof.h" +#include "mms_util.h" + +static Octstr *data; +static Octstr *from; +static List *to; +static MmsMsg *m; + +static MmsBoxSettings *settings; + +static int find_own(int i, int argc, char *argv[]) +{ + + if (argv[i][1] == 'f') + if (i + 1 < argc) { + from = octstr_create(argv[i+1]); + return 1; + } else + return -1; + else if (argv[i][1] == 't') + if (i + 1 < argc) { + int j, m; + List *l = octstr_split(octstr_create(argv[i+1]), + octstr_imm(":")); + for (j = 0, m = list_len(l); j < m; j++) + list_append(to, list_get(l, j)); + list_destroy(l, NULL); + return 1; + } else + return -1; + else if (argv[i][1] == 'm') + if (i + 1 < argc) { + data = octstr_read_file(argv[i+1]); + return 1; + } else + return -1; + else + return -1; +} + +static Cfg *cfg; + +static List *proxyrelays; + +int main(int argc, char *argv[]) +{ + Octstr *fname, *s; + + int cfidx; + CfgGroup *grp; + Octstr *log, *alog; + long loglevel; + int msize; + + if (argc < 2) + return -1; + + mms_lib_init(); + to = list_create(); + srandom(time(NULL)); + + cfidx = get_and_set_debugs(argc, argv, find_own); + + if (argv[cfidx] == NULL) + fname = octstr_imm("mmsc.conf"); + else + fname = octstr_create(argv[cfidx]); + + cfg = cfg_create(fname); + + if (cfg_read(cfg) == -1) + panic(0, "Couldn't read configuration from '%s'.", octstr_get_cstr(fname)); + + octstr_destroy(fname); + + info(0, "----------------------------------------"); + info(0, " MMSC Message sender runner version %s starting", MMSC_VERSION); + + grp = cfg_get_single_group(cfg, octstr_imm("core")); + log = cfg_get(grp, octstr_imm("log-file")); + if (log != NULL) { + if (cfg_get_integer(&loglevel, grp, octstr_imm("log-level")) == -1) + loglevel = 0; + log_open(octstr_get_cstr(log), loglevel, GW_NON_EXCL); + octstr_destroy(log); + } + + /* Get access log and open it. */ + alog = cfg_get(grp, octstr_imm("access-log")); + if (alog) { + alog_open(octstr_get_cstr(alog), 1, 1); + octstr_destroy(alog); + } + + /* Load proxy relays. */ + + proxyrelays = mms_proxy_relays(cfg); + + /* Load settings. */ + settings = mms_load_mmsbox_settings(cfg); + + if (!settings) + panic(0, "No global MMSC configuration!"); + + if (from == NULL || + to == NULL) { + error(0, "Sender and recipient addresses required!\n"); + exit(-1); + } + + mms_start_profile_engine(octstr_get_cstr(settings->ua_profile_cache_dir)); + + if (data) { + m = mms_frombinary(data, from ? from : octstr_imm("anon@anon")); + if (m) + mms_msgdump(m,0); + msize = octstr_len(data); + } else + msize = 0; + if (!m) + panic(0, "No Message supplied, or failed to decode binary data!"); + + s = mms_queue_add(from, to, NULL, NULL, NULL, NULL, time(NULL), + time(NULL) + settings->default_msgexpiry, m, + NULL, 0, octstr_get_cstr(settings->global_queuedir)); + + mms_log("Received", from, to, msize, s, NULL, NULL, "mmssend",NULL); + + printf("Queued: %s\n", octstr_get_cstr(s)); + octstr_destroy(s); + return 0; +} diff --git a/mbuni/stamp-h1 b/mbuni/stamp-h1 new file mode 100644 index 0000000..4547fe1 --- /dev/null +++ b/mbuni/stamp-h1 @@ -0,0 +1 @@ +timestamp for config.h diff --git a/mbuni/todo b/mbuni/todo new file mode 100644 index 0000000..b2683f1 --- /dev/null +++ b/mbuni/todo @@ -0,0 +1,5 @@ +- correct handling of sender-visibility flag when receiving message (and also use correct address for billing. no spoofing). +- Sending DLR correctly when sending from/to email. +- In content adaptation: Colour depth adjustment +- logging a la access.log (one log per interface -- mm1-log, mm4-log, etc) +- Need to parameterise some values: tmp dir, response messages (say on failed content adaptation)