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