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