8e16c897ec72c7a650ad112a20addef4562bd40e
[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                      SilcMessageFlags flags, const unsigned char *message,
170                      SilcUInt32 message_len)
171 {
172   /* Yay! We got a message from channel. */
173   fprintf(stdout, "<%s> %s\n", sender->nickname, message);
174 }
175
176
177 /* Private message to the client. The `sender' is the sender of the
178    message. The message is `message'and maybe NULL.  The `flags'  
179    indicates message flags  and it is used to determine how the message
180    can be interpreted (like it may tell the message is multimedia
181    message). */
182
183 static void
184 silc_private_message(SilcClient client, SilcClientConnection conn,
185                      SilcClientEntry sender, SilcMessageFlags flags,
186                      const unsigned char *message,
187                      SilcUInt32 message_len)
188 {
189   /* MyBot does not support private message receiving */
190 }
191
192
193 /* Notify message to the client. The notify arguments are sent in the
194    same order as servers sends them. The arguments are same as received
195    from the server except for ID's.  If ID is received application receives
196    the corresponding entry to the ID. For example, if Client ID is received
197    application receives SilcClientEntry.  Also, if the notify type is
198    for channel the channel entry is sent to application (even if server
199    does not send it because client library gets the channel entry from
200    the Channel ID in the packet's header). */
201
202 static void
203 silc_notify(SilcClient client, SilcClientConnection conn,
204             SilcNotifyType type, ...)
205 {
206   char *str;
207   va_list va;
208
209   va_start(va, type);
210
211   /* Here we can receive all kinds of different data from the server, but
212      our simple bot is interested only in receiving the "not-so-important"
213      stuff, just for fun. :) */
214   switch (type) {
215   case SILC_NOTIFY_TYPE_NONE:
216     /* Received something that we are just going to dump to screen. */
217     str = va_arg(va, char *);
218     fprintf(stdout, "--- %s\n", str);
219     break;
220
221   case SILC_NOTIFY_TYPE_MOTD:
222     /* Received the Message of the Day from the server. */
223     str = va_arg(va, char *);
224     fprintf(stdout, "%s", str);
225     fprintf(stdout, "\n");
226     break;
227
228   default:
229     /* Ignore rest */
230     break;
231   }
232
233   va_end(va);
234 }
235
236
237 /* Command handler. This function is called always in the command function.
238    If error occurs it will be called as well. `conn' is the associated
239    client connection. `cmd_context' is the command context that was
240    originally sent to the command. `success' is FALSE if error occurred
241    during command. `command' is the command being processed. It must be
242    noted that this is not reply from server. This is merely called just
243    after application has called the command. Just to tell application
244    that the command really was processed. */
245
246 static void
247 silc_command(SilcClient client, SilcClientConnection conn,
248              SilcClientCommandContext cmd_context, bool success,
249              SilcCommand command, SilcStatus status)
250 {
251   /* If error occurred in client library with our command, print the error */
252   if (status != SILC_STATUS_OK)
253     fprintf(stderr, "MyBot: COMMAND %s: %s\n",
254             silc_get_command_name(command),
255             silc_get_status_message(status));
256 }
257
258
259 /* Command reply handler. This function is called always in the command reply
260    function. If error occurs it will be called as well. Normal scenario
261    is that it will be called after the received command data has been parsed
262    and processed. The function is used to pass the received command data to
263    the application.
264
265    `conn' is the associated client connection. `cmd_payload' is the command
266    payload data received from server and it can be ignored. It is provided
267    if the application would like to re-parse the received command data,
268    however, it must be noted that the data is parsed already by the library
269    thus the payload can be ignored. `success' is FALSE if error occurred.
270    In this case arguments are not sent to the application. The `status' is
271    the command reply status server returned. The `command' is the command
272    reply being processed. The function has variable argument list and each
273    command defines the number and type of arguments it passes to the
274    application (on error they are not sent). */
275
276 static void
277 silc_command_reply(SilcClient client, SilcClientConnection conn,
278                    SilcCommandPayload cmd_payload, bool success,
279                    SilcCommand command, SilcStatus status, ...)
280 {
281   va_list va;
282
283   /* If error occurred in client library with our command, print the error */
284   if (status != SILC_STATUS_OK)
285     fprintf(stderr, "MyBot: COMMAND REPLY %s: %s\n",
286             silc_get_command_name(command),
287             silc_get_status_message(status));
288
289   va_start(va, status);
290
291   /* Check for successful JOIN */
292   if (command == SILC_COMMAND_JOIN) {
293     SilcChannelEntry channel;
294
295     (void)va_arg(va, SilcClientEntry);
296     channel = va_arg(va, SilcChannelEntry);
297
298     fprintf(stdout, "MyBot: Joined '%s' channel\n", channel->channel_name);
299
300     /* Now send the "hello" to the channel */
301     silc_client_send_channel_message(client, conn, channel, NULL, 0,
302                                      "hello", strlen("hello"), FALSE);
303     fprintf(stdout, "MyBot: Sent 'hello' to channel\n");
304   }
305
306   va_end(va);
307 }
308
309
310 /* Called to indicate that connection was either successfully established
311    or connecting failed.  This is also the first time application receives
312    the SilcClientConnection objecet which it should save somewhere.
313    If the `success' is FALSE the application must always call the function
314    silc_client_close_connection. */
315
316 static void
317 silc_connected(SilcClient client, SilcClientConnection conn,
318                SilcClientConnectionStatus status)
319 {
320   MyBot mybot = client->application;
321   SilcBuffer idp;
322
323   if (status == SILC_CLIENT_CONN_ERROR) {
324     fprintf(stderr, "MyBot: Could not connect to server\n");
325     silc_client_close_connection(client, conn);
326     return;
327   }
328
329   fprintf(stdout, "MyBot: Connected to server.\n");
330
331   /* Save the connection context */
332   mybot->conn = conn;
333
334   /* Now that we are connected, join to mybot channel with JOIN command. */
335   silc_client_command_call(client, conn, "JOIN mybot");
336 }
337
338
339 /* Called to indicate that connection was disconnected to the server.
340    The `status' may tell the reason of the disconnection, and if the
341    `message' is non-NULL it may include the disconnection message
342    received from server. */
343
344 static void
345 silc_disconnected(SilcClient client, SilcClientConnection conn,
346                   SilcStatus status, const char *message)
347 {
348   MyBot mybot = client->application;
349
350   /* We got disconnected from server */
351   mybot->conn = NULL;
352   fprintf(stdout, "MyBot: %s:%s\n", silc_get_status_message(status),
353           message);
354 }
355
356
357 /* Find authentication method and authentication data by hostname and
358    port. The hostname may be IP address as well. When the authentication
359    method has been resolved the `completion' callback with the found
360    authentication method and authentication data is called. The `conn'
361    may be NULL. */
362
363 static void
364 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
365                      char *hostname, SilcUInt16 port,
366                      SilcGetAuthMeth completion,
367                      void *context)
368 {
369   /* MyBot assumes that there is no authentication requirement in the
370      server and sends nothing as authentication.  We just reply with
371      TRUE, meaning we know what is the authentication method. :). */
372   completion(TRUE, SILC_AUTH_NONE, NULL, 0, context);
373 }
374
375
376 /* Verifies received public key. The `conn_type' indicates which entity
377    (server, client etc.) has sent the public key. If user decides to trust
378    the application may save the key as trusted public key for later
379    use. The `completion' must be called after the public key has been
380    verified. */
381
382 static void
383 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
384                        SilcSocketType conn_type, unsigned char *pk,
385                        SilcUInt32 pk_len, SilcSKEPKType pk_type,
386                        SilcVerifyPublicKey completion, void *context)
387 {
388   /* MyBot is also very trusting, so we just accept the public key
389      we get here.  Of course, we would have to verify the authenticity
390      of the public key but our bot is too simple for that.  We just
391      reply with TRUE, meaning "yeah, we trust it". :) */
392   completion(TRUE, context);
393 }
394
395
396 /* Ask (interact, that is) a passphrase from user. The passphrase is
397    returned to the library by calling the `completion' callback with
398    the `context'. The returned passphrase SHOULD be in UTF-8 encoded,
399    if not then the library will attempt to encode. */
400
401 static void
402 silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
403                     SilcAskPassphrase completion, void *context)
404 {
405   /* MyBot does not support asking passphrases from users since there
406      is no user in our little client.  We just reply with nothing. */
407   completion(NULL, 0, context);
408 }
409
410
411 /* Notifies application that failure packet was received.  This is called
412    if there is some protocol active in the client.  The `protocol' is the
413    protocol context.  The `failure' is opaque pointer to the failure
414    indication.  Note, that the `failure' is protocol dependant and
415    application must explicitly cast it to correct type.  Usually `failure'
416    is 32 bit failure type (see protocol specs for all protocol failure
417    types). */
418
419 static void
420 silc_failure(SilcClient client, SilcClientConnection conn,
421              SilcProtocol protocol, void *failure)
422 {
423   /* Well, something bad must have happened during connecting to the
424      server since we got here.  Let's just print that something failed.
425      The "failure" would include more information but let's not bother
426      with that now. */
427   fprintf(stderr, "MyBot: Connecting failed (protocol failure)\n");
428 }
429
430
431 /* Asks whether the user would like to perform the key agreement protocol.
432    This is called after we have received an key agreement packet or an
433    reply to our key agreement packet. This returns TRUE if the user wants
434    the library to perform the key agreement protocol and FALSE if it is not
435    desired (application may start it later by calling the function
436    silc_client_perform_key_agreement). If TRUE is returned also the
437    `completion' and `context' arguments must be set by the application. */
438
439 static bool
440 silc_key_agreement(SilcClient client, SilcClientConnection conn,
441                    SilcClientEntry client_entry, const char *hostname,
442                    SilcUInt16 port, SilcKeyAgreementCallback *completion,
443                    void **context)
444 {
445   /* MyBot does not support incoming key agreement protocols, it's too
446      simple for that. */
447   return FALSE;
448 }
449
450
451 /* Notifies application that file transfer protocol session is being
452    requested by the remote client indicated by the `client_entry' from
453    the `hostname' and `port'. The `session_id' is the file transfer
454    session and it can be used to either accept or reject the file
455    transfer request, by calling the silc_client_file_receive or
456    silc_client_file_close, respectively. */
457
458 static void
459 silc_ftp(SilcClient client, SilcClientConnection conn,
460          SilcClientEntry client_entry, SilcUInt32 session_id,
461          const char *hostname, SilcUInt16 port)
462 {
463   /* MyBot does not support file transfer, it's too simple for that too. */
464 }
465
466
467 /* Delivers SILC session detachment data indicated by `detach_data' to the
468    application.  If application has issued SILC_COMMAND_DETACH command
469    the client session in the SILC network is not quit.  The client remains
470    in the network but is detached.  The detachment data may be used later
471    to resume the session in the SILC Network.  The appliation is
472    responsible of saving the `detach_data', to for example in a file.
473
474    The detachment data can be given as argument to the functions
475    silc_client_connect_to_server, or silc_client_add_connection when
476    creating connection to remote server, inside SilcClientConnectionParams
477    structure.  If it is provided the client library will attempt to resume
478    the session in the network.  After the connection is created
479    successfully, the application is responsible of setting the user
480    interface for user into the same state it was before detaching (showing
481    same channels, channel modes, etc).  It can do this by fetching the
482    information (like joined channels) from the client library. */
483
484 static void
485 silc_detach(SilcClient client, SilcClientConnection conn,
486             const unsigned char *detach_data, SilcUInt32 detach_data_len)
487 {
488   /* Oh, and MyBot does not support session detaching either. */
489 }
490
491 /* Our client operations for the MyBot.  This structure is filled with
492    functions and given as argument to the silc_client_alloc function.
493    Even though our little bot does not need all these functions we must
494    provide them since the SILC Client Library wants them all. */
495 /* This structure and all the functions were taken from the
496    lib/silcclient/client_ops_example.c. */
497 SilcClientOperations ops = {
498   silc_say,
499   silc_channel_message,
500   silc_private_message,
501   silc_notify,
502   silc_command,
503   silc_command_reply,
504   silc_connected,
505   silc_disconnected,
506   silc_get_auth_method,
507   silc_verify_public_key,
508   silc_ask_passphrase,
509   silc_failure,
510   silc_key_agreement,
511   silc_ftp,
512   silc_detach
513 };
514
515 int main(int argc, char **argv)
516 {
517   /* Start the bot */
518   return mybot_start();
519 }