Merged silc_1_1_branch to trunk.
[silc.git] / tutorial / mybot / mybot.c
1 /*
2
3   mybot.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>, November 2002, 2007
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 */
23
24 #include "silc.h"               /* Mandatory include for SILC applications */
25 #include "silcclient.h"         /* SILC Client Library API */
26
27 SilcClientOperations ops;
28
29 static void silc_running(SilcClient client, void *application);
30 static void silc_stopped(SilcClient client, void *context);
31
32 /******* MyBot code **********************************************************/
33
34 /* This is context for our MyBot client */
35 typedef struct {
36   SilcClient client;            /* The actual SILC Client */
37   SilcClientConnection conn;    /* Connection to the server */
38   SilcPublicKey public_key;     /* My public key */
39   SilcPrivateKey private_key;   /* My private key */
40 } *MyBot;
41
42 /* Connect callback called after connected to remote server. */
43
44 static void
45 silc_connected(SilcClient client, SilcClientConnection conn,
46                SilcClientConnectionStatus status,
47                SilcStatus error, const char *message,
48                void *context)
49 {
50   MyBot mybot = client->application;
51
52   if (status == SILC_CLIENT_CONN_DISCONNECTED) {
53     SILC_LOG_DEBUG(("Disconnected %s", message ? message : ""));
54     silc_client_stop(client, silc_stopped, mybot);
55     return;
56   }
57
58   if (status != SILC_CLIENT_CONN_SUCCESS &&
59       status != SILC_CLIENT_CONN_SUCCESS_RESUME) {
60     SILC_LOG_DEBUG(("Error connecting to server %d", status));
61     silc_client_stop(client, silc_stopped, mybot);
62     return;
63   }
64
65   fprintf(stdout, "\nMyBot: Connected to server\n\n");
66
67   /* Now that we have connected to server, let's join a channel named
68      "mybot". */
69   silc_client_command_call(client, conn, "JOIN mybot");
70
71   /* Save the connection context */
72   mybot->conn = conn;
73 }
74
75 /* Running callback given to silc_client_init called to indicate that the
76    Client Library is running.  After this Client API functions can be
77    called. */
78
79 static void silc_running(SilcClient client, void *application)
80 {
81   MyBot mybot = application;
82
83   SILC_LOG_DEBUG(("Client is running"));
84
85   /* Connect to server.  The silc_connected callback will be called after
86      the connection is established or if an error occurs during connecting. */
87   silc_client_connect_to_server(mybot->client, NULL,
88                                 mybot->public_key, mybot->private_key,
89                                 "silc.silcnet.org", 706,
90                                 silc_connected, mybot);
91 }
92
93 /* Client stopped callback given to silc_client_stop.  Called to indicate
94    that Client Library is stopped. */
95
96 static void silc_stopped(SilcClient client, void *context)
97 {
98   SILC_LOG_DEBUG(("Client stopped"));
99 }
100
101 /* Start the MyBot, by creating the SILC Client entity by using the
102    SILC Client Library API. */
103 int mybot_start(void)
104 {
105   MyBot mybot;
106   SilcClientParams params;
107
108   /* Allocate the MyBot structure */
109   mybot = silc_calloc(1, sizeof(*mybot));
110   if (!mybot) {
111     perror("Out of memory");
112     return 1;
113   }
114
115   memset(&params, 0, sizeof(params));
116   params.threads = TRUE;
117   mybot->client = silc_client_alloc(&ops, &params, mybot, NULL);
118   if (!mybot->client) {
119     perror("Could not allocate SILC Client");
120     return 1;
121   }
122
123   /* Now we initialize the client. */
124   if (!silc_client_init(mybot->client, silc_get_username(),
125                         silc_net_localhost(), "I am the MyBot",
126                         silc_running, mybot)) {
127     perror("Could not init client");
128     return 1;
129   }
130
131   if (!silc_load_key_pair("mybot.pub", "mybot.prv", "",
132                           &mybot->public_key,
133                           &mybot->private_key)) {
134     /* The keys don't exist.  Let's generate us a key pair then!  There's
135        nice ready routine for that too.  Let's do 2048 bit RSA key pair. */
136     fprintf(stdout, "MyBot: Key pair does not exist, generating it.\n");
137     if (!silc_create_key_pair("rsa", 2048, "mybot.pub", "mybot.prv", NULL, "",
138                               &mybot->public_key,
139                               &mybot->private_key, FALSE)) {
140       perror("Could not generated key pair");
141       return 1;
142     }
143   }
144
145   /* And, then we are ready to go.  Since we are really simple client we
146      don't have user interface and we don't have to deal with message loops
147      or interactivity.  That's why we can just hand over the execution
148      to the library by calling silc_client_run.  */
149   silc_client_run(mybot->client);
150
151   /* When we get here, we have quit the client, so clean up and exit */
152   silc_client_free(mybot->client);
153   silc_free(mybot);
154   return 0;
155 }
156
157 /******* SILC Client Operations **********************************************/
158
159 /* The SILC Client Library requires these "client operations".  They are
160    functions that the library may call at any time to indicate to application
161    that something happened, like message was received, or authentication
162    is required or something else.  Since our MyBot is really simple client
163    we don't need most of the operations, so we just define them and don't
164    do anything in them. */
165
166 /* "say" client operation is a message from the client library to the
167    application.  It may include error messages or something else.  We
168    just dump them to screen. */
169
170 static void
171 silc_say(SilcClient client, SilcClientConnection conn,
172          SilcClientMessageType type, char *msg, ...)
173 {
174   char str[200];
175   va_list va;
176   va_start(va, msg);
177   vsnprintf(str, sizeof(str) - 1, msg, va);
178   fprintf(stdout, "MyBot: %s\n", str);
179   va_end(va);
180 }
181
182
183 /* Message for a channel. The `sender' is the sender of the message
184    The `channel' is the channel. The `message' is the message.  Note
185    that `message' maybe NULL.  The `flags' indicates message flags
186    and it is used to determine how the message can be interpreted
187    (like it may tell the message is multimedia message). */
188
189 static void
190 silc_channel_message(SilcClient client, SilcClientConnection conn,
191                      SilcClientEntry sender, SilcChannelEntry channel,
192                      SilcMessagePayload payload,
193                      SilcChannelPrivateKey key,
194                      SilcMessageFlags flags, const unsigned char *message,
195                      SilcUInt32 message_len)
196 {
197   /* Yay! We got a message from channel. */
198
199   if (flags & SILC_MESSAGE_FLAG_SIGNED)
200     fprintf(stdout, "[SIGNED] <%s> %s\n", sender->nickname, message);
201   else
202     fprintf(stdout, "<%s> %s\n", sender->nickname, message);
203 }
204
205
206 /* Private message to the client. The `sender' is the sender of the
207    message. The message is `message'and maybe NULL.  The `flags'
208    indicates message flags  and it is used to determine how the message
209    can be interpreted (like it may tell the message is multimedia
210    message). */
211
212 static void
213 silc_private_message(SilcClient client, SilcClientConnection conn,
214                      SilcClientEntry sender, SilcMessagePayload payload,
215                      SilcMessageFlags flags,
216                      const unsigned char *message,
217                      SilcUInt32 message_len)
218 {
219   /* MyBot does not support private message receiving */
220 }
221
222
223 /* Notify message to the client. The notify arguments are sent in the
224    same order as servers sends them. The arguments are same as received
225    from the server except for ID's.  If ID is received application receives
226    the corresponding entry to the ID. For example, if Client ID is received
227    application receives SilcClientEntry.  Also, if the notify type is
228    for channel the channel entry is sent to application (even if server
229    does not send it because client library gets the channel entry from
230    the Channel ID in the packet's header). */
231
232 static void
233 silc_notify(SilcClient client, SilcClientConnection conn,
234             SilcNotifyType type, ...)
235 {
236   char *str;
237   va_list va;
238
239   va_start(va, type);
240
241   /* Here we can receive all kinds of different data from the server, but
242      our simple bot is interested only in receiving the "not-so-important"
243      stuff, just for fun. :) */
244   switch (type) {
245   case SILC_NOTIFY_TYPE_NONE:
246     /* Received something that we are just going to dump to screen. */
247     str = va_arg(va, char *);
248     fprintf(stdout, "--- %s\n", str);
249     break;
250
251   case SILC_NOTIFY_TYPE_MOTD:
252     /* Received the Message of the Day from the server. */
253     str = va_arg(va, char *);
254     fprintf(stdout, "%s", str);
255     fprintf(stdout, "\n");
256     break;
257
258   default:
259     /* Ignore rest */
260     break;
261   }
262
263   va_end(va);
264 }
265
266
267 /* Command handler. This function is called always in the command function.
268    If error occurs it will be called as well. `conn' is the associated
269    client connection. `cmd_context' is the command context that was
270    originally sent to the command. `success' is FALSE if error occurred
271    during command. `command' is the command being processed. It must be
272    noted that this is not reply from server. This is merely called just
273    after application has called the command. Just to tell application
274    that the command really was processed. */
275
276 static void
277 silc_command(SilcClient client, SilcClientConnection conn,
278              SilcBool success, SilcCommand command, SilcStatus status,
279              SilcUInt32 argc, unsigned char **argv)
280 {
281   /* If error occurred in client library with our command, print the error */
282   if (status != SILC_STATUS_OK)
283     fprintf(stderr, "MyBot: COMMAND %s: %s\n",
284             silc_get_command_name(command),
285             silc_get_status_message(status));
286 }
287
288
289 /* Command reply handler. This function is called always in the command reply
290    function. If error occurs it will be called as well. Normal scenario
291    is that it will be called after the received command data has been parsed
292    and processed. The function is used to pass the received command data to
293    the application.
294
295    `conn' is the associated client connection. `cmd_payload' is the command
296    payload data received from server and it can be ignored. It is provided
297    if the application would like to re-parse the received command data,
298    however, it must be noted that the data is parsed already by the library
299    thus the payload can be ignored. `success' is FALSE if error occurred.
300    In this case arguments are not sent to the application. The `status' is
301    the command reply status server returned. The `command' is the command
302    reply being processed. The function has variable argument list and each
303    command defines the number and type of arguments it passes to the
304    application (on error they are not sent). */
305
306 static void
307 silc_command_reply(SilcClient client, SilcClientConnection conn,
308                    SilcCommand command, SilcStatus status,
309                    SilcStatus error, va_list ap)
310 {
311   /* If error occurred in client library with our command, print the error */
312   if (status != SILC_STATUS_OK)
313     fprintf(stderr, "MyBot: COMMAND REPLY %s: %s\n",
314             silc_get_command_name(command),
315             silc_get_status_message(status));
316
317   /* Check for successful JOIN.  See
318      http://silcnet.org/docs/toolkit/command_reply_args.html for the
319      different arguments the client library returns. */
320   if (command == SILC_COMMAND_JOIN) {
321     SilcChannelEntry channel;
322     SilcHash sha1hash;
323
324     (void)va_arg(ap, SilcClientEntry);
325     channel = va_arg(ap, SilcChannelEntry);
326
327     fprintf(stdout, "MyBot: Joined '%s' channel\n", channel->channel_name);
328
329     /* Now send the "hello" to the channel */
330     silc_client_send_channel_message(client, conn, channel, NULL, 0, NULL,
331                                      "hello", strlen("hello"));
332     fprintf(stdout, "MyBot: Sent 'hello' to channel\n");
333
334     /* Now send digitally signed "hello" to the channel.  We have to allocate
335        hash function for the signature process. */
336     silc_hash_alloc("sha1", &sha1hash);
337     silc_client_send_channel_message(client, conn, channel, NULL,
338                                      SILC_MESSAGE_FLAG_SIGNED, sha1hash,
339                                      "hello, with signature",
340                                      strlen("hello, with signature"));
341     silc_hash_free(sha1hash);
342     fprintf(stdout, "MyBot: Sent 'hello, with signature' to channel\n");
343   }
344 }
345
346 /* Find authentication method and authentication data by hostname and
347    port. The hostname may be IP address as well. When the authentication
348    method has been resolved the `completion' callback with the found
349    authentication method and authentication data is called. The `conn'
350    may be NULL. */
351
352 static void
353 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
354                      char *hostname, SilcUInt16 port,
355                      SilcAuthMethod auth_method,
356                      SilcGetAuthMeth completion,
357                      void *context)
358 {
359   /* MyBot assumes that there is no authentication requirement in the
360      server and sends nothing as authentication.  We just reply with
361      TRUE, meaning we know what is the authentication method. :). */
362   completion(SILC_AUTH_NONE, NULL, 0, context);
363 }
364
365
366 /* Verifies received public key. The `conn_type' indicates which entity
367    (server, client etc.) has sent the public key. If user decides to trust
368    the application may save the key as trusted public key for later
369    use. The `completion' must be called after the public key has been
370    verified. */
371
372 static void
373 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
374                        SilcConnectionType conn_type,
375                        SilcPublicKey public_key,
376                        SilcVerifyPublicKey completion, void *context)
377 {
378   fprintf(stdout, "MyBot: server's public key\n");
379   silc_show_public_key(public_key);
380   completion(TRUE, context);
381 }
382
383
384 /* Ask (interact, that is) a passphrase from user. The passphrase is
385    returned to the library by calling the `completion' callback with
386    the `context'. The returned passphrase SHOULD be in UTF-8 encoded,
387    if not then the library will attempt to encode. */
388
389 static void
390 silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
391                     SilcAskPassphrase completion, void *context)
392 {
393   /* MyBot does not support asking passphrases from users since there
394      is no user in our little client.  We just reply with nothing. */
395   completion(NULL, 0, context);
396 }
397
398
399 /* Asks whether the user would like to perform the key agreement protocol.
400    This is called after we have received an key agreement packet or an
401    reply to our key agreement packet. This returns TRUE if the user wants
402    the library to perform the key agreement protocol and FALSE if it is not
403    desired (application may start it later by calling the function
404    silc_client_perform_key_agreement). If TRUE is returned also the
405    `completion' and `context' arguments must be set by the application. */
406
407 static void
408 silc_key_agreement(SilcClient client, SilcClientConnection conn,
409                    SilcClientEntry client_entry, const char *hostname,
410                    SilcUInt16 protocol, SilcUInt16 port)
411 {
412   /* MyBot does not support incoming key agreement protocols, it's too
413      simple for that. */
414 }
415
416
417 /* Notifies application that file transfer protocol session is being
418    requested by the remote client indicated by the `client_entry' from
419    the `hostname' and `port'. The `session_id' is the file transfer
420    session and it can be used to either accept or reject the file
421    transfer request, by calling the silc_client_file_receive or
422    silc_client_file_close, respectively. */
423
424 static void
425 silc_ftp(SilcClient client, SilcClientConnection conn,
426          SilcClientEntry client_entry, SilcUInt32 session_id,
427          const char *hostname, SilcUInt16 port)
428 {
429   /* MyBot does not support file transfer, it's too simple for that too. */
430 }
431
432
433 /* Our client operations for the MyBot.  This structure is filled with
434    functions and given as argument to the silc_client_alloc function.
435    Even though our little bot does not need all these functions we must
436    provide them since the SILC Client Library wants them all. */
437 /* This structure and all the functions were taken from the
438    lib/silcclient/client_ops_example.c. */
439 SilcClientOperations ops = {
440   silc_say,
441   silc_channel_message,
442   silc_private_message,
443   silc_notify,
444   silc_command,
445   silc_command_reply,
446   silc_get_auth_method,
447   silc_verify_public_key,
448   silc_ask_passphrase,
449   silc_key_agreement,
450   silc_ftp
451 };
452
453 int main(int argc, char **argv)
454 {
455   /* Start mybot */
456   return mybot_start();
457 }