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