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