This repository has been archived by the owner on Feb 1, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathftpclient.cpp
315 lines (271 loc) · 8.44 KB
/
ftpclient.cpp
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
#include <iostream>
#include <string>
#include <regex>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define EMPTY_STRING ""
#define BUFSIZE 1024
#define FTP_PORT 21
#define USER 1
#define PASS 2
#define HOST 3
#define PORT 4
#define PATH 5
#define CHECK_REPLY \
if (reply.empty()) { \
close(socketDescConnect); \
return 1; \
}
using namespace std;
smatch match(string pattern, string source);
string sendMessage(string message, int socket_desc, string code);
int isResponse(int socketDesc);
int connectToFtp(const char *argv, int port);
int stringToInt(string number);
int main(int argc, char *argv[])
{
int socketDescConnect;
int socketDescTransfer;
smatch addressParams;
string address[6];
string message;
string reply;
// check if is set parameter with ftp address
if (argc != 2) {
cerr << "Bad params" << endl;
return 1;
}
// regex to split address from argument
string regex = "(?:ftp:\\/\\/)?(?:ftp:\\/\\/(\\w+)(?:\\:)(\\w+)"
"(?:@)?)?((?:\\.|\\w+)+)"
"(?:(?:\\:)?(\\d+)?)?((?:\\/\\S+)+\\/?$)?";
// matching address
// TODO check if parameter is correct
addressParams = match(regex, argv[1]);
if (addressParams.empty()) {
cerr << "wrong format of FTP address entered" << endl;
return 1;
}
// load address parameters to array
// TODO edit
for (unsigned int i = 0; i < addressParams.size(); ++i) {
address[i] = addressParams[i].str();
}
// connect to server
if (address[PORT] == EMPTY_STRING) {
socketDescConnect = connectToFtp(address[HOST].c_str(), FTP_PORT);
} else {
socketDescConnect = connectToFtp(address[HOST].c_str(),
stringToInt(address[PORT]));
}
if (socketDescConnect == -1) {
return 1;
}
// send empty string to check if server is FTP
reply = sendMessage("", socketDescConnect, "220");
CHECK_REPLY;
// send username
(address[USER] == EMPTY_STRING) ?
message = "USER anonymous\r\n" :
message = "USER " + address[USER] + "\r\n";
reply = sendMessage(message.c_str(), socketDescConnect, "331");
CHECK_REPLY;
// send password
(address[PASS] == EMPTY_STRING) ?
message = "PASS secret\r\n" :
message = "PASS " + address[PASS] + "\r\n";
reply = sendMessage(message.c_str(), socketDescConnect, "230");
CHECK_REPLY;
// send directory / file listing as plain ASCII
message = "TYPE A\r\n";
reply = sendMessage(message.c_str(), socketDescConnect, "200");
CHECK_REPLY;
// enter to passive mode -> expect new socket connection to transfer data
message = "PASV\r\n";
reply = sendMessage(message.c_str(), socketDescConnect, "227");
CHECK_REPLY;
// regex to split address from argument
regex = ".*\\((\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)\\).*";
addressParams = match(regex, reply);
if (addressParams.empty()) {
cerr << "Can't find IP address information in reply" << endl;
return 1;
}
// get ip and port for transfer data from FTP server
// TODO edit
address[HOST] = EMPTY_STRING;
address[HOST].append(addressParams[1]);
address[HOST].append(".");
address[HOST].append(addressParams[2]);
address[HOST].append(".");
address[HOST].append(addressParams[3]);
address[HOST].append(".");
address[HOST].append(addressParams[4]);
socketDescTransfer = connectToFtp(address[HOST].c_str(),
((stringToInt(addressParams[5]) * 256)
+ stringToInt(addressParams[6])));
if (socketDescTransfer == -1) {
return 1;
}
// print file list
(address[PATH] == EMPTY_STRING) ?
message = "LIST /\r\n" :
message = "LIST " + address[PATH] + "\r\n";
reply = sendMessage(message.c_str(), socketDescConnect, "150");
CHECK_REPLY;
// load and print files on ftp (TODO better solution)
// TODO make function receiveMessage
char serverReply[BUFSIZE] = EMPTY_STRING;
string serverReplyStr = EMPTY_STRING;
int recvStatus = 0;
while(true) {
// empty buffer
memset(serverReply, 0, sizeof(serverReply));
// check if there is data to receive
if (isResponse(socketDescTransfer) <= 0) {
cerr << "No data in socket to receive" << endl;
return 1;
}
// receive reply
if ((recvStatus = recv(socketDescTransfer, serverReply, BUFSIZE - 1, 0)) < 0) {
cerr << "Recv failed" << endl;
return 1;
}
if (recvStatus == 0) {
break;
}
serverReplyStr.append(serverReply);
}
close(socketDescTransfer);
// check if output file list is correctly loaded
reply = sendMessage("", socketDescConnect, "226");
CHECK_REPLY;
cout << serverReplyStr;
close(socketDescConnect);
return 0;
}
/**
* Function: isResponse
* --------------------
* Check if there are are some data
* at socket to accept
*
* @param socketDesc socket descriptor
* @return count of socket handles that are ready or
* 0 if time limit expires
*/
int isResponse(int socketDesc)
{
fd_set readfds;
struct timeval time;
FD_ZERO(&readfds);
FD_SET(socketDesc, &readfds);
time.tv_sec = 10;
time.tv_usec = 0;
return select((socketDesc + 1), &readfds, NULL, NULL, &time);
}
/**
* Function: connectToFtp
* ----------------------
* Create new socket, convert hostname to IP address
* and connect to FTP server
*
* @param hostname hostname of FTP server
* @param port FTP server port
* @return socketDesc socket descriptor
*/
int connectToFtp(const char *hostname, int port)
{
int socketDesc;
struct hostent *he;
struct sockaddr_in server;
string ip = EMPTY_STRING;
// create socket
if ((socketDesc = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
cerr << "Can't create socket" << endl;
return -1;
}
// convert hostname to ip
if ((he = gethostbyname(hostname)) == NULL) {
cerr << "Convert hostname to IP failed" << endl;
return -1;
}
// connect socket to a server
memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
server.sin_family = AF_INET;
server.sin_port = htons(port);
if (connect(socketDesc, (struct sockaddr *)&server, sizeof(server)) < 0) {
cerr << "Connection error" << endl;
return -1;
}
return socketDesc;
}
/**
* Function: sendMessage
* ---------------------
* Send message to FTP server and expect answer
*
* @param message message that will be sent to FTP server
* @param socket_desc descriptor of socket that is connected to FTP server
* @param code code number that is expected in server reply
* @return string string containing server reply message
*/
string sendMessage(string message, int socket_desc, string code)
{
char serverReply[BUFSIZE] = EMPTY_STRING;
string serverReplyStr = EMPTY_STRING;
// send message
if (send(socket_desc, message.c_str(), strlen(message.c_str()), 0) < 0) {
cerr << "Send failed" << endl;
return EMPTY_STRING;
}
do {
// empty buffer
memset(serverReply, 0, sizeof(serverReply));
// check if there is data to receive
if (isResponse(socket_desc) <= 0) {
cerr << "No data in socket to receive" << endl;
return EMPTY_STRING;
}
// receive reply
if (recv(socket_desc, serverReply, BUFSIZE - 1, 0) < 0) {
cerr << "Recv failed" << endl;
return EMPTY_STRING;
}
serverReplyStr.append(serverReply);
} while (serverReplyStr.find(code) == string::npos);
return serverReplyStr;
}
/**
* Function: match
* ---------------
* Match regular expression and return matched group
*
* @param pattern regular expression that'll be used to match groups
* @param source source text on which will be regex search done
* @return match matched groups
*/
smatch match(string pattern, string source)
{
smatch match;
regex_search(source, match, regex(pattern));
return match;
}
/**
* Function: stringToInt
* ---------------------
* Convert string value to integer
*
* @param stringNum number in string
* @return integer number converted to int
*/
int stringToInt(string number)
{
int integer;
istringstream convert (number);
convert >> integer;
return integer;
}