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