Initial revision
[monky] / mldonkey.c
1 #include <string.h>
2 #include <unistd.h>
3 #include <errno.h>
4 #include <netdb.h>
5 #include <fcntl.h>
6 #include <arpa/inet.h>
7 #include "conky.h"
8
9 int64 buf_to_int(char *buf, int pos, int size);
10 void int_to_buf(int64 i, char *buf, int pos, int size);
11
12 #define BUF16_TO_INT(buf, pos) buf_to_int((buf), (pos), 2)
13 #define BUF32_TO_INT(buf, pos) buf_to_int((buf), (pos), 4)
14 #define BUF64_TO_INT(buf, pos) buf_to_int((buf), (pos), 8)
15
16 #define INT_TO_BUF16(i, buf, pos) int_to_buf((i), (buf), (pos), 2)
17 #define INT_TO_BUF32(i, buf, pos) int_to_buf((i), (buf), (pos), 4)
18 #define INT_TO_BUF64(i, buf, pos) int_to_buf((i), (buf), (pos), 8)
19
20 mldonkey_info mlinfo;
21 mldonkey_config mlconfig;
22
23 /* Call this function to update the information about mldonkey.
24  * Note that the function will not reconnect to mldonkey if the
25  * pointer to the mldonkey_config has not changed. As it uses static
26  * data, it cannot be used in a multithreaded env.
27  * Returns 1 if connected and info filled, 0 if connected but not filled,
28  * -1 otherwise. */
29
30 enum to_gui {
31   CoreProtocol, /* 0 */
32   Options_info,
33   RESERVED2,
34   DefineSearches,
35   Result_info,
36   Search_result,
37   Search_waiting,
38   File_info,
39   File_downloaded,
40   File_availability,
41   File_source, /* 10 */
42   Server_busy,
43   Server_user,
44   Server_state,
45   Server_info,
46   Client_info,
47   Client_state,
48   Client_friend,
49   Client_file,
50   Console,
51   Network_info, /* 20 */
52   User_info,
53   Room_info,
54   Room_message,
55   Room_add_user,
56   Client_stats,
57   Server_info_v2,
58   MessageFromClient,
59   ConnectedServers,
60   DownloadFiles,
61   DownloadedFiles, /* 30 */
62   Room_info_v2,
63   Room_remove_user,
64   Shared_file_info,
65   Shared_file_upload,
66   Shared_file_unshared,
67   Add_section_option,
68   Client_stats_v2,
69   Add_plugin_option,
70   Client_stats_v3,
71   File_info_v2,      /* 40 */
72   DownloadFiles_v2,
73   DownloadedFiles_v2,
74   File_info_v3,
75   DownloadFiles_v3,
76   DownloadedFiles_v3,
77   File_downloaded_v2,
78   BadPassword,
79   Shared_file_info_v2,
80   Client_stats_v4, /* 49 */
81 };
82
83 #define MLDONKEY_DISCONNECTED   0
84 #define MLDONKEY_CONNECTING     1
85 #define MLDONKEY_AUTHENTICATING 2
86 #define MLDONKEY_CONNECTED      3
87
88 #define MAX_MESSAGE_LEN 65000
89 static int write_pos = 0;
90 static char write_buf[MAX_MESSAGE_LEN];
91 static char read_buf[MAX_MESSAGE_LEN];
92 static int read_pos;
93 static int mldonkey_sock = -1;
94 static int mldonkey_state = MLDONKEY_DISCONNECTED;
95 static mldonkey_config *old_config = NULL;
96
97 /* int64 ------------------------------ */
98
99 int64 buf_to_int(char *buf, int pos, int size)
100 {
101     int i;
102     int64 res = 0;
103
104     for(i = 0; i < size; i++){
105         res += (buf[pos + i] & 0xFF) << (8 * i);
106     }
107     return res;
108 }
109
110 void int_to_buf(int64 i, char *buf, int pos, int size)
111 {
112     int j;
113
114     for(j = 0; j < size; j++){
115         buf[pos + j] = (i & (-1)) >> (8 * j);
116     }
117 }
118
119 /* Write operations --------------------- */
120
121 void init_message()
122 { write_pos = 0; }
123
124 void write_int8(int code)
125 {
126   write_buf[write_pos++] = code;
127 }
128
129 void write_opcode(int code)
130 {
131   write_buf[write_pos++] = code;
132 }
133
134 void write_int16(int code)
135 {
136   INT_TO_BUF16(code, write_buf, write_pos);
137   write_pos += 2;
138 }
139
140 void write_int32(int code)
141 {
142   INT_TO_BUF32(code, write_buf, write_pos);
143   write_pos += 4;
144 }
145
146 void write_int64(int64 code)
147 {
148   INT_TO_BUF64(code, write_buf, write_pos);
149   write_pos += 8;
150 }
151
152 void write_string(char *str)
153 {
154   if(str == NULL){
155     write_int16(0);
156   } else {
157     int len = strlen(str);
158     write_int16(len);
159     memcpy((void *) (write_buf + write_pos), (void *) str, (size_t) len);
160     write_pos += len;
161   }
162 }
163
164
165 int write_message(char *mtype)
166 {
167   char header[4];
168
169   INT_TO_BUF32(write_pos, header, 0);
170   if(4 != write(mldonkey_sock, header, 4) ||
171       write_pos != write(mldonkey_sock,(void *)write_buf,(size_t)write_pos)){
172     ERR("Error in transmitting %s\n",mtype);
173     write_pos = 0;
174
175     /* Immediatly close the connection */
176     close(mldonkey_sock);
177     mldonkey_state = MLDONKEY_DISCONNECTED;
178     mldonkey_sock = -1;
179     return -1;
180   } else {
181     write_pos = 0;
182     return 0;
183   }
184 }
185
186
187 /* Read operations ----------------------------*/
188
189 int read_int8()
190 {
191   return read_buf[read_pos++];
192 }
193
194 int read_int16()
195 {
196   int i = BUF16_TO_INT(read_buf, read_pos);
197   read_pos += 2;
198   return i;
199 }
200
201 int read_int32()
202 {
203   int i = BUF32_TO_INT(read_buf, read_pos);
204   read_pos += 4;
205   return i;
206 }
207
208 int64 read_int64()
209 {
210   int64 i = BUF64_TO_INT(read_buf, read_pos);
211   read_pos += 8;
212   return i;
213 }
214
215 char* read_string()
216 {
217   char *buf;
218   int len;
219
220   len = BUF16_TO_INT(read_buf, read_pos);
221   read_pos += 2;
222
223   buf = (char *) malloc((size_t) len+1);
224   memmove(read_buf + read_pos, buf, len);
225   buf[len] = 0;
226   read_pos += len;
227
228   return buf;
229 }
230
231 /* protocol impl. ----------------------------- */
232
233 void close_sock();
234
235 /* This function returns the number of messages read, 0 if it blocks,
236 -1 on error. */
237 int cut_messages(int reinit)
238 {
239   int nread;
240   static int toread = 0;
241   static int pos = 0;
242
243   if(reinit){
244     toread = 0;
245     pos = 0;
246     read_pos = 0;
247     return 0;
248   }
249
250   while(1){
251     if(toread == 0){
252       nread = read(mldonkey_sock, read_buf+pos,4-pos);
253       if(nread <= 0){
254         if(errno == EAGAIN) {
255           return 0;
256         } else {
257           close_sock();
258           pos = 0; toread = 0; return -1; }
259       }
260       pos += nread;
261       if(pos == 4){
262         toread = BUF32_TO_INT(read_buf,0);
263         pos = 0;
264       }
265     } else {
266       nread = read(mldonkey_sock, read_buf+pos, toread - pos);
267       if(nread <= 0){
268         if(errno == EAGAIN) return 0; else {
269           pos = 0; toread = 0;
270           close_sock();
271           return -1; }
272       }
273       pos += nread;
274       if(pos == toread){
275         /* We have one message !!! */
276         int old_pos = pos;
277         read_pos = 0;
278         pos = 0;
279         toread = 0;
280
281         return old_pos;
282       }
283     }
284   }
285 }
286
287 void close_sock()
288 {
289   old_config = NULL;
290   if(mldonkey_sock >= 0) close(mldonkey_sock);
291   mldonkey_sock = -1;
292   mldonkey_state = MLDONKEY_DISCONNECTED;
293   cut_messages(1);
294 }
295
296 int mldonkey_connect(mldonkey_config *config)
297 {       
298   if(config != old_config){
299     struct sockaddr_in sa;
300     int retcode;
301     close_sock();
302
303
304     old_config=config;
305     /* resolve hostname */
306     memset(&sa, 0, sizeof(sa));
307
308     if(config->mldonkey_hostname == NULL)
309       config->mldonkey_hostname = "127.0.0.1";
310     if(config->mldonkey_hostname[0] >= '0' &&
311         config->mldonkey_hostname[0] <= '9'){
312       #ifdef HAS_INET_ATON
313       if (inet_aton(config->mldonkey_hostname, &sa.sin_addr) == 0) return -1;
314       #else
315
316       sa.sin_addr.s_addr = inet_addr(config->mldonkey_hostname);
317       if (sa.sin_addr.s_addr == (unsigned int) -1) return -1;
318       #endif
319
320     } else {
321       struct hostent * hp;
322       hp = gethostbyname(config->mldonkey_hostname);
323       if (hp == (struct hostent *) NULL) return -1;
324       sa.sin_addr.s_addr = (unsigned long)hp->h_addr_list[0];
325     }
326
327     sa.sin_port = htons(config->mldonkey_port);
328     sa.sin_family = AF_INET;
329
330     if ((mldonkey_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
331       ERR("Opening socket");
332       close_sock();
333       return -1;
334     }
335
336     if( connect(mldonkey_sock, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
337       if (errno != EAGAIN &&
338           errno != EINTR &&
339           errno != EINPROGRESS &&
340           errno != EWOULDBLOCK) {
341 //        ERR("Connection failed");
342         close_sock();
343         return -1;
344       }
345     }
346
347     retcode = fcntl(mldonkey_sock, F_GETFL, 0);
348     if (retcode == -1 ||
349         fcntl(mldonkey_sock, F_SETFL, retcode | O_NONBLOCK) == -1){
350       return -1;
351     }
352
353
354     mldonkey_state = MLDONKEY_CONNECTING;
355     return 0;
356   }
357
358   return 0;
359 }
360
361 int mldonkey_can_read()
362 {
363   return cut_messages(0);
364 }
365
366 int mldonkey_info_message(mldonkey_info *info)
367 {
368   int opcode = read_int16();
369
370   switch(opcode){
371
372     case CoreProtocol :
373     init_message();
374
375     write_int16(0); /* GUI protocol */
376     write_int32(10); /* Version 10 ! */
377     write_message("GuiProtocol");
378
379     write_int16(47); /* GUI protocol */
380
381     write_int16(1);
382     write_int32(1);
383     write_int8(1);
384     write_message("GuiExtensions");
385
386     init_message();
387     write_int16(5); /* Password */
388     write_string(old_config->mldonkey_password);
389     write_message("Password");
390
391     break;
392
393     case BadPassword :
394     ERR("Bad Password\n");
395     close_sock();
396     break;
397
398     case Client_stats:
399     case Client_stats_v2:
400     case Client_stats_v3:
401     ERR("Client stats format too old...\n");
402     break;
403
404     case Client_stats_v4:
405     mldonkey_state = MLDONKEY_CONNECTED;
406
407     info->upload_counter = read_int64();
408     info->download_counter = read_int64();
409     info->shared_counter = read_int64();
410     info->nshared_files = read_int32();
411     info->tcp_upload_rate = read_int32();
412     info->tcp_download_rate = read_int32();
413     info->udp_upload_rate = read_int32();
414     info->udp_download_rate = read_int32();
415     info->ndownloading_files = read_int32();
416     info->ndownloaded_files = read_int32();
417
418     break;
419   }
420
421   return 0;
422 }
423
424 int get_mldonkey_status(mldonkey_config *config, mldonkey_info *info)
425 {
426   if( mldonkey_connect(config) >= 0){
427     while(mldonkey_can_read() > 0){
428       mldonkey_info_message(info);
429     }
430   }
431   return mldonkey_state;
432 }