Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
112 andreas 1
/*
2
 * Copyright (C) 2022 by Andreas Theofilu <andreas@theosys.at>
3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, write to the Free Software Foundation,
16
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
17
 */
18
 
19
#include <fstream>
20
 
21
#include <sys/socket.h>
22
#include <netinet/in.h>
23
#include <string.h>
24
#include <stdio.h>
25
#include <stdlib.h>
26
#include <sys/stat.h>
27
#include <sys/sendfile.h>
28
#include <fcntl.h>
29
#include <unistd.h>
30
 
31
#include "tfsfreader.h"
32
#include "tsocket.h"
33
#include "tconfig.h"
34
#include "terror.h"
35
#include "tresources.h"
113 andreas 36
#include "readtp4.h"
112 andreas 37
 
38
#define FTP_PORT    21
39
 
40
#define FTP_CMD_USER    0
41
#define FTP_CMD_PASS    1
42
#define FTP_CMD_PASV    2
43
#define FTP_CMD_TYPE    3
44
#define FTP_CMD_RETR    4
45
#define FTP_CMD_QUIT    5
46
 
47
using std::string;
48
using std::vector;
49
 
50
TFsfReader::TFsfReader()
51
{
52
    DECL_TRACER("TFsfReader::TFsfReader()");
53
 
54
    mFtpCmds = {
55
        { "USER", 331 },    // 0
56
        { "PASS", 230 },    // 1
57
        { "PASV", 227 },    // 2
58
        { "TYPE", 200 },    // 3
59
        { "RETR", 150 },    // 4
60
        { "QUIT", 221 }     // 5
61
    };
62
}
63
 
64
TFsfReader::~TFsfReader()
65
{
66
    DECL_TRACER("TFsfReader::~TFsfReader()");
67
 
68
    if (mFtp)
69
        delete mFtp;
70
 
71
    if (mFtpData)
72
        delete mFtpData;
73
}
74
 
75
bool TFsfReader::sendCommand(int cmd, const string& param)
76
{
77
    DECL_TRACER("TFsfReader::sendCommand(int cmd, const string& param)");
78
 
79
    if (cmd < 0 || cmd > (int)mFtpCmds.size() || !mFtp)
80
        return false;
81
 
82
    string buf;
83
 
84
    if (!param.empty())
85
        buf = mFtpCmds[cmd].cmd + "\r\n";
86
    else
87
        buf = mFtpCmds[cmd].cmd + " " + param + "\r\n";
88
 
113 andreas 89
    if (mFtp->send((char *)buf.c_str(), buf.length()) != TSocket::npos)
112 andreas 90
        return false;
91
 
92
    char buffer[128];
93
    size_t size;
94
    memset(buffer, 0, sizeof(buffer));
95
 
96
    if ((size = mFtp->receive(buffer, sizeof(buffer))) == TSocket::npos)
97
        return false;
98
 
99
    mLastCmdBuffer = buffer;
100
    int code = 0;
101
 
102
    if (mFtpCmds[cmd].success && !isdigit(buffer[0]))
103
    {
104
        MSG_ERROR("Unexpected answer: " << buffer);
105
        return false;
106
    }
107
    else if (mFtpCmds[cmd].success)
108
        code = atoi(buffer);
109
 
110
    if (code != mFtpCmds[cmd].success)
111
    {
112
        MSG_ERROR("FTP error: " << buffer);
113
        return false;
114
    }
115
 
116
    MSG_DEBUG("FTP command " << mFtpCmds[cmd].cmd << " returned: " << buffer);
117
    return true;
118
}
119
 
120
bool TFsfReader::copyOverFTP(const string& fname, const string& target)
121
{
122
    DECL_TRACER("TFsfReader::copyOverFTP(const string& fname, const string& target, const string& user, const string& pw)");
123
 
124
    if (mFtp)
125
        delete mFtp;
126
 
127
    mFtp = new TSocket(TConfig::getController(), FTP_PORT);
128
 
129
    string buf, filename;
130
    char *f, buffer[128];
131
    size_t size;
132
 
133
    if (!mFtp->connect())
134
        return false;
135
 
136
    // Server should answer with: 220 <message>
137
    memset(buffer, 0, sizeof(buffer));
138
 
139
    if ((size = mFtp->receive(buffer, sizeof(buffer))) == TSocket::npos)
140
    {
141
        mFtp->close();
142
        return false;
143
    }
144
 
145
    if (!startsWith(buffer, "220 "))
146
    {
147
        MSG_ERROR("Unexpected answer: " << buffer);
148
        mFtp->close();
149
        return false;
150
    }
151
 
152
    // We send the authentication
153
    if (!sendCommand(FTP_CMD_USER, TConfig::getFtpUser()))
154
    {
155
        mFtp->close();
156
        return false;
157
    }
158
 
159
    // Here we send the password
160
    if (!sendCommand(FTP_CMD_PASS, TConfig::getFtpPassword()))
161
    {
162
        mFtp->close();
163
        return false;
164
    }
165
 
166
    // Switching to passive mode
167
    if (!sendCommand(FTP_CMD_PASV, ""))
168
    {
169
        mFtp->close();
170
        return false;
171
    }
172
 
173
    size_t pos = mLastCmdBuffer.find_last_of("(");
174
 
175
    if (pos != string::npos)
176
    {
177
        size_t end = mLastCmdBuffer.find_last_of(")");
178
 
179
        if (end != string::npos)
180
        {
181
            string info = mLastCmdBuffer.substr(pos + 1, end-pos-2);
182
            MSG_DEBUG("PASV " << info);
183
            vector<string> parts = StrSplit(info, ",");
184
 
185
            if (parts.size() < 6)
186
            {
187
                MSG_ERROR("PASV command failed!");
188
            }
189
            else
190
            {
191
                mDataPort = atoi(parts[4].c_str()) * 256 + atoi(parts[5].c_str());
192
                mPassive = true;
193
            }
194
        }
195
    }
196
    else
197
    {
198
        MSG_ERROR("Unexpected response from FTP server!");
199
        mFtp->close();
200
        return false;
201
    }
202
 
203
    // We connect to the data port
204
    if (mFtpData)
205
        delete mFtpData;
206
 
207
    mFtpData = new TSocket(TConfig::getController(), mDataPort);
208
 
209
    if (!mFtpData->connect())
210
    {
211
        mFtp->close();
212
        return false;
213
    }
214
 
215
    // Switch to image mode
216
    if (!sendCommand(FTP_CMD_TYPE, "I"))
217
    {
218
        mFtp->close();
219
        return false;
220
    }
221
 
222
    // GET a file
223
    if (!sendCommand(FTP_CMD_RETR, fname))
224
    {
225
        mFtp->close();
226
        return false;
227
    }
228
 
229
    size = 0;
230
    pos = mLastCmdBuffer.find_last_of("(");
231
 
232
    if (pos != string::npos)
233
    {
234
        string info = mLastCmdBuffer.substr(pos + 1);
235
        MSG_DEBUG("RETR size: " << info);
236
        size = atoi(info.c_str());
237
    }
238
    else
239
    {
240
        MSG_ERROR("Unexpected response: " << mLastCmdBuffer);
241
        mFtp->close();
242
        return false;
243
    }
244
 
245
    // FIXME: Add code to receive file
246
    f = new char[size+1];
247
 
248
    if (mFtpData->readAbsolut(f, size) == TSocket::npos)
249
    {
250
        MSG_ERROR("Error receiving file " << fname << " from FTP server!");
251
        mFtpData->close();
252
        mFtp->close();
253
        delete[] f;
254
        return false;
255
    }
256
 
257
    // Write the file into place
258
    try
259
    {
260
        std::ofstream file;
261
 
262
        file.open(target, std::ios_base::binary);
263
        file.write(f, size);
264
        file.close();
265
    }
266
    catch (std::exception& e)
267
    {
268
        MSG_ERROR("Error on file " << target << ": " << e.what());
269
        mFtpData->close();
270
        mFtp->close();
271
        delete[] f;
272
        return false;
273
    }
274
 
275
    // QUIT from FTP-server
276
    if (!sendCommand(FTP_CMD_QUIT, ""))
277
    {
278
        mFtp->close();
279
        mFtpData->close();
280
        return false;
281
    }
282
 
283
    mFtp->close();
284
    mFtpData->close();
285
    return true;
286
}
113 andreas 287
 
288
bool TFsfReader::unpack(const string& fname, const string& path)
289
{
290
    DECL_TRACER("TFsfReader::unpack(const string& fname, const string& path)");
291
 
292
    if (fname.empty() || path.empty())
293
    {
294
        MSG_ERROR("Invalid parameters!");
295
        return false;
296
    }
297
 
298
    reader::ReadTP4 readtp4(fname, path);
299
    return readtp4.doRead();
300
}