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