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