Added support for automatically parsing and encoding signature
[silc.git] / tutorial / mybot / mybot.c
1 /*
2
3   mybot.c 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>, November 2002
6   This code is Public Domain.
7
8   MyBot
9
10   Example SILC client called "mybot".  It is a robot client which
11   connects to SILC Network into silc.silcnet.org server and joins
12   channel called "mybot" and says "hello" on the channel.
13
14   This code use the SILC Client Library provided by the SILC
15   Toolkit distribution.
16
17   Compilation:
18
19   gcc -o mybot mybot.c -I/usr/local/silc/include -L/usr/local/silc/lib \
20       -lsilc -lsilcclient -lpthread -ldl
21
22   The MyBot works as follows (logicly):
23
24   main -> mybot_start -> silc_client_connect_to_server
25                 v
26           silc_client_run (message loop...)
27                 v
28           silc_verify_public_key
29                 v
30           silc_get_auth_method
31                 v
32           silc_connected -> silc_client_command_call (JOIN)
33                 v
34           silc_command_reply -> silc_send_channel_message ("hello")
35                 v
36           message loop...
37                 v
38   main <- mybot_start
39
40 */
41
42 #include "silcincludes.h"       /* Mandatory include for SILC applications */
43 #include "silcclient.h"         /* SILC Client Library API */
44
45 SilcClientOperations ops;
46
47 /******* MyBot code **********************************************************/
48
49 /* This is context for our MyBot client */
50 typedef struct {
51   SilcClient client;            /* The actual SILC Client */
52   SilcClientConnection conn;    /* Connection to the server */
53 } *MyBot;
54
55 /* Start the MyBot, by creating the SILC Client entity by using the
56    SILC Client Library API. */
57 int mybot_start(void)
58 {
59   MyBot mybot;
60
61   /* Allocate the MyBot structure */
62   mybot = silc_calloc(1, sizeof(*mybot));
63   if (!mybot) {
64     perror("Out of memory");
65     return 1;
66   }
67
68   /* Allocate our SILC Client which is the MyBot.  The arguments to the
69      function are:
70
71      ops           - our client operations that the library requires
72      param         - parameters, but we don't have any so we pass NULL,
73      application   - our application, ie. the MyBot of course!
74      version       - silc version, provided by the library if we put NULL
75   */
76   mybot->client = silc_client_alloc(&ops, NULL, mybot, NULL);
77   if (!mybot->client) {
78     perror("Could not allocate SILC Client");
79     return 1;
80   }
81
82   /* Now fill the allocated client with mandatory parameters the library
83      requires: username, hostname and "real name". */
84   mybot->client->username = silc_get_username();
85   mybot->client->hostname = silc_net_localhost();
86   mybot->client->realname = strdup("I am the MyBot");
87
88   /* Now we initialize the client. */
89   if (!silc_client_init(mybot->client)) {
90     perror("Could not init client");
91     return 1;
92   }
93
94   /* Then we load our public key from the file.  The library requires
95      the key pair loaded before the client is started.  The SILC Toolkit
96      provides nice routines to do just that so we don't have to worry
97      about much.
98
99      Oh, and if the key pair doesn't exist, we create one here
100      automatically, and save them to files for future. */
101   if (!silc_load_key_pair("mybot.pub", "mybot.prv", "",
102                           &mybot->client->pkcs,
103                           &mybot->client->public_key,
104                           &mybot->client->private_key)) {
105     /* The keys don't exist.  Let's generate us a key pair then!  There's
106        nice ready routine for that too.  Let's do 2048 bit RSA key pair. */
107     fprintf(stdout, "MyBot: Key pair does not exist, generating it.\n");
108     if (!silc_create_key_pair("rsa", 2048, "mybot.pub", "mybot.prv", NULL, "",
109                               &mybot->client->pkcs,
110                               &mybot->client->public_key,
111                               &mybot->client->private_key, FALSE)) {
112       perror("Could not generated key pair");
113       return 1;
114     }
115   }
116
117   /* Start connecting to server.  This is asynchronous connecting so the
118      connection is actually created later after we run the client. */
119   silc_client_connect_to_server(mybot->client, NULL, 706,
120                                 "silc.silcnet.org", mybot);
121
122   /* And, then we are ready to go.  Since we are really simple client we
123      don't have user interface and we don't have to deal with message loops
124      or interactivity.  That's why we can just hand over the execution
125      to the library by calling silc_client_run.  */
126   silc_client_run(mybot->client);
127
128   /* When we get here, we have quit the client, so clean up and exit */
129   silc_client_free(mybot->client);
130   silc_free(mybot);
131   return 0;
132 }
133
134 /******* SILC Client Operations **********************************************/
135
136 /* The SILC Client Library requires these "client operations".  They are
137    functions that the library may call at any time to indicate to application
138    that something happened, like message was received, or authentication
139    is required or something else.  Since our MyBot is really simple client
140    we don't need most of the operations, so we just define them and don't
141    do anything in them. */
142
143 /* "say" client operation is a message from the client library to the
144    application.  It may include error messages or something else.  We
145    just dump them to screen. */
146
147 static void
148 silc_say(SilcClient client, SilcClientConnection conn,
149          SilcClientMessageType type, char *msg, ...)
150 {
151   char str[200];
152   va_list va;
153   va_start(va, msg);
154   vsnprintf(str, sizeof(str) - 1, msg, va);
155   fprintf(stdout, "MyBot: %s\n", str);
156   va_end(va);
157 }
158
159
160 /* Message for a channel. The `sender' is the sender of the message
161    The `channel' is the channel. The `message' is the message.  Note
162    that `message' maybe NULL.  The `flags' indicates message flags
163    and it is used to determine how the message can be interpreted
164    (like it may tell the message is multimedia message). */
165
166 static void
167 silc_channel_message(SilcClient client, SilcClientConnection conn,
168                      SilcClientEntry sender, SilcChannelEntry channel,
169                      SilcMessagePayload payload,
170                      SilcMessageFlags flags, const unsigned char *message,
171                      SilcUInt32 message_len)
172 {
173   /* Yay! We got a message from channel. */
174   fprintf(stdout, "<%s> %s\n", sender->nickname, message);
175 }
176
177
178 /* Private message to the client. The `sender' is the sender of the
179    message. The message is `message'and maybe NULL.  The `flags'  
180    indicates message flags  and it is used to determine how the message
181    can be interpreted (like it may tell the message is multimedia
182    message). */
183
184 static void
185 silc_private_message(SilcClient client, SilcClientConnection conn,
186                      SilcClientEntry sender, SilcMessagePayload payload,
187                      SilcMessageFlags flags,
188                      const unsigned char *message,
189                      SilcUInt32 message_len)
190 {
191   /* MyBot does not support private message receiving */
192 }
193
194
195 /* Notify message to the client. The notify arguments are sent in the
196    same order as servers sends them. The arguments are same as received
197    from the server except for ID's.  If ID is received application receives
198    the corresponding entry to the ID. For example, if Client ID is received
199    application receives SilcClientEntry.  Also, if the notify type is
200    for channel the channel entry is sent to application (even if server
201    does not send it because client library gets the channel entry from
202    the Channel ID in the packet's header). */
203
204 static void
205 silc_notify(SilcClient client, SilcClientConnection conn,
206             SilcNotifyType type, ...)
207 {
208   char *str;
209   va_list va;
210
211   va_start(va, type);
212
213   /* Here we can receive all kinds of different data from the server, but
214      our simple bot is interested only in receiving the "not-so-important"
215      stuff, just for fun. :) */
216   switch (type) {
217   case SILC_NOTIFY_TYPE_NONE:
218     /* Received something that we are just going to dump to screen. */
219     str = va_arg(va, char *);
220     fprintf(stdout, "--- %s\n", str);
221     break;
222
223   case SILC_NOTIFY_TYPE_MOTD:
224     /* Received the Message of the Day from the server. */
225     str = va_arg(va, char *);
226     fprintf(stdout, "%s", str);
227     fprintf(stdout, "\n");
228     break;
229
230   default:
231     /* Ignore rest */
232     break;
233   }
234
235   va_end(va);
236 }
237
238
239 /* Command handler. This function is called always in the command function.
240    If error occurs it will be called as well. `conn' is the associated
241    client connection. `cmd_context' is the command context that was
242    originally sent to the command. `success' is FALSE if error occurred
243    during command. `command' is the command being processed. It must be
244    noted that this is not reply from server. This is merely called just
245    after application has called the command. Just to tell application
246    that the command really was processed. */
247
248 static void
249 silc_command(SilcClient client, SilcClientConnection conn,
250              SilcClientCommandContext cmd_context, bool success,
251              SilcCommand command, SilcStatus status)
252 {
253   /* If error occurred in client library with our command, print the error */
254   if (status != SILC_STATUS_OK)
255     fprintf(stderr, "MyBot: COMMAND %s: %s\n",
256             silc_get_command_name(command),
257             silc_get_status_message(status));
258 }
259
260
261 /* Command reply handler. This function is called always in the command reply
262    function. If error occurs it will be called as well. Normal scenario
263    is that it will be called after the received command data has been parsed
264    and processed. The function is used to pass the received command data to
265    the application.
266
267    `conn' is the associated client connection. `cmd_payload' is the command
268    payload data received from server and it can be ignored. It is provided
269    if the application would like to re-parse the received command data,
270    however, it must be noted that the data is parsed already by the library
271    thus the payload can be ignored. `success' is FALSE if error occurred.
272    In this case arguments are not sent to the application. The `status' is
273    the command reply status server returned. The `command' is the command
274    reply being processed. The function has variable argument list and each
275    command defines the number and type of arguments it passes to the
276    application (on error they are not sent). */
277
278 static void
279 silc_command_reply(SilcClient client, SilcClientConnection conn,
280                    SilcCommandPayload cmd_payload, bool success,
281                    SilcCommand command, SilcStatus status, ...)
282 {
283   va_list va;
284
285   /* If error occurred in client library with our command, print the error */
286   if (status != SILC_STATUS_OK)
287     fprintf(stderr, "MyBot: COMMAND REPLY %s: %s\n",
288             silc_get_command_name(command),
289             silc_get_status_message(status));
290
291   va_start(va, status);
292
293   /* Check for successful JOIN */
294   if (command == SILC_COMMAND_JOIN) {
295     SilcChannelEntry channel;
296
297     (void)va_arg(va, SilcClientEntry);
298     channel = va_arg(va, SilcChannelEntry);
299
300     fprintf(stdout, "MyBot: Joined '%s' channel\n", channel->channel_name);
301
302     /* Now send the "hello" to the channel */
303     silc_client_send_channel_message(client, conn, channel, NULL, 0,
304                                      "hello", strlen("hello"), FALSE);
305     fprintf(stdout, "MyBot: Sent 'hello' to channel\n");
306   }
307
308   va_end(va);
309 }
310
311
312 /* Called to indicate that connection was either successfully established
313    or connecting failed.  This is also the first time application receives
314    the SilcClientConnection objecet which it should save somewhere.
315    If the `success' is FALSE the application must always call the function
316    silc_client_close_connection. */
317
318 static void
319 silc_connected(SilcClient client, SilcClientConnection conn,
320                SilcClientConnectionStatus status)
321 {
322   MyBot mybot = client->application;
323   SilcBuffer idp;
324
325   if (status == SILC_CLIENT_CONN_ERROR) {
326     fprintf(stderr, "MyBot: Could not connect to server\n");
327     silc_client_close_connection(client, conn);
328     return;
329   }
330
331   fprintf(stdout, "MyBot: Connected to server.\n");
332
333   /* Save the connection context */
334   mybot->conn = conn;
335
336   /* Now that we are connected, join to mybot channel with JOIN command. */
337   silc_client_command_call(client, conn, "JOIN mybot");
338 }
339
340
341 /* Called to indicate that connection was disconnected to the server.
342    The `status' may tell the reason of the disconnection, and if the
343    `message' is non-NULL it may include the disconnection message
344    received from server. */
345
346 static void
347 silc_disconnected(SilcClient client, SilcClientConnection conn,
348                   SilcStatus status, const char *message)
349 {
350   MyBot mybot = client->application;
351
352   /* We got disconnected from server */
353   mybot->conn = NULL;
354   fprintf(stdout, "MyBot: %s:%s\n", silc_get_status_message(status),
355           message);
356 }
357
358
359 /* Find authentication method and authentication data by hostname and
360    port. The hostname may be IP address as well. When the authentication
361    method has been resolved the `completion' callback with the found
362    authentication method and authentication data is called. The `conn'
363    may be NULL. */
364
365 static void
366 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
367                      char *hostname, SilcUInt16 port,
368                      SilcGetAuthMeth completion,
369                      void *context)
370 {
371   /* MyBot assumes that there is no authentication requirement in the
372      server and sends nothing as authentication.  We just reply with
373      TRUE, meaning we know what is the authentication method. :). */
374   completion(TRUE, SILC_AUTH_NONE, NULL, 0, context);
375 }
376
377
378 /* Verifies received public key. The `conn_type' indicates which entity
379    (server, client etc.) has sent the public key. If user decides to trust
380    the application may save the key as trusted public key for later
381    use. The `completion' must be called after the public key has been
382    verified. */
383
384 static void
385 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
386                        SilcSocketType conn_type, unsigned char *pk,
387                        SilcUInt32 pk_len, SilcSKEPKType pk_type,
388                        SilcVerifyPublicKey completion, void *context)
389 {
390   /* MyBot is also very trusting, so we just accept the public key
391      we get here.  Of course, we would have to verify the authenticity
392      of the public key but our bot is too simple for that.  We just
393      reply with TRUE, meaning "yeah, we trust it". :) */
394   completion(TRUE, context);
395 }
396
397
398 /* Ask (interact, that is) a passphrase from user. The passphrase is
399    returned to the library by calling the `completion' callback with
400    the `context'. The returned passphrase SHOULD be in UTF-8 encoded,
401    if not then the library will attempt to encode. */
402
403 static void
404 silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
405                     SilcAskPassphrase completion, void *context)
406 {
407   /* MyBot does not support asking passphrases from users since there
408      is no user in our little client.  We just reply with nothing. */
409   completion(NULL, 0, context);
410 }
411
412
413 /* Notifies application that failure packet was received.  This is called
414    if there is some protocol active in the client.  The `protocol' is the
415    protocol context.  The `failure' is opaque pointer to the failure
416    indication.  Note, that the `failure' is protocol dependant and
417    application must explicitly cast it to correct type.  Usually `failure'
418    is 32 bit failure type (see protocol specs for all protocol failure
419    types). */
420
421 static void
422 silc_failure(SilcClient client, SilcClientConnection conn,
423              SilcProtocol protocol, void *failure)
424 {
425   /* Well, something bad must have happened during connecting to the
426      server since we got here.  Let's just print that something failed.
427      The "failure" would include more information but let's not bother
428      with that now. */
429   fprintf(stderr, "MyBot: Connecting failed (protocol failure)\n");
430 }
431
432
433 /* Asks whether the user would like to perform the key agreement protocol.
434    This is called after we have received an key agreement packet or an
435    reply to our key agreement packet. This returns TRUE if the user wants
436    the library to perform the key agreement protocol and FALSE if it is not
437    desired (application may start it later by calling the function
438    silc_client_perform_key_agreement). If TRUE is returned also the
439    `completion' and `context' arguments must be set by the application. */
440
441 static bool
442 silc_key_agreement(SilcClient client, SilcClientConnection conn,
443                    SilcClientEntry client_entry, const char *hostname,
444                    SilcUInt16 port, SilcKeyAgreementCallback *completion,
445                    void **context)
446 {
447   /* MyBot does not support incoming key agreement protocols, it's too
448      simple for that. */
449   return FALSE;
450 }
451
452
453 /* Notifies application that file transfer protocol session is being
454    requested by the remote client indicated by the `client_entry' from
455    the `hostname' and `port'. The `session_id' is the file transfer
456    session and it can be used to either accept or reject the file
457    transfer request, by calling the silc_client_file_receive or
458    silc_client_file_close, respectively. */
459
460 static void
461 silc_ftp(SilcClient client, SilcClientConnection conn,
462          SilcClientEntry client_entry, SilcUInt32 session_id,
463          const char *hostname, SilcUInt16 port)
464 {
465   /* MyBot does not support file transfer, it's too simple for that too. */
466 }
467
468
469 /* Delivers SILC session detachment data indicated by `detach_data' to the
470    application.  If application has issued SILC_COMMAND_DETACH command
471    the client session in the SILC network is not quit.  The client remains
472    in the network but is detached.  The detachment data may be used later
473    to resume the session in the SILC Network.  The appliation is
474    responsible of saving the `detach_data', to for example in a file.
475
476    The detachment data can be given as argument to the functions
477    silc_client_connect_to_server, or silc_client_add_connection when
478    creating connection to remote server, inside SilcClientConnectionParams
479    structure.  If it is provided the client library will attempt to resume
480    the session in the network.  After the connection is created
481    successfully, the application is responsible of setting the user
482    interface for user into the same state it was before detaching (showing
483    same channels, channel modes, etc).  It can do this by fetching the
484    information (like joined channels) from the client library. */
485
486 static void
487 silc_detach(SilcClient client, SilcClientConnection conn,
488             const unsigned char *detach_data, SilcUInt32 detach_data_len)
489 {
490   /* Oh, and MyBot does not support session detaching either. */
491 }
492
493 /* Our client operations for the MyBot.  This structure is filled with
494    functions and given as argument to the silc_client_alloc function.
495    Even though our little bot does not need all these functions we must
496    provide them since the SILC Client Library wants them all. */
497 /* This structure and all the functions were taken from the
498    lib/silcclient/client_ops_example.c. */
499 SilcClientOperations ops = {
500   silc_say,
501   silc_channel_message,
502   silc_private_message,
503   silc_notify,
504   silc_command,
505   silc_command_reply,
506   silc_connected,
507   silc_disconnected,
508   silc_get_auth_method,
509   silc_verify_public_key,
510   silc_ask_passphrase,
511   silc_failure,
512   silc_key_agreement,
513   silc_ftp,
514   silc_detach
515 };
516
517 int main(int argc, char **argv)
518 {
519   /* Start the bot */
520   return mybot_start();
521 }