Browse files

Inital commit to public repository

  • Loading branch information...
0 parents commit aadaa8b5d7eda23e72dbded9d6437b40358353f3 @hacker committed Mar 8, 2009
Showing with 1,094 additions and 0 deletions.
  1. +14 −0 .gitignore
  2. +12 −0 AUTHORS
  3. +19 −0 COPYING
  4. 0 ChangeLog
  5. +16 −0 Makefile.am
  6. +2 −0 NEWS
  7. +6 −0 NEWS.xml
  8. +24 −0 NEWS.xsl
  9. 0 README
  10. +8 −0 autogen.sh
  11. +71 −0 configure.ac
  12. +5 −0 debian/changelog
  13. +1 −0 debian/compat
  14. +13 −0 debian/control
  15. +1 −0 debian/copyright
  16. +3 −0 debian/dirs
  17. +2 −0 debian/docs
  18. +1 −0 debian/examples
  19. +57 −0 debian/iii.init
  20. +18 −0 debian/iii.postinst
  21. +3 −0 debian/iii.postrm
  22. +4 −0 debian/iii.prerm
  23. +11 −0 debian/rules
  24. +1 −0 doc/.gitignore
  25. +17 −0 doc/000000000000.conf
  26. +6 −0 doc/Makefile.am
  27. +106 −0 doc/iiid.8.in
  28. +28 −0 scripts/xml2sh.xsl
  29. +10 −0 src/.gitignore
  30. +32 −0 src/Makefile.am
  31. +47 −0 src/eyefi.h
  32. +186 −0 src/eyefiservice.cc
  33. +26 −0 src/eyefiworker.cc
  34. +15 −0 src/eyefiworker.h
  35. +67 −0 src/eyekinfig.cc
  36. +25 −0 src/eyekinfig.h
  37. +103 −0 src/eyetil.cc
  38. +48 −0 src/eyetil.h
  39. +86 −0 src/iiid.cc
14 .gitignore
@@ -0,0 +1,14 @@
+Makefile
+Makefile.in
+/aclocal.m4
+/autom4te.cache
+/config.h
+/config.h.in
+/config.log
+/config.status
+/configure
+/stamp-h1
+/INSTALL
+/depcomp
+/install-sh
+/missing
12 AUTHORS
@@ -0,0 +1,12 @@
+Klever dissected:
+ Michael 'hacker' Krelin <hacker@klever.net>
+ Leonid Ivanov <kamel@klever.net>
+
+
+Thanks to:
+
+Brian Muller <bmuller AT butterfat DOT net> of mod_auth_openid project for
+suggestions, bug reports, testing and actually making use of the library.
+
+Joseph Smarr <joseph AT plaxo DOT com> of plaxo.com for robustness enhancements
+and making use of my work.
19 COPYING
@@ -0,0 +1,19 @@
+Copyright (c) 2009 Klever Group (http://www.klever.net/)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
0 ChangeLog
No changes.
16 Makefile.am
@@ -0,0 +1,16 @@
+SUBDIRS=doc src
+
+all-local: NEWS
+
+NEWS: NEWS.xsl NEWS.xml
+ ${XSLTPROC} -o $@ $^
+
+EXTRA_DIST = NEWS.xml NEWS.xsl
+
+ISSUEFILES = $$(find ${top_srcdir} -type f '(' \
+ -name '*.cc' -or -name '*.h' \
+ ')' ) \
+ ${top_srcdir}/configure.ac
+issues: todo fixme xxx
+todo fixme xxx:
+ @grep --color=auto -in '$@:' ${ISSUEFILES} || true
2 NEWS
@@ -0,0 +1,2 @@
+0.0 (March 8th, 2009)
+ - Initial release
6 NEWS.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="us-ascii"?>
+<news>
+ <version version="0.0" date="March 8th, 2009">
+ <ni>Initial release</ni>
+ </version>
+</news>
24 NEWS.xsl
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="us-ascii"?>
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ >
+ <xsl:output
+ method="text"
+ encoding="us-ascii"
+ media-type="text/plain" />
+
+ <xsl:template match="news">
+ <xsl:apply-templates/>
+ </xsl:template>
+ <xsl:template match="version">
+ <xsl:value-of select="concat(@version,' (',@date,')&#xA;')"/>
+ <xsl:apply-templates/>
+ </xsl:template>
+ <xsl:template match="ni">
+ <xsl:text> - </xsl:text>
+ <xsl:apply-templates mode="text"/>
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:template>
+ <xsl:template match="*|text()"/>
+
+</xsl:stylesheet>
0 README
No changes.
8 autogen.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+WANT_AUTOMAKE=1.8
+export WANT_AUTOMAKE
+ aclocal \
+&& autoheader \
+&& automake -a \
+&& autoconf \
+&& ./configure "$@"
71 configure.ac
@@ -0,0 +1,71 @@
+AC_INIT([iii], [0.0], [iii-bugs@klever.net])
+AC_CONFIG_SRCDIR([configure.ac])
+AC_CONFIG_HEADERS([config.h])
+AM_INIT_AUTOMAKE([dist-bzip2])
+
+AC_PROG_INSTALL
+AC_PROG_CXX
+AC_PROG_CC
+PKG_PROG_PKG_CONFIG
+
+AC_HEADER_STDC
+
+AC_PATH_PROG([XSLTPROC],[xsltproc],[true])
+
+PKG_CHECK_MODULES([MODULES],[gsoap++ openssl libconfuse],,[
+ AC_MSG_ERROR([one of the build dependencies isn't satisfied])
+])
+
+AC_PATH_PROG([SOAPCPP2],[soapcpp2],[false])
+test "$SOAPCPP2" = "false" && AC_MSG_ERROR([no soapcpp2 tool, part of gsoap package, found.])
+
+notfound=false
+AC_CHECK_HEADERS([archive.h],[
+ AC_CHECK_LIB([archive],[archive_read_new],,[notfound=true])
+],[notfound=true])
+$notfound && AC_MSG_ERROR([no required libarchive library found. get one from http://people.freebsd.org/~kientzle/libarchive/])
+
+notfound=false
+AC_LANG_PUSH([C++])
+AC_CHECK_HEADERS([autosprintf.h],[
+ AC_CHECK_LIB([asprintf],[main],,[notfound=true])
+],[notfound=true])
+$notfound && AC_MSG_ERROR([no autosprintf, part of gettext, found])
+AC_LANG_POP([C++])
+
+nitpick=false
+AC_ARG_ENABLE([nitpicking],
+ AC_HELP_STRING([--enable-nitpicking],[make compiler somewhat overly fastidious about the code it deals with]),
+ [ test "$enableval" = "no" || nitpick=true ]
+)
+if $nitpick ; then
+ CPP_NITPICK="-pedantic -Wall -Wextra -Wundef -Wshadow \
+ -Wunsafe-loop-optimizations -Wconversion -Wmissing-format-attribute \
+ -Wredundant-decls -ansi -Wlogical-op -Wmissing-noreturn"
+ C_NITPICK="$CPP_NITPICK"
+ CXX_NITPICK="$C_NITPICK"
+
+ CPPFLAGS="$CPPFLAGS $CPP_NITPICK"
+ CFLAGS="$CFLAGS $C_NITPICK"
+ CXXFLAGS="$CXXFLAGS $CXX_NITPICK"
+fi
+
+ndebug=true
+AC_ARG_ENABLE([debug],
+ AC_HELP_STRING([--enable-debug],[enable debugginc code]),
+ [ test "$enableval" = "no" || ndebug=false ]
+)
+if $ndebug ; then
+ CPPFLAGS_DEBUG="-DNDEBUG"
+else
+ CPPFLAGS_DEBUG="-DDEBUG"
+fi
+AC_SUBST([CPPFLAGS_DEBUG])
+
+AC_CONFIG_FILES([
+ Makefile
+ src/Makefile
+ doc/Makefile
+ doc/iiid.8
+])
+AC_OUTPUT
5 debian/changelog
@@ -0,0 +1,5 @@
+iii (0.0) unstable; urgency=low
+
+ * Initial release
+
+ -- Michael Krelin <hacker@klever.net> Sun, 08 Mar 2009 19:39:38 +0100
1 debian/compat
@@ -0,0 +1 @@
+4
13 debian/control
@@ -0,0 +1,13 @@
+Source: iii
+Section: graphics
+Priority: optional
+Maintainer: Michael Krelin <hacker@klever.net>
+Build-Depends: debhelper (>=7), gsoap, libssl-dev, libconfuse-dev, libarchive-dev, gettext
+Standards-Version: 3.8.0
+
+Package: iii
+Architecture: any
+Depends: adduser, ${shlibs:Depends}
+Description: Eye-Fi Manager implementation
+ Implementation of Eye-Fi manager service for Linux
+
1 debian/copyright
3 debian/dirs
@@ -0,0 +1,3 @@
+/etc/iii
+/usr/sbin
+/usr/share/doc/iii
2 debian/docs
@@ -0,0 +1,2 @@
+AUTHORS
+NEWS
1 debian/examples
@@ -0,0 +1 @@
+doc/000000000000.conf
57 debian/iii.init
@@ -0,0 +1,57 @@
+#!/bin/sh
+### BEGIN INIT INFO
+# Provides: iii
+# Required-Start: $remote_fs $network
+# Required-Stop: $remote_fs $network
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: Start iii eye-fi card manager daemon.
+### END INIT INFO
+
+set -e
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin
+NAME=iii
+DAEMON=/usr/sbin/${NAME}d
+DESC="Eye-Fi card manager daemon"
+PIDFILE=/var/run/${NAME}d.pid
+SCRIPTNAME=/etc/init.d/$NAME
+USER=eyefi
+SSD="/sbin/start-stop-daemon"
+
+test -x $DAEMON || exit 0
+
+if [ -f /etc/default/$NAME ] ; then
+ . /etc/default/$NAME
+ fi
+
+test -z "$NO_IIID" || exit 0
+
+. /lib/lsb/init-functions
+
+case "$1" in
+ start)
+ log_daemon_msg "Starting $DESC" $NAME
+ $SSD --start --pidfile $PIDFILE --chuid $USER --background --make-pidfile --exec $DAEMON -- $DAEMON_OPTS
+ log_end_msg $?
+ ;;
+ stop)
+ log_daemon_msg "Stopping $DESC" $NAME
+ if $SSD --stop --oknodo --retry 30 --pidfile $PIDFILE --exec $DAEMON ; then
+ rm -f $PIDFILE
+ log_end_msg 0
+ else
+ log_end_msg 1
+ fi
+ ;;
+ restart|force-reload)
+ $SCRIPTNAME stop
+ $SCRIPTNAME start
+ ;;
+ *)
+ echo "Usage: $SCRIPTNAME {start|stop}" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
18 debian/iii.postinst
@@ -0,0 +1,18 @@
+#!/bin/sh
+set -e
+UG=eyefi
+P=eyekindo
+H=/var/lib/$P
+
+if [ "$1" = configure ] ; then
+ getent group $UG >/dev/null 2>&1 || addgroup --system $UG
+ getent passwd $UG >/dev/null 2>&1 || adduser --system --home $H \
+ --no-create-home --disabled-password --ingroup $UG $UG
+ if ! test -d $H ; then
+ mkdir -p $H
+ chown $UG:$UG $H
+ chmod 2770 $H
+ fi
+fi
+
+#DEBHELPER#
3 debian/iii.postrm
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+#DEBHELPER#
4 debian/iii.prerm
@@ -0,0 +1,4 @@
+#!/bin/sh
+set -e
+
+#DEBHELPER#
11 debian/rules
@@ -0,0 +1,11 @@
+#!/usr/bin/make -f
+%:
+ dh $@
+
+build: build-stamp
+build-stamp:
+ sh autogen.sh --version
+ dh build --before configure
+ dh_auto_configure -- --enable-debug
+ dh build --after configure
+ touch $@
1 doc/.gitignore
@@ -0,0 +1 @@
+iiid.8
17 doc/000000000000.conf
@@ -0,0 +1,17 @@
+# target directory for uploaded files, if the name contains %s, it is replaced
+# with eyefi card mac address.
+targetdir = "/var/lib/eyekindo/%s/"
+# the upload key as seen in Mac and (perhaps) windows Settings.xml file
+uploadkey = "e3e2c4a305cee6bce0ebb38a3259ac08"
+
+# commands to be executed at certain events, the commands also receive certain
+# values of interested passed via environment.
+# for on-start-session: EYEFI_MACADDRESS, EYEFI_TRANSFER_MODE, EYEFI_TRANSFERMODETIMESTAMP
+on-start-session = "bash /usr/local/lib/iii/on-start-session.bash"
+# for on-upload-photo: EYEFI_MACADDRESS, EYEFI_UPLOADED (uploaded file name)
+on-upload-photo = "bash /usr/local/lib/iii/on-upload-photo.bash"
+# for on-mark-last-photo-in-roll: EYEFI_MACADDRESS, EYEFI_MERGEDELTA
+on-mark-last-photo-in-roll = "bash /usr/local/lib/iii/on-mark-last-photo-in-roll.bash"
+
+# file mode creation mask
+umask = 002
6 doc/Makefile.am
@@ -0,0 +1,6 @@
+man_MANS=iiid.8
+
+EXTRA_DIST = 000000000000.conf
+
+clean-local:
+ rm -f iiid.8
106 doc/iiid.8.in
@@ -0,0 +1,106 @@
+.TH iiid 8 "March 8th, 2009" "iiid(8)" "Klever Group (http://www.klever.net/)"
+.hla en
+
+.SH NAME
+
+iiid \- An eye-fi card management daemon
+
+.SH SYNOPSYS
+
+\fBiiid\fR
+[\fB-h\fR] [\fB--help\fR] [\fB--usage\fR]
+[\fB-V\fR] [\fB--version\fR]
+[\fB-L\fR] [\fB--license\fR]
+[\fB-p\fR \fIport\fR] [\fB--port=\fR\fIport\fR]
+
+.SH DESCRIPTION
+
+iiid daemon is a minimalistic open source eye-fi management daemon
+implementation. It is listening for incoming connections and accepts files from
+eye-fi card, optionally invoking scripts to sort out files or send out
+notifications.
+
+.SH OPTIONS
+
+.TP
+\fB-p\fR \fIport\fR, \fB--port=\fR\fIport\fR
+Set the port to listen to. You're not likely to ever need to change this,
+perhaps for debugging purpose or if you want to proxy connections.
+.TP
+\fB-h\fR, \fB--help\fR, \fB--usage\fR
+Display short usage instructions and exit.
+.TP
+\fB-V\fR, \fB--version\fR
+Report version and exit.
+.TP
+\fB-L\fR, \fB--license\fR
+Show licensing terms.
+
+.SH EXIT STATUS
+
+The daemon isn't supposed to exit at this point, but, like most executables, it returns zero in case of success, non-zero otherwise.
+
+.SH FILES
+
+.TP
+@sysconfdir@/iii/\fBXXXXXXXXXXXX.conf/fR
+Upon successfull connection, the daemon reads the configuration from this file,
+where XXXXXXXXXXXX is the macaddress of your eyefi card. You can lookup the mac
+address of your card in your Mac/Windows Settings.xml file. Be sure to remove
+dashes from it.
+
+.SH CONFIGURATION OPTIONS
+
+.TP
+\fBtargetdir\fR = "\fI/var/lib/iii/%s/\fR"
+Sets the target directory for uploaded files. If the \fI%s\fR placeholder is
+present in the string, it is replaced with card mac address.
+.TP
+\fBuploadkey\fR = "\fIxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\fR"
+The upload key as seen in Mac/Windows Settings.xml file.
+.TP
+\fBon-start-session\fR = "\fIlogger iii-StartSession ${EYEFI_MACADDRESS} ${EYEFI_TRANSFER_MODE} ${EYEFI_TRANSFERMODETIMESTAMP}\fR"
+The command to execute on \fBStartSession\fR request. The command receives some
+information via environment variables.
+.TP
+\fBon-upload-photo\fR = "\fIlogger iii-UploadPhoto ${EYEFI_MACADDRESS} ${EYEFI_UPLOADED}\fR"
+The command to execute after successfull photo upload. The information about
+the card mac address and uploaded file name is passed via environment
+variables.
+.TP
+\fBon-mark-last-photo-in-roll\fR = "\fIlogger iii-MarkLastPhotoInRoll ${EYEFI_MACADDRESS} ${EYEFI_MERGEDELTA}\fR"
+The command to execute on \fBMarkLastPhotoInRoll\fR request. The information
+about request is passed via environment variables.
+.TP
+\fBumask\fR = \fI002\fR
+The file mode creation mask.
+
+.SH AUTHOR
+
+Written by Michael Krelin <hacker@klever.net>
+
+.SH COPYRIGHT
+
+Copyright (c) 2009 Klever Group (http://www.klever.net/)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+.SH BUGS
+
+You tell me. Send reports to <iii-bugs@klever.net>
28 scripts/xml2sh.xsl
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="us-ascii"?>
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ >
+ <xsl:output
+ method="text"
+ encoding="us-ascii"
+ media-type="text/plain" />
+
+ <xsl:template match="/">
+ <xsl:apply-templates select="/Config/Cards/Card"/>
+ </xsl:template>
+
+ <xsl:template match="/Config/Cards/Card">
+ <xsl:text>cat &gt;</xsl:text>
+ <xsl:value-of select="translate(@MacAddress,'-','')"/>
+ <xsl:text>.conf &lt;&lt;__EOF__&#xa;</xsl:text>
+ <xsl:text>uploadkey=&quot;</xsl:text>
+ <xsl:value-of select="UploadKey"/>
+ <xsl:text>&quot;&#xa;</xsl:text>
+ <xsl:text>targetdir=&quot;/var/lib/iii/%s&quot;&#xa;</xsl:text>
+ <xsl:text>umask=022&#xa;</xsl:text>
+ <xsl:text>__EOF__&#xa;</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="*|text()"/>
+
+</xsl:stylesheet>
10 src/.gitignore
@@ -0,0 +1,10 @@
+/.deps
+eyefi.nsmap
+soapC.cpp
+soapH.h
+soapStub.h
+soapeyefiService.cpp
+soapeyefiService.h
+*.o
+/iiid
+/COPYING.cc
32 src/Makefile.am
@@ -0,0 +1,32 @@
+sbin_PROGRAMS=iiid
+noinst_HEADERS = \
+ eyefi.h \
+ eyekinfig.h eyetil.h \
+ eyefiworker.h
+
+AM_CPPFLAGS = ${CPPFLAGS_DEBUG} \
+ -DEYEKIN_CONF_DIR=\"${sysconfdir}/${PACKAGE}\"
+DEFAULT_INCLUDES = -I${top_builddir} -I${builddir} -I${srcdir}
+INCLUDES = ${MODULES_CFLAGS}
+
+iiid_SOURCES = iiid.cc \
+ eyekinfig.cc eyetil.cc \
+ eyefiservice.cc eyefiworker.cc
+nodist_iiid_SOURCES = \
+ ${builddir}/soapC.cpp ${builddir}/soapeyefiService.cpp \
+ COPYING.cc
+iiid_LDADD = ${MODULES_LIBS}
+
+COPYING.cc: ${top_srcdir}/COPYING
+ echo "const char * COPYING = " >$@ || (rm $@;exit 1)
+ sed -e 's/"/\\"/g' -e 's/^/\"/' -e 's/$$/\\n\"/' $< >>$@ || (rm $@;exit 1)
+ echo ';' >>$@ || (rm $@;exit 1)
+
+${srcdir}/eyefiservice.cc: ${builddir}/soapeyefiService.h
+${srcdir}/iiid.cc: ${builddir}/eyefi.nsmap
+
+${builddir}soapC.cpp ${builddir}/soapeyefiService.cpp ${builddir}/eyefi.nsmap ${builddir}/soapeyefiService.h: ${srcdir}/eyefi.h
+ ${SOAPCPP2} -d${builddir} -S -L -a -i -w -x $<
+
+clean-local:
+ rm -f soap{{H,Stub,eyefiService}.h,{C,eyefiService}.cpp} eyefi.nsmap COPYING.cc
47 src/eyefi.h
@@ -0,0 +1,47 @@
+//gsoap efs service name: eyefi
+//gsoap efs service location: http://api.eye.fi/api/soap/eyefilm/v1
+//gsoap efs service namespace: EyeFi/SOAP/EyeFilm
+//gsoap efs service method-action: StartSession "urn:StartSession"
+//gsoap efs service method-action: GetPhotoStatus "urn:GetPhotoStatus"
+//gsoap efs service method-action: MarkLastPhotoInRoll "urn:MarkLastPhotoInRoll"
+//gsoap rns service namespace: http://localhost/api/soap/eyefilm
+
+struct rns__StartSessionResponse {
+ std::string credential;
+ std::string snonce;
+ int transfermode;
+ unsigned int transfermodetimestamp;
+ bool upsyncallowed;
+};
+
+int efs__StartSession(
+ std::string macaddress,std::string cnonce,
+ int transfermode,long transfermodetimestamp,
+ struct rns__StartSessionResponse &r );
+
+struct rns__GetPhotoStatusResponse {
+ int fileid;
+ long offset;
+};
+
+int efs__GetPhotoStatus(
+ std::string credential, std::string macaddress,
+ std::string filename, long filesize, std::string filesignature,
+ struct rns__GetPhotoStatusResponse &r );
+
+struct rns__MarkLastPhotoInRollResponse {
+};
+
+int efs__MarkLastPhotoInRoll(
+ std::string macaddress, int mergedelta,
+ struct rns__MarkLastPhotoInRollResponse &r );
+
+struct rns__UploadPhotoResponse {
+ bool success;
+};
+
+int efs__UploadPhoto(
+ int fileid, std::string macaddress,
+ std::string filename, long filesize, std::string filesignature,
+ std::string encryption, int flags,
+ struct rns__UploadPhotoResponse& r );
186 src/eyefiservice.cc
@@ -0,0 +1,186 @@
+#include <cassert>
+#include <iostream>
+#include <fstream>
+#include <stdexcept>
+#include <iterator>
+#include <syslog.h>
+#include <sys/wait.h>
+#include <autosprintf.h>
+#include "eyekinfig.h"
+#include "eyetil.h"
+#include "soapeyefiService.h"
+
+static bool detached_child() {
+ pid_t p = fork();
+ if(p<0) throw std::runtime_error("failed to fork()");
+ if(!p) {
+ p = fork();
+ if(p<0) {
+ syslog(LOG_ERR,"Failed to re-fork child process");
+ _exit(-1);
+ }
+ if(!p) {
+ setsid();
+ for(int i=getdtablesize();i>=0;--i) close(i);
+ int i=open("/dev/null",O_RDWR); assert(i==0);
+ i = dup(i); assert(i==1);
+ i = dup(i); assert(i==2);
+ return true;
+ }
+ _exit(0);
+ }
+ int rc;
+ if(waitpid(p,&rc,0)<0) throw std::runtime_error("failed to waitpid()");
+ if(!WIFEXITED(rc)) throw std::runtime_error("error in forked process");
+ if(WEXITSTATUS(rc)) throw std::runtime_error("forked process signalled error");
+ return false;
+}
+
+int eyefiService::StartSession(
+ std::string macaddress,std::string cnonce,
+ int transfermode,long transfermodetimestamp,
+ struct rns__StartSessionResponse &r ) {
+#ifndef NDEBUG
+ syslog(LOG_DEBUG,
+ "StartSession request from %s with cnonce=%s, transfermode=%d, transfermodetimestamp=%ld",
+ macaddress.c_str(), cnonce.c_str(), transfermode, transfermodetimestamp );
+#endif
+ r.credential = binary_t(macaddress+cnonce+eyekinfig_t(macaddress).get_upload_key()).md5().hex();
+ /* TODO: better nonce generator */
+ time_t t = time(0);
+ r.snonce = binary_t(&t,sizeof(t)).md5().hex();
+ r.transfermode=2;
+ r.transfermodetimestamp=t;
+ r.upsyncallowed=false;
+
+ std::string cmd = eyekinfig_t(macaddress).get_on_start_session();
+ if(!cmd.empty()) {
+ if(detached_child()) {
+ putenv( gnu::autosprintf("EYEFI_MACADDRESS=%s",macaddress.c_str()) );
+ putenv( gnu::autosprintf("EYEFI_TRANSFERMODE=%d",transfermode) );
+ putenv( gnu::autosprintf("EYEFI_TRANSFERMODETIMESTAMP=%ld",transfermodetimestamp) );
+ char *argv[] = { (char*)"/bin/sh", (char*)"-c", (char*)cmd.c_str(), 0 };
+ execv("/bin/sh",argv);
+ syslog(LOG_ERR,"Failed to execute '%s'",cmd.c_str());
+ _exit(-1);
+ }
+ }
+ return SOAP_OK;
+}
+
+int eyefiService::GetPhotoStatus(
+ std::string credential, std::string macaddress,
+ std::string filename, long filesize, std::string filesignature,
+ struct rns__GetPhotoStatusResponse &r ) {
+#ifndef NDEBUG
+ syslog(LOG_DEBUG,
+ "GetPhotoStatus request from %s with credential=%s, filename=%s, filesize=%ld, filesignature=%s",
+ macaddress.c_str(), credential.c_str(), filename.c_str(), filesize, filesignature.c_str() );
+#endif
+ r.fileid = 1; r.offset = 0;
+ return SOAP_OK;
+}
+
+int eyefiService::MarkLastPhotoInRoll(
+ std::string macaddress, int mergedelta,
+ struct rns__MarkLastPhotoInRollResponse &r ) {
+#ifndef NDEBUG
+ syslog(LOG_DEBUG,
+ "MarkLastPhotoInRoll request from %s with mergedelta=%d",
+ macaddress.c_str(), mergedelta );
+#endif
+ std::string cmd = eyekinfig_t(macaddress).get_on_mark_last_photo_in_roll();
+ if(!cmd.empty()) {
+ if(detached_child()) {
+ putenv( gnu::autosprintf("EYEFI_MACADDRESS=%s",macaddress.c_str()) );
+ putenv( gnu::autosprintf("EYEFI_MERGEDELTA=%d",mergedelta) );
+ char *argv[] = { (char*)"/bin/sh", (char*)"-c", (char*)cmd.c_str(), 0 };
+ execv("/bin/sh",argv);
+ syslog(LOG_ERR,"Failed to execute '%s'",cmd.c_str());
+ _exit(-1);
+ }
+ }
+ return SOAP_OK;
+}
+
+int eyefiService::UploadPhoto(
+ int fileid, std::string macaddress,
+ std::string filename, long filesize, std::string filesignature,
+ std::string encryption, int flags,
+ struct rns__UploadPhotoResponse& r ) {
+#ifndef NDEBUG
+ syslog(LOG_DEBUG,
+ "UploadPhoto request from %s with fileid=%d, filename=%s, filesize=%ld,"
+ " filesignature=%s, encryption=%s, flags=%04X",
+ macaddress.c_str(), fileid, filename.c_str(), filesize,
+ filesignature.c_str(), encryption.c_str(), flags );
+#endif
+ eyekinfig_t eyekinfig(macaddress);
+
+ umask(eyekinfig.get_umask());
+
+ std::string td = eyekinfig.get_targetdir();
+ /* TODO: try to create, if needed */
+ tmpdir_t indir(td+"/.incoming.XXXXXX");
+
+ for(soap_multipart::iterator i=mime.begin(),ie=mime.end();i!=ie;++i) {
+#ifndef NDEBUG
+ syslog(LOG_DEBUG,
+ " MIME attachment with id=%s, type=%s, size=%ld",
+ (*i).id, (*i).type, (long)(*i).size );
+#endif
+
+#ifndef NDEBUG
+ if((*i).id && !strcmp((*i).id,"INTEGRITYDIGEST")) {
+ std::string idigest((*i).ptr,(*i).size);
+ syslog(LOG_DEBUG, " INTEGRITYDIGEST=%s", idigest.c_str());
+ }
+#endif
+ if( (*i).id && !strcmp((*i).id,"FILENAME") ) {
+ assert( (*i).type && !strcmp((*i).type,"application/x-tar") );
+#ifdef III_SAVE_TARS
+ std::string tarfile = indir.get_file(filename);
+ {
+ std::ofstream(tarfile.c_str(),std::ios::out|std::ios::binary).write((*i).ptr,(*i).size);
+ }
+#endif
+ tarchive_t a((*i).ptr,(*i).size);
+ if(!a.read_next_header())
+ throw std::runtime_error("failed to tarchive_t::read_next_header())");
+ std::string jf = indir.get_file(a.entry_pathname());
+ std::string::size_type ls = jf.rfind('/');
+ std::string jbn = (ls==std::string::npos)?jf:jf.substr(ls+1);
+ int fd=open(jf.c_str(),O_CREAT|O_WRONLY,0666);
+ assert(fd>0);
+ a.read_data_into_fd(fd);
+ close(fd);
+ std::string tf = td+'/'+jbn;
+ bool success = false;
+ if(!link(jf.c_str(), tf.c_str())) {
+ unlink(jf.c_str()); success = true;
+ }else{
+ for(int i=1;i<32767;++i) {
+ tf = (const char*)gnu::autosprintf( "%s/(%05d)%s",
+ td.c_str(), i, jbn.c_str() );
+ if(!link(jf.c_str(), tf.c_str())) {
+ unlink(jf.c_str()); success = true;
+ break;
+ }
+ }
+ }
+ std::string cmd = eyekinfig.get_on_upload_photo();
+ if(success && !cmd.empty()) {
+ if(detached_child()) {
+ putenv( gnu::autosprintf("EYEFI_MACADDRESS=%s",macaddress.c_str()) );
+ putenv( gnu::autosprintf("EYEFI_UPLOADED=%s",tf.c_str()) );
+ char *argv[] = { (char*)"/bin/sh", (char*)"-c", (char*)cmd.c_str(), 0 };
+ execv("/bin/sh",argv);
+ syslog(LOG_ERR,"Failed to execute '%s'",cmd.c_str());
+ _exit(-1);
+ }
+ }
+ }
+ }
+ r.success = true;
+ return SOAP_OK;
+}
26 src/eyefiworker.cc
@@ -0,0 +1,26 @@
+#include <sys/wait.h>
+#include <stdexcept>
+#include "eyefiworker.h"
+
+eyefiworker::eyefiworker()
+ : eyefiService(SOAP_IO_STORE|SOAP_IO_KEEPALIVE) {
+ bind_flags = SO_REUSEADDR; max_keep_alive = 0;
+ }
+
+int eyefiworker::run(int port) {
+ if(!soap_valid_socket(bind(0,port,5)))
+ throw std::runtime_error("failed to bind()");
+ while(true) {
+ while(waitpid(-1,0,WNOHANG)>0);
+ if(!soap_valid_socket(accept()))
+ throw std::runtime_error("failed to accept()");
+ pid_t p = fork();
+ if(p<0) throw std::runtime_error("failed to fork()");
+ if(!p) {
+ (void)serve();
+ soap_destroy(this); soap_end(this); soap_done(this);
+ _exit(0);
+ }
+ close(socket); socket = SOAP_INVALID_SOCKET;
+ }
+}
15 src/eyefiworker.h
@@ -0,0 +1,15 @@
+#ifndef __EYEFIWORKER_H
+#define __EYEFIWORKER_H
+
+#include "soapeyefiService.h"
+
+class eyefiworker : public eyefiService {
+ public:
+
+ eyefiworker();
+
+ int run(int port);
+
+};
+
+#endif /* __EYEFIWORKER_H */
67 src/eyekinfig.cc
@@ -0,0 +1,67 @@
+#include <cassert>
+#include <stdexcept>
+#include <autosprintf.h>
+#include "eyekinfig.h"
+
+#include "config.h"
+
+eyekinfig_t::eyekinfig_t(const std::string& ma)
+ : macaddress(ma) {
+ static cfg_opt_t opts[] = {
+ CFG_STR((char*)"targetdir",(char*)"/var/lib/" PACKAGE "/%s",CFGF_NONE),
+ CFG_STR((char*)"uploadkey",(char*)"",CFGF_NONE),
+ CFG_STR((char*)"on-start-session",(char*)"",CFGF_NONE),
+ CFG_STR((char*)"on-upload-photo",(char*)"",CFGF_NONE),
+ CFG_STR((char*)"on-mark-last-photo-in-roll",(char*)"",CFGF_NONE),
+ CFG_INT((char*)"umask",022,CFGF_NONE),
+ CFG_END()
+ };
+ cfg = cfg_init(opts,CFGF_NONE);
+ if(!cfg)
+ throw std::runtime_error("failed to cfg_init()");
+ std::string::size_type ls = macaddress.rfind('/');
+ if(cfg_parse(cfg,gnu::autosprintf(
+ EYEKIN_CONF_DIR "/%s.conf",
+ (ls==std::string::npos)
+ ? macaddress.c_str()
+ : macaddress.substr(ls+1).c_str()
+ )) ==CFG_PARSE_ERROR) {
+ if(cfg) cfg_free(cfg);
+ cfg=0;
+ throw std::runtime_error("failed to cfg_parse()");
+ }
+ }
+
+eyekinfig_t::~eyekinfig_t() {
+ if(cfg) cfg_free(cfg);
+}
+
+std::string eyekinfig_t::get_targetdir() {
+ assert(cfg);
+ return gnu::autosprintf(cfg_getstr(cfg,"targetdir"),macaddress.c_str());
+}
+
+std::string eyekinfig_t::get_upload_key() {
+ assert(cfg);
+ return cfg_getstr(cfg,"uploadkey");
+}
+
+std::string eyekinfig_t::get_on_start_session() {
+ assert(cfg);
+ return cfg_getstr(cfg,"on-start-session");
+}
+std::string eyekinfig_t::get_on_upload_photo() {
+ assert(cfg);
+ return cfg_getstr(cfg,"on-upload-photo");
+}
+
+std::string eyekinfig_t::get_on_mark_last_photo_in_roll() {
+ assert(cfg);
+ return cfg_getstr(cfg,"on-mark-last-photo-in-roll");
+}
+
+
+int eyekinfig_t::get_umask() {
+ assert(cfg);
+ return cfg_getint(cfg,"umask");
+}
25 src/eyekinfig.h
@@ -0,0 +1,25 @@
+#ifndef __EYEKINFIG_H
+#define __EYEKINFIG_H
+
+#include <confuse.h>
+#include <string>
+
+class eyekinfig_t {
+ public:
+ std::string macaddress;
+ cfg_t *cfg;
+
+ eyekinfig_t(const std::string& ma);
+ ~eyekinfig_t();
+
+ std::string get_targetdir();
+ std::string get_upload_key();
+
+ std::string get_on_start_session();
+ std::string get_on_upload_photo();
+ std::string get_on_mark_last_photo_in_roll();
+
+ int get_umask();
+};
+
+#endif /* __EYEKINFIG_H */
103 src/eyetil.cc
@@ -0,0 +1,103 @@
+#include <stdlib.h>
+#include <syslog.h>
+#include <iostream>
+#include <cassert>
+#include <stdexcept>
+#include <openssl/md5.h>
+#include "eyetil.h"
+
+binary_t& binary_t::from_hex(const std::string& h) {
+ /* TODO: algorithmize */
+ std::string::size_type hs = h.length();
+ if(hs&1)
+ throw std::runtime_error("odd number of characters in hexadecimal number");
+ int rvs = hs>>1;
+ resize(rvs);
+ const unsigned char *hp = (const unsigned char*)h.data();
+ iterator oi=begin();
+ char t[3] = { 0,0,0 };
+ for(int i=0;i<rvs;++i) {
+ t[0]=*(hp++); t[1]=*(hp++);
+ *(oi++) = strtol(t,0,16);
+ }
+ return *this;
+}
+
+binary_t& binary_t::from_data(const void *d,size_t s) {
+ resize(s);
+ std::copy((const unsigned char*)d,(const unsigned char *)d+s,
+ begin() );
+ return *this;
+}
+
+std::string binary_t::hex() const {
+ std::string rv;
+ rv.reserve((size()<<1)+1);
+ char t[3] = {0,0,0};
+ for(const_iterator i=begin(),ie=end();i!=ie;++i) {
+ int rc = snprintf(t,sizeof(t),"%02x",*i);
+ assert(rc<sizeof(t));
+ rv += t;
+ }
+ return rv;
+}
+
+binary_t binary_t::md5() const {
+ binary_t rv(MD5_DIGEST_LENGTH);
+ if(!MD5(
+ (const unsigned char*)&(front()),size(),
+ (unsigned char*)&(rv.front()) ))
+ throw std::runtime_error("failed to md5()");
+ return rv;
+}
+
+tmpdir_t::tmpdir_t(const std::string& dt) : dir(dt) {
+ if(!mkdtemp((char*)dir.data()))
+ throw std::runtime_error("failed to mkdtmp()");
+}
+tmpdir_t::~tmpdir_t() {
+ assert(!dir.empty());
+ if(rmdir(dir.c_str())) {
+ syslog(LOG_WARNING,"Failed to remove '%s' directory",dir.c_str());
+ }
+}
+
+std::string tmpdir_t::get_file(const std::string& f) {
+ std::string::size_type ls = f.rfind('/');
+ return dir+'/'+(
+ (ls==std::string::npos)
+ ? f
+ : f.substr(ls+1)
+ );
+}
+
+tarchive_t::tarchive_t(void *p,size_t s) : a(archive_read_new()), e(0) {
+ if(!a) throw std::runtime_error("failed to archive_read_new()");
+ if(archive_read_support_format_tar(a)) {
+ archive_read_finish(a);
+ throw std::runtime_error("failed to archive_read_support_format_tar()");
+ }
+ if(archive_read_open_memory(a,p,s)) {
+ archive_read_finish(a);
+ throw std::runtime_error("failed to archive_read_open_memory()");
+ }
+}
+tarchive_t::~tarchive_t() {
+ assert(a);
+ archive_read_finish(a);
+}
+
+bool tarchive_t::read_next_header() {
+ assert(a);
+ return archive_read_next_header(a,&e)==ARCHIVE_OK;
+}
+
+std::string tarchive_t::entry_pathname() {
+ assert(a); assert(e);
+ return archive_entry_pathname(e);
+}
+
+bool tarchive_t::read_data_into_fd(int fd) {
+ assert(a);
+ return archive_read_data_into_fd(a,fd)==ARCHIVE_OK;
+}
48 src/eyetil.h
@@ -0,0 +1,48 @@
+#ifndef __EYETIL_H
+#define __EYETIL_H
+
+#include <vector>
+#include <string>
+#include <archive.h>
+#include <archive_entry.h>
+
+class binary_t : public std::vector<unsigned char> {
+ public:
+ binary_t() { }
+ binary_t(size_type n) : std::vector<unsigned char>(n) { }
+ binary_t(const std::string& h) { from_hex(h); }
+ binary_t(const void *d,size_t s) { from_data(d,s); }
+
+ binary_t& from_hex(const std::string& h);
+ binary_t& from_data(const void *d,size_t s);
+
+ std::string hex() const;
+ binary_t md5() const;
+};
+
+class tmpdir_t {
+ public:
+ std::string dir;
+
+ tmpdir_t(const std::string& dt);
+ ~tmpdir_t();
+
+ std::string get_file(const std::string& f);
+};
+
+class tarchive_t {
+ public:
+ struct archive *a;
+ struct archive_entry *e;
+
+ tarchive_t(void *p,size_t s);
+ ~tarchive_t();
+
+ bool read_next_header();
+
+ std::string entry_pathname();
+
+ bool read_data_into_fd(int fd);
+};
+
+#endif /* __EYETIL_H */
86 src/iiid.cc
@@ -0,0 +1,86 @@
+#include <syslog.h>
+#include <getopt.h>
+#include <iostream>
+#include <cassert>
+#include <stdexcept>
+#include "eyetil.h"
+#include "eyefiworker.h"
+
+#include "config.h"
+
+#include "eyefi.nsmap"
+
+#define PHEADER \
+ PACKAGE " Version " VERSION "\n" \
+ "Copyright (c) 2009 Klever Group"
+
+int main(int argc,char **argv) try {
+
+ int port = 59278;
+
+ while(true) {
+ static struct option opts[] = {
+ { "help", no_argument, 0, 'h' },
+ { "usage", no_argument, 0, 'h' },
+ { "version", no_argument, 0, 'V' },
+ { "license", no_argument, 0, 'L' },
+ { "port", required_argument, 0, 'p' },
+ { NULL, 0, 0, 0 }
+ };
+ int c = getopt_long(argc,argv,"hVLp:",opts,NULL);
+ if(c==-1) break;
+ switch(c) {
+ case 'h':
+ std::cerr << PHEADER << std::endl << std::endl
+ << " " << argv[0] << " [options]" << std::endl
+ << std::endl <<
+ " -h, --help,\n"
+ " --usage display this text\n"
+ " -V, --version display version information\n"
+ " -L, --license show license\n"
+ " -p <port>, --port=<port> port to listen to\n"
+ " (you're not likely to ever need it)\n"
+ << std::endl << std::endl;
+ exit(0);
+ break;
+ case 'V':
+ std::cerr << VERSION << std::endl;
+ exit(0);
+ break;
+ case 'L':
+ extern const char *COPYING;
+ std::cerr << COPYING << std::endl;
+ exit(0);
+ break;
+ case 'p':
+ port = strtol(optarg,0,0);
+ if(errno) {
+ std::cerr << "Failed to parse port number" << std::endl;
+ exit(1);
+ }
+ break;
+ default:
+ std::cerr << "Huh?" << std::endl;
+ exit(1);
+ break;
+ }
+ }
+
+ const char *ident = rindex(*argv,'/');
+ if(ident)
+ ++ident;
+ else
+ ident = *argv;
+ openlog(ident,LOG_PERROR|LOG_PID,LOG_DAEMON);
+ syslog(LOG_INFO,"Starting iii eye-fi manager");
+
+ eyefiworker().run(port);
+
+ closelog();
+ return 0;
+} catch(std::exception& e) {
+ syslog(LOG_CRIT,"Exiting iii daemon, because of error condition");
+ syslog(LOG_CRIT,"Exception: %s",e.what());
+ return 1;
+}
+

0 comments on commit aadaa8b

Please sign in to comment.