summaryrefslogtreecommitdiffabout
path: root/src/eyefiservice.cc
blob: 153a7c489ae843401fd191c4253ea0fb2f990fb0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#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=transfermode;
    r.transfermodetimestamp=transfermodetimestamp;
    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();
    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;
}