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