Added data timeout detector too to detect if server is down
[crypto.git] / apps / silcmap / silcmap_client.c
1 /*
2
3   silcmap_client.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2003 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 #include "silcincludes.h"
21 #include "silcclient.h"
22 #include "silcmap.h"
23
24 /******* Map Client Routines *************************************************/
25
26 SILC_TASK_CALLBACK(silc_map_process_done)
27 {
28   SilcMap map = context;
29
30   /* Program stops */
31   silc_schedule_stop(map->client->schedule);
32 }
33
34 /* This function processes the data that was gathered from the server
35    and producess the outputs and the map. */
36
37 void silc_map_process_data(SilcMap map, SilcMapConnection mapconn)
38 {
39   SilcMapCommand cmd;
40   SilcMap ret_map;
41   SilcInt16 r, g, b, lr, lg, lb;
42   int i;
43
44   map->conn_num++;
45   SILC_LOG_DEBUG(("Processing the data from server (%d/%d)",
46                   map->conn_num, map->conns_num));
47
48   /* Change colors according to server status */
49   silc_map_parse_color(mapconn->up_color, &r, &g, &b);
50   silc_map_parse_color(mapconn->up_text_color, &lr, &lg, &lb);
51   if (mapconn->down) {
52     silc_map_parse_color(mapconn->down_color, &r, &g, &b);
53     silc_map_parse_color(mapconn->down_text_color, &lr, &lg, &lb);
54   }
55
56   /* Execute the map commands */
57   silc_dlist_start(mapconn->commands);
58   while ((cmd = silc_dlist_get(mapconn->commands)) != SILC_LIST_END) {
59     if (cmd->cut) {
60       if (silc_map_cut(map, cmd->x, cmd->y, cmd->width,
61                        cmd->height, &ret_map)) {
62         silc_map_write_ppm(ret_map, cmd->filename);
63         silc_map_free(ret_map);
64       }
65       continue;
66     }
67
68     if (cmd->draw_line) {
69       if (cmd->color_set) {
70         r = cmd->r;
71         g = cmd->g;
72         b = cmd->b;
73       }
74       silc_map_draw_line(map, cmd->width, cmd->x, cmd->y, cmd->x2, cmd->y2,
75                          r, g, b);
76       continue;
77     }
78
79     if (cmd->draw_text) {
80       if (cmd->color_set) {
81         lr = cmd->r;
82         lg = cmd->g;
83         lb = cmd->b;
84       }
85       silc_map_draw_text(map, cmd->text, cmd->x, cmd->y, lr, lg, lb);
86
87       continue;
88     }
89
90     if (cmd->draw_circle) {
91       if (cmd->color_set) {
92         r = cmd->r;
93         g = cmd->g;
94         b = cmd->b;
95       }
96       if (cmd->lcolor_set) {
97         lr = cmd->lr;
98         lg = cmd->lg;
99         lb = cmd->lb;
100       }
101       silc_map_draw_circle(map, cmd->x, cmd->y, r, g, b,
102                            cmd->text, cmd->lposx, cmd->lposy, lr, lg, lb);
103       continue;
104     }
105
106     if (cmd->draw_rectangle) {
107       if (cmd->color_set) {
108         r = cmd->r;
109         g = cmd->g;
110         b = cmd->b;
111       }
112       if (cmd->lcolor_set) {
113         lr = cmd->lr;
114         lg = cmd->lg;
115         lb = cmd->lb;
116       }
117       silc_map_draw_rectangle(map, cmd->x, cmd->y, r, g, b,
118                               cmd->text, cmd->lposx, cmd->lposy, lr, lg, lb);
119       continue;
120     }
121
122   }
123
124   /* Write the html data file */
125   if (map->writehtml.writehtml)
126     silc_map_writehtml(map, mapconn);
127
128   /* Write uptime reliability data */
129   if (map->writerel.writerel)
130     silc_map_writerel(map, mapconn);
131
132   /* If this was last connection, we are done and ready to quit. */
133   if (map->conn_num == map->conns_num) {
134     SILC_LOG_DEBUG(("All connections processed"));
135
136     /* Produce output */
137     if (map->writemap.writemap)
138       silc_map_write_ppm(map, map->writemap.filename);
139     for (i = 0; i < map->cut_count; i++) {
140       if (silc_map_cut(map, map->cut[i].x, map->cut[i].y, map->cut[i].width,
141                        map->cut[i].height, &ret_map)) {
142         silc_map_write_ppm(ret_map, map->cut[i].filename);
143         silc_map_free(ret_map);
144       }
145     }
146
147     /* Write the HTML index file */
148     if (map->writehtml.writehtml)
149       silc_map_writehtml_index(map);
150
151     /* Write the HTML map file(s) */
152     silc_map_writemaphtml(map);
153
154     /* Write uptime reliability graph */
155     if (map->writerel.writerel)
156       silc_map_writerelhtml(map);
157
158     /* Schedule to stop */
159     silc_schedule_task_add(map->client->schedule, 0,
160                            silc_map_process_done, map, 0, 1,
161                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
162   }
163 }
164
165 /* Timeout callback to detect if server is down. */
166
167 SILC_TASK_CALLBACK(silc_map_connect_timeout)
168 {
169   SilcMapConnection mapconn = context;
170
171   SILC_LOG_DEBUG(("Connection timeout"));
172
173   silc_schedule_task_del_by_context(mapconn->map->client->schedule, mapconn);
174
175   /* The server is down. */
176   mapconn->down = TRUE;
177
178   /* Continue to produce the data and the map. */
179   silc_map_process_data(mapconn->map, mapconn);
180 }
181
182 /* Timeout callback to detect if server is down. */
183
184 SILC_TASK_CALLBACK(silc_map_data_timeout)
185 {
186   SilcMapConnection mapconn = context;
187
188   SILC_LOG_DEBUG(("data timeout"));
189
190   silc_schedule_task_del_by_context(mapconn->map->client->schedule, mapconn);
191
192   /* The server is down. */
193   mapconn->down = TRUE;
194
195   /* Close connection, we didn't get any data. */
196   SILC_LOG_DEBUG(("Closing connection to %s:%d", mapconn->conn->remote_host,
197                   mapconn->conn->remote_port));
198   silc_client_close_connection(mapconn->conn->client, mapconn->conn);
199
200   /* Continue to produce the data and the map. */
201   silc_map_process_data(mapconn->map, mapconn);
202 }
203
204 /* Close connection to server */
205
206 SILC_TASK_CALLBACK(silc_map_connect_close)
207 {
208   SilcMapConnection mapconn = context;
209
210   SILC_LOG_DEBUG(("Closing connection to %s:%d", mapconn->conn->remote_host,
211                   mapconn->conn->remote_port));
212
213   silc_client_close_connection(mapconn->conn->client, mapconn->conn);
214
215   /* Continue to produce the data and the map. */
216   silc_map_process_data(mapconn->map, mapconn);
217 }
218
219 /* Create connection to remote server to gather information about it. */
220
221 void silc_map_connect(SilcMap map, SilcMapConnection mapconn)
222 {
223   char *ip;
224
225   if (!mapconn->connect) {
226     silc_schedule_task_add(map->client->schedule, 0,
227                            silc_map_connect_timeout, mapconn, 0, 1,
228                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
229     return;
230   }
231
232   /* First configure IP is used to connect. */
233   silc_dlist_start(mapconn->ips);
234   ip = silc_dlist_get(mapconn->ips);
235
236   SILC_LOG_DEBUG(("Creating connection to server %s:%d", ip, mapconn->port));
237
238   /* Create connection.  We'll continue in the silc_connected after
239      connection is created. */
240   silc_client_connect_to_server(map->client, NULL,
241                                 mapconn->port, ip, mapconn);
242
243   /* Set connect timeout to detect if the server is down. */
244   silc_schedule_task_add(map->client->schedule, 0,
245                          silc_map_connect_timeout, mapconn,
246                          mapconn->connect_timeout, 0,
247                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
248 }
249
250
251 /******* SILC Client Operations **********************************************/
252
253 /* "say" client operation is a message from the client library to the
254    application.  It may include error messages or something else.  We
255    just dump them to screen. */
256
257 static void
258 silc_say(SilcClient client, SilcClientConnection conn,
259          SilcClientMessageType type, char *msg, ...)
260 {
261
262 }
263
264
265 /* Message for a channel. The `sender' is the sender of the message
266    The `channel' is the channel. The `message' is the message.  Note
267    that `message' maybe NULL.  The `flags' indicates message flags
268    and it is used to determine how the message can be interpreted
269    (like it may tell the message is multimedia message). */
270
271 static void
272 silc_channel_message(SilcClient client, SilcClientConnection conn,
273                      SilcClientEntry sender, SilcChannelEntry channel,
274                      SilcMessagePayload payload,
275                      SilcMessageFlags flags, const unsigned char *message,
276                      SilcUInt32 message_len)
277 {
278
279 }
280
281
282 /* Private message to the client. The `sender' is the sender of the
283    message. The message is `message'and maybe NULL.  The `flags'
284    indicates message flags  and it is used to determine how the message
285    can be interpreted (like it may tell the message is multimedia
286    message). */
287
288 static void
289 silc_private_message(SilcClient client, SilcClientConnection conn,
290                      SilcClientEntry sender, SilcMessagePayload payload,
291                      SilcMessageFlags flags,
292                      const unsigned char *message,
293                      SilcUInt32 message_len)
294 {
295
296 }
297
298
299 /* Notify message to the client. The notify arguments are sent in the
300    same order as servers sends them. The arguments are same as received
301    from the server except for ID's.  If ID is received application receives
302    the corresponding entry to the ID. For example, if Client ID is received
303    application receives SilcClientEntry.  Also, if the notify type is
304    for channel the channel entry is sent to application (even if server
305    does not send it because client library gets the channel entry from
306    the Channel ID in the packet's header). */
307
308 static void
309 silc_notify(SilcClient client, SilcClientConnection conn,
310             SilcNotifyType type, ...)
311 {
312
313 }
314
315
316 /* Command handler. This function is called always in the command function.
317    If error occurs it will be called as well. `conn' is the associated
318    client connection. `cmd_context' is the command context that was
319    originally sent to the command. `success' is FALSE if error occurred
320    during command. `command' is the command being processed. It must be
321    noted that this is not reply from server. This is merely called just
322    after application has called the command. Just to tell application
323    that the command really was processed. */
324
325 static void
326 silc_command(SilcClient client, SilcClientConnection conn,
327              SilcClientCommandContext cmd_context, bool success,
328              SilcCommand command, SilcStatus status)
329 {
330
331 }
332
333
334 /* Command reply handler. This function is called always in the command reply
335    function. If error occurs it will be called as well. Normal scenario
336    is that it will be called after the received command data has been parsed
337    and processed. The function is used to pass the received command data to
338    the application.
339
340    `conn' is the associated client connection. `cmd_payload' is the command
341    payload data received from server and it can be ignored. It is provided
342    if the application would like to re-parse the received command data,
343    however, it must be noted that the data is parsed already by the library
344    thus the payload can be ignored. `success' is FALSE if error occurred.
345    In this case arguments are not sent to the application. The `status' is
346    the command reply status server returned. The `command' is the command
347    reply being processed. The function has variable argument list and each
348    command defines the number and type of arguments it passes to the
349    application (on error they are not sent). */
350
351 static void
352 silc_command_reply(SilcClient client, SilcClientConnection conn,
353                    SilcCommandPayload cmd_payload, bool success,
354                    SilcCommand command, SilcStatus status, ...)
355 {
356   SilcMapConnection mapconn = conn->context;
357   va_list va;
358
359   /* If error occurred in client library with our command, print the error */
360   if (status != SILC_STATUS_OK)
361     fprintf(stderr, "COMMAND REPLY %s: %s\n",
362             silc_get_command_name(command),
363             silc_get_status_message(status));
364
365   if (!success)
366     return;
367
368   va_start(va, status);
369
370   switch (command) {
371   case SILC_COMMAND_STATS:
372     {
373       unsigned char *stats = va_arg(va, unsigned char *);
374       SilcUInt32 stats_len = va_arg(va, SilcUInt32);
375       SilcBufferStruct buf;
376
377       SILC_LOG_DEBUG(("STATS command reply"));
378
379       /* Get statistics structure */
380       silc_buffer_set(&buf, stats, stats_len);
381       silc_buffer_unformat(&buf,
382                            SILC_STR_UI_INT(&mapconn->data.starttime),
383                            SILC_STR_UI_INT(&mapconn->data.uptime),
384                            SILC_STR_UI_INT(&mapconn->data.clients),
385                            SILC_STR_UI_INT(&mapconn->data.channels),
386                            SILC_STR_UI_INT(&mapconn->data.server_ops),
387                            SILC_STR_UI_INT(&mapconn->data.router_ops),
388                            SILC_STR_UI_INT(&mapconn->data.cell_clients),
389                            SILC_STR_UI_INT(&mapconn->data.cell_channels),
390                            SILC_STR_UI_INT(&mapconn->data.cell_servers),
391                            SILC_STR_UI_INT(&mapconn->data.all_clients),
392                            SILC_STR_UI_INT(&mapconn->data.all_channels),
393                            SILC_STR_UI_INT(&mapconn->data.all_servers),
394                            SILC_STR_UI_INT(&mapconn->data.all_routers),
395                            SILC_STR_UI_INT(&mapconn->data.all_server_ops),
396                            SILC_STR_UI_INT(&mapconn->data.all_router_ops),
397                            SILC_STR_END);
398
399       mapconn->stats_received = TRUE;
400     }
401     break;
402
403   case SILC_COMMAND_MOTD:
404     {
405       char *motd = va_arg(va, char *);
406
407       SILC_LOG_DEBUG(("MOTD command reply"));
408
409       mapconn->data.motd = strdup(motd);
410       mapconn->motd_received = TRUE;
411     }
412     break;
413
414   default:
415     break;
416   };
417
418   va_end(va);
419
420   if (mapconn->motd && !mapconn->motd_received)
421     return;
422   if (!mapconn->stats_received)
423     return;
424
425   silc_schedule_task_del_by_context(client->schedule, mapconn);
426
427   /* All data is gathered, time to disconnect from the server. */
428   silc_schedule_task_add(client->schedule, 0,
429                          silc_map_connect_close, mapconn, 0, 1,
430                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
431 }
432
433
434 /* Called to indicate that connection was either successfully established
435    or connecting failed.  This is also the first time application receives
436    the SilcClientConnection objecet which it should save somewhere.
437    If the `success' is FALSE the application must always call the function
438    silc_client_close_connection. */
439
440 static void
441 silc_connected(SilcClient client, SilcClientConnection conn,
442                SilcClientConnectionStatus status)
443 {
444   SilcMapConnection mapconn = conn->context;
445   SilcMap map = mapconn->map;
446
447   silc_schedule_task_del_by_context(client->schedule, mapconn);
448
449   if (status == SILC_CLIENT_CONN_ERROR) {
450     fprintf(stderr, "Could not connect to server %s\n",
451                      conn->remote_host ? conn->remote_host : "");
452     silc_client_close_connection(client, conn);
453
454     /* Mark that this server is down. */
455     silc_schedule_task_add(map->client->schedule, 0,
456                            silc_map_connect_timeout, mapconn, 0, 1,
457                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
458     return;
459   }
460
461   if (mapconn->down) {
462     /* Already timeouted */
463     SILC_LOG_DEBUG(("Connection already timedout"));
464     silc_client_close_connection(client, conn);
465     return;
466   }
467
468   SILC_LOG_DEBUG(("Connected to server %s:%d", conn->remote_host,
469                   conn->remote_port));
470
471   mapconn->conn = conn;
472
473   /* Get statistics */
474   silc_client_command_call(client, conn, "STATS");
475
476   /* Get motd if requested */
477   if (mapconn->motd) {
478     char motd[256];
479     char *hostname;
480     silc_dlist_start(mapconn->hostnames);
481     hostname = silc_dlist_get(mapconn->hostnames);
482     memset(motd, 0, sizeof(motd));
483     silc_strncat(motd, sizeof(motd), "MOTD ", 5);
484     silc_strncat(motd, sizeof(motd), hostname, strlen(hostname));
485     silc_client_command_call(client, conn, motd);
486   }
487
488   /* Set data timeout to detect if the server is down. */
489   silc_schedule_task_add(map->client->schedule, 0,
490                          silc_map_data_timeout, mapconn,
491                          mapconn->connect_timeout, 0,
492                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
493 }
494
495
496 /* Called to indicate that connection was disconnected to the server.
497    The `status' may tell the reason of the disconnection, and if the
498    `message' is non-NULL it may include the disconnection message
499    received from server. */
500
501 static void
502 silc_disconnected(SilcClient client, SilcClientConnection conn,
503                   SilcStatus status, const char *message)
504 {
505   SilcMapConnection mapconn = conn->context;
506
507   silc_schedule_task_del_by_context(client->schedule, mapconn);
508
509   SILC_LOG_DEBUG(("Disconnected from server %s:%d", conn->remote_host,
510                   conn->remote_port));
511
512   mapconn->conn = NULL;
513 }
514
515
516 /* Find authentication method and authentication data by hostname and
517    port. The hostname may be IP address as well. When the authentication
518    method has been resolved the `completion' callback with the found
519    authentication method and authentication data is called. The `conn'
520    may be NULL. */
521
522 static void
523 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
524                      char *hostname, SilcUInt16 port,
525                      SilcGetAuthMeth completion,
526                      void *context)
527 {
528   /* No auth */
529   completion(TRUE, SILC_AUTH_NONE, NULL, 0, context);
530 }
531
532
533 /* Verifies received public key. The `conn_type' indicates which entity
534    (server, client etc.) has sent the public key. If user decides to trust
535    the application may save the key as trusted public key for later
536    use. The `completion' must be called after the public key has been
537    verified. */
538
539 static void
540 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
541                        SilcSocketType conn_type, unsigned char *pk,
542                        SilcUInt32 pk_len, SilcSKEPKType pk_type,
543                        SilcVerifyPublicKey completion, void *context)
544 {
545   /* Accept all keys without verification */
546   completion(TRUE, context);
547 }
548
549
550 /* Ask (interact, that is) a passphrase from user. The passphrase is
551    returned to the library by calling the `completion' callback with
552    the `context'. The returned passphrase SHOULD be in UTF-8 encoded,
553    if not then the library will attempt to encode. */
554
555 static void
556 silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
557                     SilcAskPassphrase completion, void *context)
558 {
559   completion(NULL, 0, context);
560 }
561
562
563 /* Notifies application that failure packet was received.  This is called
564    if there is some protocol active in the client.  The `protocol' is the
565    protocol context.  The `failure' is opaque pointer to the failure
566    indication.  Note, that the `failure' is protocol dependant and
567    application must explicitly cast it to correct type.  Usually `failure'
568    is 32 bit failure type (see protocol specs for all protocol failure
569    types). */
570
571 static void
572 silc_failure(SilcClient client, SilcClientConnection conn,
573              SilcProtocol protocol, void *failure)
574 {
575   fprintf(stderr, "Connecting failed (protocol failure)\n");
576 }
577
578
579 /* Asks whether the user would like to perform the key agreement protocol.
580    This is called after we have received an key agreement packet or an
581    reply to our key agreement packet. This returns TRUE if the user wants
582    the library to perform the key agreement protocol and FALSE if it is not
583    desired (application may start it later by calling the function
584    silc_client_perform_key_agreement). If TRUE is returned also the
585    `completion' and `context' arguments must be set by the application. */
586
587 static bool
588 silc_key_agreement(SilcClient client, SilcClientConnection conn,
589                    SilcClientEntry client_entry, const char *hostname,
590                    SilcUInt16 port, SilcKeyAgreementCallback *completion,
591                    void **context)
592 {
593   return FALSE;
594 }
595
596
597 /* Notifies application that file transfer protocol session is being
598    requested by the remote client indicated by the `client_entry' from
599    the `hostname' and `port'. The `session_id' is the file transfer
600    session and it can be used to either accept or reject the file
601    transfer request, by calling the silc_client_file_receive or
602    silc_client_file_close, respectively. */
603
604 static void
605 silc_ftp(SilcClient client, SilcClientConnection conn,
606          SilcClientEntry client_entry, SilcUInt32 session_id,
607          const char *hostname, SilcUInt16 port)
608 {
609
610 }
611
612
613 /* Delivers SILC session detachment data indicated by `detach_data' to the
614    application.  If application has issued SILC_COMMAND_DETACH command
615    the client session in the SILC network is not quit.  The client remains
616    in the network but is detached.  The detachment data may be used later
617    to resume the session in the SILC Network.  The appliation is
618    responsible of saving the `detach_data', to for example in a file.
619
620    The detachment data can be given as argument to the functions
621    silc_client_connect_to_server, or silc_client_add_connection when
622    creating connection to remote server, inside SilcClientConnectionParams
623    structure.  If it is provided the client library will attempt to resume
624    the session in the network.  After the connection is created
625    successfully, the application is responsible of setting the user
626    interface for user into the same state it was before detaching (showing
627    same channels, channel modes, etc).  It can do this by fetching the
628    information (like joined channels) from the client library. */
629
630 static void
631 silc_detach(SilcClient client, SilcClientConnection conn,
632             const unsigned char *detach_data, SilcUInt32 detach_data_len)
633 {
634
635 }
636
637 /* This structure and all the functions were taken from the
638    lib/silcclient/client_ops_example.c. */
639 SilcClientOperations silc_map_client_ops = {
640   silc_say,
641   silc_channel_message,
642   silc_private_message,
643   silc_notify,
644   silc_command,
645   silc_command_reply,
646   silc_connected,
647   silc_disconnected,
648   silc_get_auth_method,
649   silc_verify_public_key,
650   silc_ask_passphrase,
651   silc_failure,
652   silc_key_agreement,
653   silc_ftp,
654   silc_detach
655 };