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