267c04a1962e8bb80fda2f5ed98b976603b90cbc
[silc.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   /* Start connecting to server.  This is asynchronous connecting so the
65      connection is actually created later after we run the client. */
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
302     (void)va_arg(ap, SilcClientEntry);
303     channel = va_arg(ap, 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, NULL,
309                                      "hello", strlen("hello"));
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, NULL,
315                                      "hello, with signature",
316                                      strlen("hello, with signature"));
317     fprintf(stdout, "MyBot: Sent 'hello, with signature' to channel\n");
318   }
319 }
320
321 /* Find authentication method and authentication data by hostname and
322    port. The hostname may be IP address as well. When the authentication
323    method has been resolved the `completion' callback with the found
324    authentication method and authentication data is called. The `conn'
325    may be NULL. */
326
327 static void
328 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
329                      char *hostname, SilcUInt16 port,
330                      SilcAuthMethod auth_method,
331                      SilcGetAuthMeth completion,
332                      void *context)
333 {
334   /* MyBot assumes that there is no authentication requirement in the
335      server and sends nothing as authentication.  We just reply with
336      TRUE, meaning we know what is the authentication method. :). */
337   completion(SILC_AUTH_NONE, NULL, 0, context);
338 }
339
340
341 /* Verifies received public key. The `conn_type' indicates which entity
342    (server, client etc.) has sent the public key. If user decides to trust
343    the application may save the key as trusted public key for later
344    use. The `completion' must be called after the public key has been
345    verified. */
346
347 static void
348 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
349                        SilcConnectionType conn_type,
350                        SilcPublicKey public_key,
351                        SilcVerifyPublicKey completion, void *context)
352 {
353   silc_show_public_key(public_key);
354   completion(TRUE, context);
355 }
356
357
358 /* Ask (interact, that is) a passphrase from user. The passphrase is
359    returned to the library by calling the `completion' callback with
360    the `context'. The returned passphrase SHOULD be in UTF-8 encoded,
361    if not then the library will attempt to encode. */
362
363 static void
364 silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
365                     SilcAskPassphrase completion, void *context)
366 {
367   /* MyBot does not support asking passphrases from users since there
368      is no user in our little client.  We just reply with nothing. */
369   completion(NULL, 0, context);
370 }
371
372
373 /* Asks whether the user would like to perform the key agreement protocol.
374    This is called after we have received an key agreement packet or an
375    reply to our key agreement packet. This returns TRUE if the user wants
376    the library to perform the key agreement protocol and FALSE if it is not
377    desired (application may start it later by calling the function
378    silc_client_perform_key_agreement). If TRUE is returned also the
379    `completion' and `context' arguments must be set by the application. */
380
381 static void
382 silc_key_agreement(SilcClient client, SilcClientConnection conn,
383                    SilcClientEntry client_entry, const char *hostname,
384                    SilcUInt16 protocol, SilcUInt16 port)
385 {
386   /* MyBot does not support incoming key agreement protocols, it's too
387      simple for that. */
388 }
389
390
391 /* Notifies application that file transfer protocol session is being
392    requested by the remote client indicated by the `client_entry' from
393    the `hostname' and `port'. The `session_id' is the file transfer
394    session and it can be used to either accept or reject the file
395    transfer request, by calling the silc_client_file_receive or
396    silc_client_file_close, respectively. */
397
398 static void
399 silc_ftp(SilcClient client, SilcClientConnection conn,
400          SilcClientEntry client_entry, SilcUInt32 session_id,
401          const char *hostname, SilcUInt16 port)
402 {
403   /* MyBot does not support file transfer, it's too simple for that too. */
404 }
405
406
407 /* Our client operations for the MyBot.  This structure is filled with
408    functions and given as argument to the silc_client_alloc function.
409    Even though our little bot does not need all these functions we must
410    provide them since the SILC Client Library wants them all. */
411 /* This structure and all the functions were taken from the
412    lib/silcclient/client_ops_example.c. */
413 SilcClientOperations ops = {
414   silc_say,
415   silc_channel_message,
416   silc_private_message,
417   silc_notify,
418   silc_command,
419   silc_command_reply,
420   silc_get_auth_method,
421   silc_verify_public_key,
422   silc_ask_passphrase,
423   silc_key_agreement,
424   silc_ftp
425 };
426
427 int main(int argc, char **argv)
428 {
429   if (argc > 1 && !strcmp(argv[1], "-d")) {
430     silc_log_debug(TRUE);
431     silc_log_debug_hexdump(TRUE);
432     silc_log_quick(TRUE);
433     silc_log_set_debug_string("*client*,*message*");
434   }
435
436   /* Start the bot */
437   mybot_start();
438
439   return 0;
440 }