3eec5110993601674430cacc3d51639791b1e3d0
[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                      SilcMessageFlags flags, const unsigned char *message,
309                      SilcUInt32 message_len)
310 {
311
312 }
313
314
315 /* Private message to the client. The `sender' is the sender of the
316    message. The message is `message'and maybe NULL.  The `flags'
317    indicates message flags  and it is used to determine how the message
318    can be interpreted (like it may tell the message is multimedia
319    message). */
320
321 static void
322 silc_private_message(SilcClient client, SilcClientConnection conn,
323                      SilcClientEntry sender, SilcMessagePayload payload,
324                      SilcMessageFlags flags,
325                      const unsigned char *message,
326                      SilcUInt32 message_len)
327 {
328
329 }
330
331
332 /* Notify message to the client. The notify arguments are sent in the
333    same order as servers sends them. The arguments are same as received
334    from the server except for ID's.  If ID is received application receives
335    the corresponding entry to the ID. For example, if Client ID is received
336    application receives SilcClientEntry.  Also, if the notify type is
337    for channel the channel entry is sent to application (even if server
338    does not send it because client library gets the channel entry from
339    the Channel ID in the packet's header). */
340
341 static void
342 silc_notify(SilcClient client, SilcClientConnection conn,
343             SilcNotifyType type, ...)
344 {
345
346 }
347
348
349 /* Command handler. This function is called always in the command function.
350    If error occurs it will be called as well. `conn' is the associated
351    client connection. `cmd_context' is the command context that was
352    originally sent to the command. `success' is FALSE if error occurred
353    during command. `command' is the command being processed. It must be
354    noted that this is not reply from server. This is merely called just
355    after application has called the command. Just to tell application
356    that the command really was processed. */
357
358 static void
359 silc_command(SilcClient client, SilcClientConnection conn,
360              SilcClientCommandContext cmd_context, bool success,
361              SilcCommand command, SilcStatus status)
362 {
363
364 }
365
366
367 /* Command reply handler. This function is called always in the command reply
368    function. If error occurs it will be called as well. Normal scenario
369    is that it will be called after the received command data has been parsed
370    and processed. The function is used to pass the received command data to
371    the application.
372
373    `conn' is the associated client connection. `cmd_payload' is the command
374    payload data received from server and it can be ignored. It is provided
375    if the application would like to re-parse the received command data,
376    however, it must be noted that the data is parsed already by the library
377    thus the payload can be ignored. `success' is FALSE if error occurred.
378    In this case arguments are not sent to the application. The `status' is
379    the command reply status server returned. The `command' is the command
380    reply being processed. The function has variable argument list and each
381    command defines the number and type of arguments it passes to the
382    application (on error they are not sent). */
383
384 static void
385 silc_command_reply(SilcClient client, SilcClientConnection conn,
386                    SilcCommandPayload cmd_payload, bool success,
387                    SilcCommand command, SilcStatus status, ...)
388 {
389   SilcMapConnection mapconn = conn->context;
390   va_list va;
391
392   /* If error occurred in client library with our command, print the error */
393   if (status != SILC_STATUS_OK)
394     fprintf(stderr, "COMMAND REPLY %s: %s\n",
395             silc_get_command_name(command),
396             silc_get_status_message(status));
397
398   if (!success)
399     return;
400
401   va_start(va, status);
402
403   switch (command) {
404   case SILC_COMMAND_STATS:
405     {
406       unsigned char *stats = va_arg(va, unsigned char *);
407       SilcUInt32 stats_len = va_arg(va, SilcUInt32);
408       SilcBufferStruct buf;
409
410       SILC_LOG_DEBUG(("STATS command reply"));
411
412       /* Get statistics structure */
413       silc_buffer_set(&buf, stats, stats_len);
414       silc_buffer_unformat(&buf,
415                            SILC_STR_UI_INT(&mapconn->data.starttime),
416                            SILC_STR_UI_INT(&mapconn->data.uptime),
417                            SILC_STR_UI_INT(&mapconn->data.clients),
418                            SILC_STR_UI_INT(&mapconn->data.channels),
419                            SILC_STR_UI_INT(&mapconn->data.server_ops),
420                            SILC_STR_UI_INT(&mapconn->data.router_ops),
421                            SILC_STR_UI_INT(&mapconn->data.cell_clients),
422                            SILC_STR_UI_INT(&mapconn->data.cell_channels),
423                            SILC_STR_UI_INT(&mapconn->data.cell_servers),
424                            SILC_STR_UI_INT(&mapconn->data.all_clients),
425                            SILC_STR_UI_INT(&mapconn->data.all_channels),
426                            SILC_STR_UI_INT(&mapconn->data.all_servers),
427                            SILC_STR_UI_INT(&mapconn->data.all_routers),
428                            SILC_STR_UI_INT(&mapconn->data.all_server_ops),
429                            SILC_STR_UI_INT(&mapconn->data.all_router_ops),
430                            SILC_STR_END);
431
432       mapconn->stats_received = TRUE;
433     }
434     break;
435
436   case SILC_COMMAND_MOTD:
437     {
438       char *motd = va_arg(va, char *);
439
440       SILC_LOG_DEBUG(("MOTD command reply"));
441
442       mapconn->data.motd = strdup(motd);
443       mapconn->motd_received = TRUE;
444     }
445     break;
446
447   default:
448     break;
449   };
450
451   va_end(va);
452
453   if (mapconn->motd && !mapconn->motd_received)
454     return;
455   if (!mapconn->stats_received)
456     return;
457
458   silc_schedule_task_del_by_context(client->schedule, mapconn);
459
460   /* All data is gathered, time to disconnect from the server. */
461   silc_schedule_task_add(client->schedule, 0,
462                          silc_map_connect_close, mapconn, 0, 1,
463                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
464 }
465
466
467 /* Called to indicate that connection was either successfully established
468    or connecting failed.  This is also the first time application receives
469    the SilcClientConnection objecet which it should save somewhere.
470    If the `success' is FALSE the application must always call the function
471    silc_client_close_connection. */
472
473 static void
474 silc_connected(SilcClient client, SilcClientConnection conn,
475                SilcClientConnectionStatus status)
476 {
477   SilcMapConnection mapconn = conn->context;
478   SilcMap map = mapconn->map;
479
480   silc_schedule_task_del_by_context(client->schedule, mapconn);
481
482   if (status == SILC_CLIENT_CONN_ERROR) {
483     fprintf(stderr, "Could not connect to server %s\n",
484                      conn->remote_host ? conn->remote_host : "");
485     silc_client_close_connection(client, conn);
486
487     /* Mark that this server is down. */
488     silc_schedule_task_add(map->client->schedule, 0,
489                            silc_map_connect_timeout, mapconn, 0, 1,
490                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
491     return;
492   }
493
494   if (mapconn->down) {
495     /* Already timeouted */
496     SILC_LOG_DEBUG(("Connection already timedout"));
497     silc_client_close_connection(client, conn);
498     return;
499   }
500
501   SILC_LOG_DEBUG(("Connected to server %s:%d", conn->remote_host,
502                   conn->remote_port));
503
504   mapconn->conn = conn;
505
506   /* Get statistics */
507   silc_client_command_call(client, conn, "STATS");
508
509   /* Get motd if requested */
510   if (mapconn->motd) {
511     char motd[256];
512     char *hostname;
513     silc_dlist_start(mapconn->hostnames);
514     hostname = silc_dlist_get(mapconn->hostnames);
515     memset(motd, 0, sizeof(motd));
516     silc_strncat(motd, sizeof(motd), "MOTD ", 5);
517     silc_strncat(motd, sizeof(motd), hostname, strlen(hostname));
518     silc_client_command_call(client, conn, motd);
519   }
520
521   /* Set data timeout to detect if the server is down. */
522   silc_schedule_task_add(map->client->schedule, 0,
523                          silc_map_data_timeout, mapconn,
524                          mapconn->connect_timeout, 0,
525                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
526 }
527
528
529 /* Called to indicate that connection was disconnected to the server.
530    The `status' may tell the reason of the disconnection, and if the
531    `message' is non-NULL it may include the disconnection message
532    received from server. */
533
534 static void
535 silc_disconnected(SilcClient client, SilcClientConnection conn,
536                   SilcStatus status, const char *message)
537 {
538   SilcMapConnection mapconn = conn->context;
539
540   silc_schedule_task_del_by_context(client->schedule, mapconn);
541
542   SILC_LOG_DEBUG(("Disconnected from server %s:%d", conn->remote_host,
543                   conn->remote_port));
544
545   mapconn->conn = NULL;
546 }
547
548
549 /* Find authentication method and authentication data by hostname and
550    port. The hostname may be IP address as well. When the authentication
551    method has been resolved the `completion' callback with the found
552    authentication method and authentication data is called. The `conn'
553    may be NULL. */
554
555 static void
556 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
557                      char *hostname, SilcUInt16 port,
558                      SilcGetAuthMeth completion,
559                      void *context)
560 {
561   /* No auth */
562   completion(TRUE, SILC_AUTH_NONE, NULL, 0, context);
563 }
564
565
566 /* Verifies received public key. The `conn_type' indicates which entity
567    (server, client etc.) has sent the public key. If user decides to trust
568    the application may save the key as trusted public key for later
569    use. The `completion' must be called after the public key has been
570    verified. */
571
572 static void
573 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
574                        SilcSocketType conn_type, unsigned char *pk,
575                        SilcUInt32 pk_len, SilcSKEPKType pk_type,
576                        SilcVerifyPublicKey completion, void *context)
577 {
578   /* Accept all keys without verification */
579   completion(TRUE, context);
580 }
581
582
583 /* Ask (interact, that is) a passphrase from user. The passphrase is
584    returned to the library by calling the `completion' callback with
585    the `context'. The returned passphrase SHOULD be in UTF-8 encoded,
586    if not then the library will attempt to encode. */
587
588 static void
589 silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
590                     SilcAskPassphrase completion, void *context)
591 {
592   completion(NULL, 0, context);
593 }
594
595
596 /* Notifies application that failure packet was received.  This is called
597    if there is some protocol active in the client.  The `protocol' is the
598    protocol context.  The `failure' is opaque pointer to the failure
599    indication.  Note, that the `failure' is protocol dependant and
600    application must explicitly cast it to correct type.  Usually `failure'
601    is 32 bit failure type (see protocol specs for all protocol failure
602    types). */
603
604 static void
605 silc_failure(SilcClient client, SilcClientConnection conn,
606              SilcProtocol protocol, void *failure)
607 {
608   fprintf(stderr, "Connecting failed (protocol failure)\n");
609 }
610
611
612 /* Asks whether the user would like to perform the key agreement protocol.
613    This is called after we have received an key agreement packet or an
614    reply to our key agreement packet. This returns TRUE if the user wants
615    the library to perform the key agreement protocol and FALSE if it is not
616    desired (application may start it later by calling the function
617    silc_client_perform_key_agreement). If TRUE is returned also the
618    `completion' and `context' arguments must be set by the application. */
619
620 static bool
621 silc_key_agreement(SilcClient client, SilcClientConnection conn,
622                    SilcClientEntry client_entry, const char *hostname,
623                    SilcUInt16 port, SilcKeyAgreementCallback *completion,
624                    void **context)
625 {
626   return FALSE;
627 }
628
629
630 /* Notifies application that file transfer protocol session is being
631    requested by the remote client indicated by the `client_entry' from
632    the `hostname' and `port'. The `session_id' is the file transfer
633    session and it can be used to either accept or reject the file
634    transfer request, by calling the silc_client_file_receive or
635    silc_client_file_close, respectively. */
636
637 static void
638 silc_ftp(SilcClient client, SilcClientConnection conn,
639          SilcClientEntry client_entry, SilcUInt32 session_id,
640          const char *hostname, SilcUInt16 port)
641 {
642
643 }
644
645
646 /* Delivers SILC session detachment data indicated by `detach_data' to the
647    application.  If application has issued SILC_COMMAND_DETACH command
648    the client session in the SILC network is not quit.  The client remains
649    in the network but is detached.  The detachment data may be used later
650    to resume the session in the SILC Network.  The appliation is
651    responsible of saving the `detach_data', to for example in a file.
652
653    The detachment data can be given as argument to the functions
654    silc_client_connect_to_server, or silc_client_add_connection when
655    creating connection to remote server, inside SilcClientConnectionParams
656    structure.  If it is provided the client library will attempt to resume
657    the session in the network.  After the connection is created
658    successfully, the application is responsible of setting the user
659    interface for user into the same state it was before detaching (showing
660    same channels, channel modes, etc).  It can do this by fetching the
661    information (like joined channels) from the client library. */
662
663 static void
664 silc_detach(SilcClient client, SilcClientConnection conn,
665             const unsigned char *detach_data, SilcUInt32 detach_data_len)
666 {
667
668 }
669
670 /* This structure and all the functions were taken from the
671    lib/silcclient/client_ops_example.c. */
672 SilcClientOperations silc_map_client_ops = {
673   silc_say,
674   silc_channel_message,
675   silc_private_message,
676   silc_notify,
677   silc_command,
678   silc_command_reply,
679   silc_connected,
680   silc_disconnected,
681   silc_get_auth_method,
682   silc_verify_public_key,
683   silc_ask_passphrase,
684   silc_failure,
685   silc_key_agreement,
686   silc_ftp,
687   silc_detach
688 };