-rw-r--r-- | src/.gitignore | 10 | ||||
-rw-r--r-- | src/Makefile.am | 32 | ||||
-rw-r--r-- | src/eyefi.h | 47 | ||||
-rw-r--r-- | src/eyefiservice.cc | 186 | ||||
-rw-r--r-- | src/eyefiworker.cc | 26 | ||||
-rw-r--r-- | src/eyefiworker.h | 15 | ||||
-rw-r--r-- | src/eyekinfig.cc | 67 | ||||
-rw-r--r-- | src/eyekinfig.h | 25 | ||||
-rw-r--r-- | src/eyetil.cc | 103 | ||||
-rw-r--r-- | src/eyetil.h | 48 | ||||
-rw-r--r-- | src/iiid.cc | 86 |
11 files changed, 645 insertions, 0 deletions
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 | |||