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