Mercurial > pidgin.yaz
comparison src/protocols/silc/silc.c @ 8849:50d0f76639e7
[gaim-migrate @ 9616]
Let there be SILC.
committer: Tailor Script <tailor@pidgin.im>
author | Ethan Blanton <elb@pidgin.im> |
---|---|
date | Sat, 01 May 2004 19:34:44 +0000 |
parents | |
children | 4f7c365c5c5a |
comparison
equal
deleted
inserted
replaced
8848:56e6b9bdcdbc | 8849:50d0f76639e7 |
---|---|
1 /* | |
2 | |
3 silcgaim.c | |
4 | |
5 Author: Pekka Riikonen <priikone@silcnet.org> | |
6 | |
7 Copyright (C) 2004 Pekka Riikonen | |
8 | |
9 This program is free software; you can redistribute it and/or modify | |
10 it under the terms of the GNU General Public License as published by | |
11 the Free Software Foundation; version 2 of the License. | |
12 | |
13 This program is distributed in the hope that it will be useful, | |
14 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 GNU General Public License for more details. | |
17 | |
18 */ | |
19 | |
20 #include "silcincludes.h" | |
21 #include "silcclient.h" | |
22 #include "silcgaim.h" | |
23 | |
24 extern SilcClientOperations ops; | |
25 static GaimPlugin *silc_plugin = NULL; | |
26 | |
27 static const char * | |
28 silcgaim_list_icon(GaimAccount *a, GaimBuddy *b) | |
29 { | |
30 return (const char *)"silc"; | |
31 } | |
32 | |
33 static void | |
34 silcgaim_list_emblems(GaimBuddy *b, char **se, char **sw, | |
35 char **nw, char **ne) | |
36 { | |
37 } | |
38 | |
39 static GList * | |
40 silcgaim_away_states(GaimConnection *gc) | |
41 { | |
42 GList *st = NULL; | |
43 | |
44 st = g_list_append(st, _("Online")); | |
45 st = g_list_append(st, _("Hyper Active")); | |
46 st = g_list_append(st, _("Away")); | |
47 st = g_list_append(st, _("Busy")); | |
48 st = g_list_append(st, _("Indisposed")); | |
49 st = g_list_append(st, _("Wake Me Up")); | |
50 | |
51 return st; | |
52 } | |
53 | |
54 static void | |
55 silcgaim_set_away(GaimConnection *gc, const char *state, const char *msg) | |
56 { | |
57 SilcGaim sg = gc->proto_data; | |
58 SilcUInt32 mode; | |
59 SilcBuffer idp; | |
60 unsigned char mb[4]; | |
61 | |
62 if (!state) | |
63 return; | |
64 if (!sg->conn) | |
65 return; | |
66 | |
67 mode = sg->conn->local_entry->mode; | |
68 mode &= ~(SILC_UMODE_GONE | | |
69 SILC_UMODE_HYPER | | |
70 SILC_UMODE_BUSY | | |
71 SILC_UMODE_INDISPOSED | | |
72 SILC_UMODE_PAGE); | |
73 | |
74 if (!strcmp(state, _("Hyper Active"))) | |
75 mode |= SILC_UMODE_HYPER; | |
76 else if (!strcmp(state, _("Away"))) | |
77 mode |= SILC_UMODE_GONE; | |
78 else if (!strcmp(state, _("Busy"))) | |
79 mode |= SILC_UMODE_BUSY; | |
80 else if (!strcmp(state, _("Indisposed"))) | |
81 mode |= SILC_UMODE_INDISPOSED; | |
82 else if (!strcmp(state, _("Wake Me Up"))) | |
83 mode |= SILC_UMODE_PAGE; | |
84 | |
85 /* Send UMODE */ | |
86 idp = silc_id_payload_encode(sg->conn->local_id, SILC_ID_CLIENT); | |
87 SILC_PUT32_MSB(mode, mb); | |
88 silc_client_command_send(sg->client, sg->conn, SILC_COMMAND_UMODE, | |
89 ++sg->conn->cmd_ident, 2, | |
90 1, idp->data, idp->len, | |
91 2, mb, sizeof(mb)); | |
92 silc_buffer_free(idp); | |
93 } | |
94 | |
95 | |
96 /*************************** Connection Routines *****************************/ | |
97 | |
98 static void | |
99 silcgaim_keepalive(GaimConnection *gc) | |
100 { | |
101 SilcGaim sg = gc->proto_data; | |
102 silc_client_send_packet(sg->client, sg->conn, SILC_PACKET_HEARTBEAT, | |
103 NULL, 0); | |
104 } | |
105 | |
106 static int | |
107 silcgaim_scheduler(gpointer *context) | |
108 { | |
109 SilcGaim sg = (SilcGaim)context; | |
110 silc_client_run_one(sg->client); | |
111 return 1; | |
112 } | |
113 | |
114 static void | |
115 silcgaim_nickname_parse(const char *nickname, | |
116 char **ret_nickname) | |
117 { | |
118 silc_parse_userfqdn(nickname, ret_nickname, NULL); | |
119 } | |
120 | |
121 static void | |
122 silcgaim_login_connected(gpointer data, gint source, GaimInputCondition cond) | |
123 { | |
124 GaimConnection *gc = data; | |
125 SilcGaim sg = gc->proto_data; | |
126 SilcClient client = sg->client; | |
127 SilcClientConnection conn; | |
128 GaimAccount *account = sg->account; | |
129 SilcClientConnectionParams params; | |
130 const char *dfile; | |
131 | |
132 if (source < 0) { | |
133 gaim_connection_error(gc, _("Connection failed")); | |
134 return; | |
135 } | |
136 if (!g_list_find(gaim_connections_get_all(), gc)) { | |
137 close(source); | |
138 g_source_remove(sg->scheduler); | |
139 silc_client_stop(sg->client); | |
140 silc_client_free(sg->client); | |
141 silc_free(sg); | |
142 return; | |
143 } | |
144 | |
145 /* Get session detachment data, if available */ | |
146 memset(¶ms, 0, sizeof(params)); | |
147 dfile = silcgaim_session_file(gaim_account_get_username(sg->account)); | |
148 params.detach_data = silc_file_readfile(dfile, ¶ms.detach_data_len); | |
149 if (params.detach_data) | |
150 params.detach_data[params.detach_data_len] = 0; | |
151 | |
152 /* Add connection to SILC client library */ | |
153 conn = silc_client_add_connection( | |
154 sg->client, ¶ms, | |
155 (char *)gaim_account_get_string(account, "server", | |
156 "silc.silcnet.org"), | |
157 gaim_account_get_int(account, "port", 706), sg); | |
158 if (!conn) { | |
159 gaim_connection_error(gc, _("Cannot initialize SILC Client connection")); | |
160 gc->proto_data = NULL; | |
161 return; | |
162 } | |
163 sg->conn = conn; | |
164 | |
165 /* Progress */ | |
166 if (params.detach_data) { | |
167 gaim_connection_update_progress(gc, _("Resuming session"), 2, 5); | |
168 sg->resuming = TRUE; | |
169 } else { | |
170 gaim_connection_update_progress(gc, _("Performing key exchange"), 2, 5); | |
171 } | |
172 | |
173 /* Perform SILC Key Exchange. The "silc_connected" will be called | |
174 eventually. */ | |
175 silc_client_start_key_exchange(sg->client, sg->conn, source); | |
176 | |
177 /* Set default attributes */ | |
178 if (!gaim_account_get_bool(account, "reject-attrs", FALSE)) { | |
179 SilcUInt32 mask; | |
180 const char *tmp; | |
181 #ifdef HAVE_SYS_UTSNAME_H | |
182 struct utsname u; | |
183 #endif | |
184 | |
185 mask = SILC_ATTRIBUTE_MOOD_NORMAL; | |
186 silc_client_attribute_add(client, conn, | |
187 SILC_ATTRIBUTE_STATUS_MOOD, | |
188 SILC_32_TO_PTR(mask), | |
189 sizeof(SilcUInt32)); | |
190 mask = SILC_ATTRIBUTE_CONTACT_CHAT; | |
191 silc_client_attribute_add(client, conn, | |
192 SILC_ATTRIBUTE_PREFERRED_CONTACT, | |
193 SILC_32_TO_PTR(mask), | |
194 sizeof(SilcUInt32)); | |
195 #ifdef HAVE_SYS_UTSNAME_H | |
196 if (!uname(&u)) { | |
197 SilcAttributeObjDevice dev; | |
198 memset(&dev, 0, sizeof(dev)); | |
199 dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER; | |
200 dev.version = u.release; | |
201 dev.model = u.sysname; | |
202 silc_client_attribute_add(client, conn, | |
203 SILC_ATTRIBUTE_DEVICE_INFO, | |
204 (void *)&dev, sizeof(dev)); | |
205 } | |
206 #endif | |
207 #ifdef _WIN32 | |
208 tmp = _tzname[0]; | |
209 #else | |
210 tmp = tzname[0]; | |
211 #endif | |
212 silc_client_attribute_add(client, conn, | |
213 SILC_ATTRIBUTE_TIMEZONE, | |
214 (void *)tmp, strlen(tmp)); | |
215 } | |
216 | |
217 silc_free(params.detach_data); | |
218 } | |
219 | |
220 static void | |
221 silcgaim_login(GaimAccount *account) | |
222 { | |
223 SilcGaim sg; | |
224 SilcClient client; | |
225 SilcClientParams params; | |
226 GaimConnection *gc; | |
227 | |
228 gc = account->gc; | |
229 if (!gc) | |
230 return; | |
231 gc->proto_data = NULL; | |
232 | |
233 memset(¶ms, 0, sizeof(params)); | |
234 strcat(params.nickname_format, "%n@%h%a"); | |
235 params.nickname_parse = silcgaim_nickname_parse; | |
236 params.ignore_requested_attributes = | |
237 gaim_account_get_bool(account, "reject-attrs", FALSE); | |
238 | |
239 /* Allocate SILC client */ | |
240 client = silc_client_alloc(&ops, ¶ms, gc, NULL); | |
241 if (!client) { | |
242 gaim_connection_error(gc, _("Out of memory")); | |
243 return; | |
244 } | |
245 | |
246 /* Get username, real name and local hostname for SILC library */ | |
247 if (gaim_account_get_username(account)) { | |
248 client->username = strdup(gaim_account_get_username(account)); | |
249 } else { | |
250 client->username = silc_get_username(); | |
251 gaim_account_set_username(account, client->username); | |
252 } | |
253 if (gaim_account_get_user_info(account)) { | |
254 client->realname = strdup(gaim_account_get_user_info(account)); | |
255 } else { | |
256 client->realname = silc_get_real_name(); | |
257 gaim_account_set_user_info(account, client->realname); | |
258 } | |
259 client->hostname = silc_net_localhost(); | |
260 | |
261 gaim_connection_set_display_name(gc, client->username); | |
262 | |
263 /* Init SILC client */ | |
264 if (!silc_client_init(client)) { | |
265 gaim_connection_error(gc, ("Cannot initialize SILC protocol")); | |
266 return; | |
267 } | |
268 | |
269 /* Check the ~/.silc dir and create it, and new key pair if necessary. */ | |
270 if (!silcgaim_check_silc_dir(gc)) { | |
271 gaim_connection_error(gc, ("Cannot find/access ~/.silc directory")); | |
272 return; | |
273 } | |
274 | |
275 /* Progress */ | |
276 gaim_connection_update_progress(gc, _("Connecting to SILC Server"), 1, 5); | |
277 | |
278 /* Load SILC key pair */ | |
279 if (!silc_load_key_pair(gaim_prefs_get_string("/plugins/prpl/silc/pubkey"), | |
280 gaim_prefs_get_string("/plugins/prpl/silc/privkey"), | |
281 "", &client->pkcs, &client->public_key, | |
282 &client->private_key)) { | |
283 gaim_connection_error(gc, ("Could not load SILC key pair")); | |
284 return; | |
285 } | |
286 | |
287 sg = silc_calloc(1, sizeof(*sg)); | |
288 if (!sg) | |
289 return; | |
290 memset(sg, 0, sizeof(*sg)); | |
291 sg->client = client; | |
292 sg->gc = gc; | |
293 sg->account = account; | |
294 gc->proto_data = sg; | |
295 | |
296 /* Connect to the SILC server */ | |
297 if (gaim_proxy_connect(account, | |
298 gaim_account_get_string(account, "server", | |
299 "silc.silcnet.org"), | |
300 gaim_account_get_int(account, "port", 706), | |
301 silcgaim_login_connected, gc)) { | |
302 gaim_connection_error(gc, ("Unable to create connection")); | |
303 return; | |
304 } | |
305 | |
306 /* Schedule SILC using Glib's event loop */ | |
307 sg->scheduler = g_timeout_add(5, (GSourceFunc)silcgaim_scheduler, sg); | |
308 } | |
309 | |
310 static int | |
311 silcgaim_close_final(gpointer *context) | |
312 { | |
313 SilcGaim sg = (SilcGaim)context; | |
314 silc_client_stop(sg->client); | |
315 silc_client_free(sg->client); | |
316 silc_free(sg); | |
317 return 0; | |
318 } | |
319 | |
320 static void | |
321 silcgaim_close_convos(GaimConversation *convo) | |
322 { | |
323 if (convo) | |
324 gaim_conversation_destroy(convo); | |
325 } | |
326 | |
327 static void | |
328 silcgaim_close(GaimConnection *gc) | |
329 { | |
330 SilcGaim sg = gc->proto_data; | |
331 if (!sg) | |
332 return; | |
333 | |
334 /* Close all conversations */ | |
335 gaim_conversation_foreach(silcgaim_close_convos); | |
336 | |
337 /* Send QUIT */ | |
338 silc_client_command_call(sg->client, sg->conn, NULL, | |
339 "QUIT", "Leaving", NULL); | |
340 | |
341 if (sg->conn) | |
342 silc_client_close_connection(sg->client, sg->conn); | |
343 | |
344 g_source_remove(sg->scheduler); | |
345 g_timeout_add(1, (GSourceFunc)silcgaim_close_final, sg); | |
346 } | |
347 | |
348 | |
349 /****************************** Protocol Actions *****************************/ | |
350 | |
351 static void | |
352 silcgaim_attrs_cancel(GaimConnection *gc, GaimRequestFields *fields) | |
353 { | |
354 /* Nothing */ | |
355 } | |
356 | |
357 static void | |
358 silcgaim_attrs_cb(GaimConnection *gc, GaimRequestFields *fields) | |
359 { | |
360 SilcGaim sg = gc->proto_data; | |
361 SilcClient client = sg->client; | |
362 SilcClientConnection conn = sg->conn; | |
363 GaimRequestField *f; | |
364 char *tmp; | |
365 SilcUInt32 tmp_len, mask; | |
366 SilcAttributeObjService service; | |
367 SilcAttributeObjDevice dev; | |
368 SilcVCardStruct vcard; | |
369 const char *val; | |
370 | |
371 sg = gc->proto_data; | |
372 if (!sg) | |
373 return; | |
374 | |
375 memset(&service, 0, sizeof(service)); | |
376 memset(&dev, 0, sizeof(dev)); | |
377 memset(&vcard, 0, sizeof(vcard)); | |
378 | |
379 silc_client_attribute_del(client, conn, | |
380 SILC_ATTRIBUTE_USER_INFO, NULL); | |
381 silc_client_attribute_del(client, conn, | |
382 SILC_ATTRIBUTE_SERVICE, NULL); | |
383 silc_client_attribute_del(client, conn, | |
384 SILC_ATTRIBUTE_STATUS_MOOD, NULL); | |
385 silc_client_attribute_del(client, conn, | |
386 SILC_ATTRIBUTE_STATUS_FREETEXT, NULL); | |
387 silc_client_attribute_del(client, conn, | |
388 SILC_ATTRIBUTE_STATUS_MESSAGE, NULL); | |
389 silc_client_attribute_del(client, conn, | |
390 SILC_ATTRIBUTE_PREFERRED_LANGUAGE, NULL); | |
391 silc_client_attribute_del(client, conn, | |
392 SILC_ATTRIBUTE_PREFERRED_CONTACT, NULL); | |
393 silc_client_attribute_del(client, conn, | |
394 SILC_ATTRIBUTE_TIMEZONE, NULL); | |
395 silc_client_attribute_del(client, conn, | |
396 SILC_ATTRIBUTE_GEOLOCATION, NULL); | |
397 silc_client_attribute_del(client, conn, | |
398 SILC_ATTRIBUTE_DEVICE_INFO, NULL); | |
399 | |
400 /* Set mood */ | |
401 mask = 0; | |
402 f = gaim_request_fields_get_field(fields, "mood_normal"); | |
403 if (f && gaim_request_field_bool_get_value(f)) | |
404 mask |= SILC_ATTRIBUTE_MOOD_NORMAL; | |
405 f = gaim_request_fields_get_field(fields, "mood_happy"); | |
406 if (f && gaim_request_field_bool_get_value(f)) | |
407 mask |= SILC_ATTRIBUTE_MOOD_HAPPY; | |
408 f = gaim_request_fields_get_field(fields, "mood_sad"); | |
409 if (f && gaim_request_field_bool_get_value(f)) | |
410 mask |= SILC_ATTRIBUTE_MOOD_SAD; | |
411 f = gaim_request_fields_get_field(fields, "mood_angry"); | |
412 if (f && gaim_request_field_bool_get_value(f)) | |
413 mask |= SILC_ATTRIBUTE_MOOD_ANGRY; | |
414 f = gaim_request_fields_get_field(fields, "mood_jealous"); | |
415 if (f && gaim_request_field_bool_get_value(f)) | |
416 mask |= SILC_ATTRIBUTE_MOOD_JEALOUS; | |
417 f = gaim_request_fields_get_field(fields, "mood_ashamed"); | |
418 if (f && gaim_request_field_bool_get_value(f)) | |
419 mask |= SILC_ATTRIBUTE_MOOD_ASHAMED; | |
420 f = gaim_request_fields_get_field(fields, "mood_invincible"); | |
421 if (f && gaim_request_field_bool_get_value(f)) | |
422 mask |= SILC_ATTRIBUTE_MOOD_INVINCIBLE; | |
423 f = gaim_request_fields_get_field(fields, "mood_inlove"); | |
424 if (f && gaim_request_field_bool_get_value(f)) | |
425 mask |= SILC_ATTRIBUTE_MOOD_INLOVE; | |
426 f = gaim_request_fields_get_field(fields, "mood_sleepy"); | |
427 if (f && gaim_request_field_bool_get_value(f)) | |
428 mask |= SILC_ATTRIBUTE_MOOD_SLEEPY; | |
429 f = gaim_request_fields_get_field(fields, "mood_bored"); | |
430 if (f && gaim_request_field_bool_get_value(f)) | |
431 mask |= SILC_ATTRIBUTE_MOOD_BORED; | |
432 f = gaim_request_fields_get_field(fields, "mood_excited"); | |
433 if (f && gaim_request_field_bool_get_value(f)) | |
434 mask |= SILC_ATTRIBUTE_MOOD_EXCITED; | |
435 f = gaim_request_fields_get_field(fields, "mood_anxious"); | |
436 if (f && gaim_request_field_bool_get_value(f)) | |
437 mask |= SILC_ATTRIBUTE_MOOD_ANXIOUS; | |
438 silc_client_attribute_add(client, conn, | |
439 SILC_ATTRIBUTE_STATUS_MOOD, | |
440 SILC_32_TO_PTR(mask), | |
441 sizeof(SilcUInt32)); | |
442 | |
443 /* Set preferred contact */ | |
444 mask = 0; | |
445 f = gaim_request_fields_get_field(fields, "contact_chat"); | |
446 if (f && gaim_request_field_bool_get_value(f)) | |
447 mask |= SILC_ATTRIBUTE_CONTACT_CHAT; | |
448 f = gaim_request_fields_get_field(fields, "contact_email"); | |
449 if (f && gaim_request_field_bool_get_value(f)) | |
450 mask |= SILC_ATTRIBUTE_CONTACT_EMAIL; | |
451 f = gaim_request_fields_get_field(fields, "contact_call"); | |
452 if (f && gaim_request_field_bool_get_value(f)) | |
453 mask |= SILC_ATTRIBUTE_CONTACT_CALL; | |
454 f = gaim_request_fields_get_field(fields, "contact_sms"); | |
455 if (f && gaim_request_field_bool_get_value(f)) | |
456 mask |= SILC_ATTRIBUTE_CONTACT_SMS; | |
457 f = gaim_request_fields_get_field(fields, "contact_mms"); | |
458 if (f && gaim_request_field_bool_get_value(f)) | |
459 mask |= SILC_ATTRIBUTE_CONTACT_MMS; | |
460 f = gaim_request_fields_get_field(fields, "contact_video"); | |
461 if (f && gaim_request_field_bool_get_value(f)) | |
462 mask |= SILC_ATTRIBUTE_CONTACT_VIDEO; | |
463 if (mask) | |
464 silc_client_attribute_add(client, conn, | |
465 SILC_ATTRIBUTE_PREFERRED_CONTACT, | |
466 SILC_32_TO_PTR(mask), | |
467 sizeof(SilcUInt32)); | |
468 | |
469 /* Set status text */ | |
470 val = NULL; | |
471 f = gaim_request_fields_get_field(fields, "status_text"); | |
472 if (f) | |
473 val = gaim_request_field_string_get_value(f); | |
474 if (val && *val) | |
475 silc_client_attribute_add(client, conn, | |
476 SILC_ATTRIBUTE_STATUS_FREETEXT, | |
477 (void *)val, strlen(val)); | |
478 | |
479 /* Set vcard */ | |
480 val = NULL; | |
481 f = gaim_request_fields_get_field(fields, "vcard"); | |
482 if (f) | |
483 val = gaim_request_field_string_get_value(f); | |
484 if (val && *val) { | |
485 gaim_prefs_set_string("/plugins/prpl/silc/vcard", val); | |
486 gaim_prefs_sync(); | |
487 tmp = silc_file_readfile(val, &tmp_len); | |
488 if (tmp) { | |
489 tmp[tmp_len] = 0; | |
490 if (silc_vcard_decode(tmp, tmp_len, &vcard)) | |
491 silc_client_attribute_add(client, conn, | |
492 SILC_ATTRIBUTE_USER_INFO, | |
493 (void *)&vcard, | |
494 sizeof(vcard)); | |
495 } | |
496 silc_vcard_free(&vcard); | |
497 silc_free(tmp); | |
498 } | |
499 | |
500 #ifdef HAVE_SYS_UTSNAME_H | |
501 /* Set device info */ | |
502 f = gaim_request_fields_get_field(fields, "device"); | |
503 if (f && gaim_request_field_bool_get_value(f)) { | |
504 struct utsname u; | |
505 if (!uname(&u)) { | |
506 dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER; | |
507 dev.version = u.release; | |
508 dev.model = u.sysname; | |
509 silc_client_attribute_add(client, conn, | |
510 SILC_ATTRIBUTE_DEVICE_INFO, | |
511 (void *)&dev, sizeof(dev)); | |
512 } | |
513 } | |
514 #endif | |
515 | |
516 /* Set timezone */ | |
517 val = NULL; | |
518 f = gaim_request_fields_get_field(fields, "timezone"); | |
519 if (f) | |
520 val = gaim_request_field_string_get_value(f); | |
521 if (val && *val) | |
522 silc_client_attribute_add(client, conn, | |
523 SILC_ATTRIBUTE_TIMEZONE, | |
524 (void *)val, strlen(val)); | |
525 } | |
526 | |
527 static void | |
528 silcgaim_attrs(GaimConnection *gc) | |
529 { | |
530 SilcGaim sg = gc->proto_data; | |
531 SilcClient client = sg->client; | |
532 SilcClientConnection conn = sg->conn; | |
533 GaimRequestFields *fields; | |
534 GaimRequestFieldGroup *g; | |
535 GaimRequestField *f; | |
536 SilcHashTable attrs; | |
537 SilcAttributePayload attr; | |
538 gboolean mnormal = TRUE, mhappy = FALSE, msad = FALSE, | |
539 mangry = FALSE, mjealous = FALSE, mashamed = FALSE, | |
540 minvincible = FALSE, minlove = FALSE, msleepy = FALSE, | |
541 mbored = FALSE, mexcited = FALSE, manxious = FALSE; | |
542 gboolean cemail = FALSE, ccall = FALSE, csms = FALSE, | |
543 cmms = FALSE, cchat = TRUE, cvideo = FALSE; | |
544 gboolean device = TRUE; | |
545 char status[1024]; | |
546 | |
547 sg = gc->proto_data; | |
548 if (!sg) | |
549 return; | |
550 | |
551 memset(status, 0, sizeof(status)); | |
552 | |
553 attrs = silc_client_attributes_get(client, conn); | |
554 if (attrs) { | |
555 if (silc_hash_table_find(attrs, | |
556 SILC_32_TO_PTR(SILC_ATTRIBUTE_STATUS_MOOD), | |
557 NULL, (void *)&attr)) { | |
558 SilcUInt32 mood = 0; | |
559 silc_attribute_get_object(attr, &mood, sizeof(mood)); | |
560 mnormal = !mood; | |
561 mhappy = (mood & SILC_ATTRIBUTE_MOOD_HAPPY); | |
562 msad = (mood & SILC_ATTRIBUTE_MOOD_SAD); | |
563 mangry = (mood & SILC_ATTRIBUTE_MOOD_ANGRY); | |
564 mjealous = (mood & SILC_ATTRIBUTE_MOOD_JEALOUS); | |
565 mashamed = (mood & SILC_ATTRIBUTE_MOOD_ASHAMED); | |
566 minvincible = (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE); | |
567 minlove = (mood & SILC_ATTRIBUTE_MOOD_INLOVE); | |
568 msleepy = (mood & SILC_ATTRIBUTE_MOOD_SLEEPY); | |
569 mbored = (mood & SILC_ATTRIBUTE_MOOD_BORED); | |
570 mexcited = (mood & SILC_ATTRIBUTE_MOOD_EXCITED); | |
571 manxious = (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS); | |
572 } | |
573 | |
574 if (silc_hash_table_find(attrs, | |
575 SILC_32_TO_PTR(SILC_ATTRIBUTE_PREFERRED_CONTACT), | |
576 NULL, (void *)&attr)) { | |
577 SilcUInt32 contact = 0; | |
578 silc_attribute_get_object(attr, &contact, sizeof(contact)); | |
579 cemail = (contact & SILC_ATTRIBUTE_CONTACT_EMAIL); | |
580 ccall = (contact & SILC_ATTRIBUTE_CONTACT_CALL); | |
581 csms = (contact & SILC_ATTRIBUTE_CONTACT_SMS); | |
582 cmms = (contact & SILC_ATTRIBUTE_CONTACT_MMS); | |
583 cchat = (contact & SILC_ATTRIBUTE_CONTACT_CHAT); | |
584 cvideo = (contact & SILC_ATTRIBUTE_CONTACT_VIDEO); | |
585 } | |
586 | |
587 if (silc_hash_table_find(attrs, | |
588 SILC_32_TO_PTR(SILC_ATTRIBUTE_STATUS_FREETEXT), | |
589 NULL, (void *)&attr)) | |
590 silc_attribute_get_object(attr, &status, sizeof(status)); | |
591 | |
592 if (!silc_hash_table_find(attrs, | |
593 SILC_32_TO_PTR(SILC_ATTRIBUTE_DEVICE_INFO), | |
594 NULL, (void *)&attr)) | |
595 device = FALSE; | |
596 } | |
597 | |
598 fields = gaim_request_fields_new(); | |
599 | |
600 g = gaim_request_field_group_new(NULL); | |
601 f = gaim_request_field_label_new("l3", _("Your Current Mood")); | |
602 gaim_request_field_group_add_field(g, f); | |
603 f = gaim_request_field_bool_new("mood_normal", _("Normal"), mnormal); | |
604 gaim_request_field_group_add_field(g, f); | |
605 f = gaim_request_field_bool_new("mood_happy", _("Happy"), mhappy); | |
606 gaim_request_field_group_add_field(g, f); | |
607 f = gaim_request_field_bool_new("mood_sad", _("Sad"), msad); | |
608 gaim_request_field_group_add_field(g, f); | |
609 f = gaim_request_field_bool_new("mood_angry", _("Angry"), mangry); | |
610 gaim_request_field_group_add_field(g, f); | |
611 f = gaim_request_field_bool_new("mood_jealous", _("Jealous"), mjealous); | |
612 gaim_request_field_group_add_field(g, f); | |
613 f = gaim_request_field_bool_new("mood_ashamed", _("Ashamed"), mashamed); | |
614 gaim_request_field_group_add_field(g, f); | |
615 f = gaim_request_field_bool_new("mood_invincible", _("Invincible"), minvincible); | |
616 gaim_request_field_group_add_field(g, f); | |
617 f = gaim_request_field_bool_new("mood_inlove", _("In Love"), minlove); | |
618 gaim_request_field_group_add_field(g, f); | |
619 f = gaim_request_field_bool_new("mood_sleepy", _("Sleepy"), msleepy); | |
620 gaim_request_field_group_add_field(g, f); | |
621 f = gaim_request_field_bool_new("mood_bored", _("Bored"), mbored); | |
622 gaim_request_field_group_add_field(g, f); | |
623 f = gaim_request_field_bool_new("mood_excited", _("Excited"), mexcited); | |
624 gaim_request_field_group_add_field(g, f); | |
625 f = gaim_request_field_bool_new("mood_anxious", _("Anxious"), manxious); | |
626 gaim_request_field_group_add_field(g, f); | |
627 | |
628 f = gaim_request_field_label_new("l4", _("\nYour Preferred Contact Methods")); | |
629 gaim_request_field_group_add_field(g, f); | |
630 f = gaim_request_field_bool_new("contact_chat", _("Chat"), cchat); | |
631 gaim_request_field_group_add_field(g, f); | |
632 f = gaim_request_field_bool_new("contact_email", _("Email"), cemail); | |
633 gaim_request_field_group_add_field(g, f); | |
634 f = gaim_request_field_bool_new("contact_call", _("Phone"), ccall); | |
635 gaim_request_field_group_add_field(g, f); | |
636 f = gaim_request_field_bool_new("contact_sms", _("SMS"), csms); | |
637 gaim_request_field_group_add_field(g, f); | |
638 f = gaim_request_field_bool_new("contact_mms", _("MMS"), cmms); | |
639 gaim_request_field_group_add_field(g, f); | |
640 f = gaim_request_field_bool_new("contact_video", _("Video Conferencing"), cvideo); | |
641 gaim_request_field_group_add_field(g, f); | |
642 gaim_request_fields_add_group(fields, g); | |
643 | |
644 g = gaim_request_field_group_new(NULL); | |
645 f = gaim_request_field_string_new("status_text", _("Your Current Status"), | |
646 status[0] ? status : NULL, TRUE); | |
647 gaim_request_field_group_add_field(g, f); | |
648 gaim_request_fields_add_group(fields, g); | |
649 | |
650 g = gaim_request_field_group_new(NULL); | |
651 #if 0 | |
652 f = gaim_request_field_label_new("l2", _("Online Services")); | |
653 gaim_request_field_group_add_field(g, f); | |
654 f = gaim_request_field_bool_new("services", | |
655 _("Let others see what services you are using"), | |
656 TRUE); | |
657 gaim_request_field_group_add_field(g, f); | |
658 #endif | |
659 #ifdef HAVE_SYS_UTSNAME_H | |
660 f = gaim_request_field_bool_new("device", | |
661 _("Let others see what computer you are using"), | |
662 device); | |
663 gaim_request_field_group_add_field(g, f); | |
664 #endif | |
665 gaim_request_fields_add_group(fields, g); | |
666 | |
667 g = gaim_request_field_group_new(NULL); | |
668 f = gaim_request_field_string_new("vcard", _("Your VCard File"), | |
669 gaim_prefs_get_string("/plugins/prpl/silc/vcard"), | |
670 FALSE); | |
671 gaim_request_field_group_add_field(g, f); | |
672 #ifdef _WIN32 | |
673 f = gaim_request_field_string_new("timezone", _("Timezone"), _tzname[0], FALSE); | |
674 #else | |
675 f = gaim_request_field_string_new("timezone", _("Timezone"), tzname[0], FALSE); | |
676 #endif | |
677 gaim_request_field_group_add_field(g, f); | |
678 gaim_request_fields_add_group(fields, g); | |
679 | |
680 | |
681 gaim_request_fields(NULL, _("User Online Status Attributes"), | |
682 _("User Online Status Attributes"), | |
683 _("You can let other users see your online status information " | |
684 "and your personal information. Please fill the information " | |
685 "you would like other users to see about yourself."), | |
686 fields, | |
687 "OK", G_CALLBACK(silcgaim_attrs_cb), | |
688 "Cancel", G_CALLBACK(silcgaim_attrs_cancel), gc); | |
689 } | |
690 | |
691 static void | |
692 silcgaim_detach(GaimConnection *gc) | |
693 { | |
694 SilcGaim sg; | |
695 | |
696 if (!gc) | |
697 return; | |
698 sg = gc->proto_data; | |
699 if (!sg) | |
700 return; | |
701 | |
702 /* Call DETACH */ | |
703 silc_client_command_call(sg->client, sg->conn, "DETACH"); | |
704 sg->detaching = TRUE; | |
705 } | |
706 | |
707 static void | |
708 silcgaim_view_motd(GaimConnection *gc) | |
709 { | |
710 SilcGaim sg; | |
711 | |
712 if (!gc) | |
713 return; | |
714 sg = gc->proto_data; | |
715 if (!sg) | |
716 return; | |
717 | |
718 if (!sg->motd) { | |
719 gaim_notify_error( | |
720 gc, _("Message of the Day"), _("No Message of the Day available"), | |
721 _("There is no Message of the Day associated with this connection")); | |
722 return; | |
723 } | |
724 | |
725 gaim_notify_formatted(gc, "Message of the Day", "Message of the Day", NULL, | |
726 sg->motd, NULL, NULL); | |
727 } | |
728 | |
729 static GList * | |
730 silcgaim_actions(GaimConnection *gc) | |
731 { | |
732 struct proto_actions_menu *pam; | |
733 GList *list = NULL; | |
734 | |
735 if (!gaim_account_get_bool(gc->account, "reject-attrs", FALSE)) { | |
736 pam = g_new0(struct proto_actions_menu, 1); | |
737 pam->label = _("Online Status"); | |
738 pam->callback = silcgaim_attrs; | |
739 pam->gc = gc; | |
740 list = g_list_append(list, pam); | |
741 } | |
742 | |
743 pam = g_new0(struct proto_actions_menu, 1); | |
744 pam->label = _("Detach From Server"); | |
745 pam->callback = silcgaim_detach; | |
746 pam->gc = gc; | |
747 list = g_list_append(list, pam); | |
748 | |
749 pam = g_new0(struct proto_actions_menu, 1); | |
750 pam->label = _("View Message of the Day"); | |
751 pam->callback = silcgaim_view_motd; | |
752 pam->gc = gc; | |
753 list = g_list_append(list, pam); | |
754 | |
755 return list; | |
756 } | |
757 | |
758 | |
759 /******************************* IM Routines *********************************/ | |
760 | |
761 typedef struct { | |
762 char *nick; | |
763 unsigned char *message; | |
764 SilcUInt32 message_len; | |
765 SilcMessageFlags flags; | |
766 } *SilcGaimIM; | |
767 | |
768 static void | |
769 silcgaim_send_im_resolved(SilcClient client, | |
770 SilcClientConnection conn, | |
771 SilcClientEntry *clients, | |
772 SilcUInt32 clients_count, | |
773 void *context) | |
774 { | |
775 GaimConnection *gc = client->application; | |
776 SilcGaim sg = gc->proto_data; | |
777 SilcGaimIM im = context; | |
778 GaimConversation *convo; | |
779 char tmp[256], *nickname = NULL; | |
780 SilcClientEntry client_entry; | |
781 | |
782 convo = gaim_find_conversation_with_account(im->nick, sg->account); | |
783 if (!convo) | |
784 return; | |
785 | |
786 if (!clients) | |
787 goto err; | |
788 | |
789 if (clients_count > 1) { | |
790 silc_parse_userfqdn(im->nick, &nickname, NULL); | |
791 | |
792 /* Find the correct one. The im->nick might be a formatted nick | |
793 so this will find the correct one. */ | |
794 clients = silc_client_get_clients_local(client, conn, | |
795 nickname, im->nick, | |
796 &clients_count); | |
797 if (!clients) | |
798 goto err; | |
799 client_entry = clients[0]; | |
800 silc_free(clients); | |
801 } else { | |
802 client_entry = clients[0]; | |
803 } | |
804 | |
805 /* Send the message */ | |
806 silc_client_send_private_message(client, conn, client_entry, im->flags, | |
807 im->message, im->message_len, TRUE); | |
808 gaim_conv_im_write(GAIM_CONV_IM(convo), conn->local_entry->nickname, | |
809 im->message, 0, time(NULL)); | |
810 | |
811 goto out; | |
812 | |
813 err: | |
814 g_snprintf(tmp, sizeof(tmp), | |
815 _("User <I>%s</I> is not present in the network"), im->nick); | |
816 gaim_conversation_write(convo, NULL, tmp, GAIM_MESSAGE_SYSTEM, time(NULL)); | |
817 | |
818 out: | |
819 g_free(im->nick); | |
820 g_free(im->message); | |
821 silc_free(im); | |
822 silc_free(nickname); | |
823 } | |
824 | |
825 static int | |
826 silcgaim_send_im(GaimConnection *gc, const char *who, const char *msg, | |
827 GaimConvImFlags flags) | |
828 { | |
829 SilcGaim sg = gc->proto_data; | |
830 SilcClient client = sg->client; | |
831 SilcClientConnection conn = sg->conn; | |
832 SilcClientEntry *clients; | |
833 SilcUInt32 clients_count, mflags; | |
834 char *nickname; | |
835 int ret; | |
836 gboolean sign = gaim_prefs_get_bool("/plugins/prpl/silc/sign_im"); | |
837 | |
838 if (!who || !msg) | |
839 return 0; | |
840 | |
841 /* See if command */ | |
842 if (strlen(msg) > 1 && msg[0] == '/') { | |
843 if (!silc_client_command_call(client, conn, msg + 1)) | |
844 gaim_notify_error(gc, ("Call Command"), _("Cannot call command"), | |
845 _("Unknown command")); | |
846 return 0; | |
847 } | |
848 | |
849 if (!silc_parse_userfqdn(who, &nickname, NULL)) | |
850 return 0; | |
851 | |
852 mflags = SILC_MESSAGE_FLAG_UTF8; | |
853 if (sign) | |
854 mflags |= SILC_MESSAGE_FLAG_SIGNED; | |
855 | |
856 /* Find client entry */ | |
857 clients = silc_client_get_clients_local(client, conn, nickname, who, | |
858 &clients_count); | |
859 if (!clients) { | |
860 /* Resolve unknown user */ | |
861 SilcGaimIM im = silc_calloc(1, sizeof(*im)); | |
862 if (!im) | |
863 return 0; | |
864 im->nick = g_strdup(who); | |
865 im->message = g_strdup(msg); | |
866 im->message_len = strlen(im->message); | |
867 im->flags = mflags; | |
868 silc_client_get_clients(client, conn, nickname, NULL, | |
869 silcgaim_send_im_resolved, im); | |
870 silc_free(nickname); | |
871 return 0; | |
872 } | |
873 | |
874 /* Send private message directly */ | |
875 ret = silc_client_send_private_message(client, conn, clients[0], | |
876 mflags, (char *)msg, | |
877 strlen(msg), TRUE); | |
878 | |
879 silc_free(nickname); | |
880 silc_free(clients); | |
881 return ret; | |
882 } | |
883 | |
884 | |
885 /************************** Plugin Initialization ****************************/ | |
886 | |
887 static GaimPluginPrefFrame * | |
888 silcgaim_pref_frame(GaimPlugin *plugin) | |
889 { | |
890 GaimPluginPrefFrame *frame; | |
891 GaimPluginPref *ppref; | |
892 | |
893 frame = gaim_plugin_pref_frame_new(); | |
894 | |
895 ppref = gaim_plugin_pref_new_with_label(_("Instant Messages")); | |
896 gaim_plugin_pref_frame_add(frame, ppref); | |
897 | |
898 ppref = gaim_plugin_pref_new_with_name_and_label( | |
899 "/plugins/prpl/silc/sign_im", | |
900 _("Digitally sign all IM messages")); | |
901 gaim_plugin_pref_frame_add(frame, ppref); | |
902 | |
903 ppref = gaim_plugin_pref_new_with_name_and_label( | |
904 "/plugins/prpl/silc/verify_im", | |
905 _("Verify all IM message signatures")); | |
906 gaim_plugin_pref_frame_add(frame, ppref); | |
907 | |
908 ppref = gaim_plugin_pref_new_with_label(_("Channel Messages")); | |
909 gaim_plugin_pref_frame_add(frame, ppref); | |
910 | |
911 ppref = gaim_plugin_pref_new_with_name_and_label( | |
912 "/plugins/prpl/silc/sign_chat", | |
913 _("Digitally sign all channel messages")); | |
914 gaim_plugin_pref_frame_add(frame, ppref); | |
915 | |
916 ppref = gaim_plugin_pref_new_with_name_and_label( | |
917 "/plugins/prpl/silc/verify_chat", | |
918 _("Verify all channel message signatures")); | |
919 gaim_plugin_pref_frame_add(frame, ppref); | |
920 | |
921 ppref = gaim_plugin_pref_new_with_label(_("Default SILC Key Pair")); | |
922 gaim_plugin_pref_frame_add(frame, ppref); | |
923 | |
924 ppref = gaim_plugin_pref_new_with_name_and_label( | |
925 "/plugins/prpl/silc/pubkey", | |
926 _("SILC Public Key")); | |
927 gaim_plugin_pref_frame_add(frame, ppref); | |
928 | |
929 ppref = gaim_plugin_pref_new_with_name_and_label( | |
930 "/plugins/prpl/silc/privkey", | |
931 _("SILC Private Key")); | |
932 gaim_plugin_pref_frame_add(frame, ppref); | |
933 | |
934 return frame; | |
935 } | |
936 | |
937 static GaimPluginUiInfo prefs_info = | |
938 { | |
939 silcgaim_pref_frame, | |
940 }; | |
941 | |
942 static GaimPluginProtocolInfo prpl_info = | |
943 { | |
944 GAIM_PRPL_API_VERSION, | |
945 OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | | |
946 OPT_PROTO_PASSWORD_OPTIONAL, | |
947 NULL, | |
948 NULL, | |
949 silcgaim_list_icon, | |
950 silcgaim_list_emblems, | |
951 silcgaim_status_text, | |
952 silcgaim_tooltip_text, | |
953 silcgaim_away_states, | |
954 silcgaim_actions, | |
955 silcgaim_buddy_menu, | |
956 silcgaim_chat_info, | |
957 silcgaim_login, | |
958 silcgaim_close, | |
959 silcgaim_send_im, | |
960 NULL, | |
961 NULL, | |
962 silcgaim_get_info, | |
963 silcgaim_set_away, | |
964 NULL, | |
965 NULL, | |
966 NULL, | |
967 silcgaim_idle_set, | |
968 NULL, | |
969 silcgaim_add_buddy, | |
970 silcgaim_add_buddies, | |
971 silcgaim_remove_buddy, | |
972 NULL, | |
973 NULL, | |
974 NULL, | |
975 NULL, | |
976 NULL, | |
977 NULL, | |
978 NULL, | |
979 silcgaim_chat_join, | |
980 NULL, | |
981 silcgaim_chat_invite, | |
982 silcgaim_chat_leave, | |
983 NULL, | |
984 silcgaim_chat_send, | |
985 silcgaim_keepalive, | |
986 NULL, | |
987 NULL, | |
988 NULL, | |
989 NULL, | |
990 NULL, | |
991 NULL, | |
992 NULL, | |
993 NULL, | |
994 NULL, | |
995 NULL, | |
996 NULL, | |
997 NULL, | |
998 silcgaim_chat_set_topic, | |
999 NULL, | |
1000 silcgaim_roomlist_get_list, | |
1001 silcgaim_roomlist_cancel, | |
1002 NULL, | |
1003 silcgaim_chat_menu | |
1004 }; | |
1005 | |
1006 static GaimPluginInfo info = | |
1007 { | |
1008 GAIM_PLUGIN_API_VERSION, /**< api_version */ | |
1009 GAIM_PLUGIN_PROTOCOL, /**< type */ | |
1010 NULL, /**< ui_requirement */ | |
1011 0, /**< flags */ | |
1012 NULL, /**< dependencies */ | |
1013 GAIM_PRIORITY_DEFAULT, /**< priority */ | |
1014 | |
1015 "prpl-silc", /**< id */ | |
1016 "SILC", /**< name */ | |
1017 "1.0", /**< version */ | |
1018 /** summary */ | |
1019 N_("SILC Protocol Plugin"), | |
1020 /** description */ | |
1021 N_("Secure Internet Live Conferencing (SILC) Protocol"), | |
1022 N_("Pekka Riikonen"), /**< author */ | |
1023 N_("http://silcnet.org/"), /**< homepage */ | |
1024 | |
1025 NULL, /**< load */ | |
1026 NULL, /**< unload */ | |
1027 NULL, /**< destroy */ | |
1028 | |
1029 NULL, /**< ui_info */ | |
1030 &prpl_info, /**< extra_info */ | |
1031 &prefs_info /**< prefs_info */ | |
1032 }; | |
1033 | |
1034 static void | |
1035 init_plugin(GaimPlugin *plugin) | |
1036 { | |
1037 GaimAccountOption *option; | |
1038 char tmp[256]; | |
1039 | |
1040 silc_plugin = plugin; | |
1041 | |
1042 /* Account options */ | |
1043 option = gaim_account_option_string_new(_("Connect server"), | |
1044 "server", | |
1045 "silc.silcnet.org"); | |
1046 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
1047 option = gaim_account_option_int_new(_("Port"), "port", 706); | |
1048 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
1049 | |
1050 option = gaim_account_option_bool_new(_("Public key authentication"), | |
1051 "pubkey-auth", FALSE); | |
1052 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
1053 #if 0 /* XXX Public key auth interface with explicit key pair is | |
1054 broken in SILC Toolkit */ | |
1055 g_snprintf(tmp, sizeof(tmp), _("%s/public_key.pub"), silcgaim_silcdir()); | |
1056 option = gaim_account_option_string_new(_("Public Key File"), | |
1057 "public-key", tmp); | |
1058 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
1059 g_snprintf(tmp, sizeof(tmp), _("%s/private_key.prv"), silcgaim_silcdir()); | |
1060 option = gaim_account_option_string_new(_("Private Key File"), | |
1061 "public-key", tmp); | |
1062 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
1063 #endif | |
1064 | |
1065 option = gaim_account_option_bool_new(_("Reject watching by other users"), | |
1066 "reject-watch", FALSE); | |
1067 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
1068 option = gaim_account_option_bool_new(_("Block invites"), | |
1069 "block-invites", FALSE); | |
1070 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
1071 option = gaim_account_option_bool_new(_("Block IMs without Key Exchange"), | |
1072 "block-ims", FALSE); | |
1073 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
1074 option = gaim_account_option_bool_new(_("Reject online status attribute requests"), | |
1075 "reject-attrs", FALSE); | |
1076 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
1077 | |
1078 /* Preferences */ | |
1079 gaim_prefs_add_none("/plugins/prpl/silc"); | |
1080 gaim_prefs_add_bool("/plugins/prpl/silc/sign_im", FALSE); | |
1081 gaim_prefs_add_bool("/plugins/prpl/silc/verify_im", FALSE); | |
1082 gaim_prefs_add_bool("/plugins/prpl/silc/sign_chat", FALSE); | |
1083 gaim_prefs_add_bool("/plugins/prpl/silc/verify_chat", FALSE); | |
1084 g_snprintf(tmp, sizeof(tmp), _("%s/public_key.pub"), silcgaim_silcdir()); | |
1085 gaim_prefs_add_string("/plugins/prpl/silc/pubkey", tmp); | |
1086 g_snprintf(tmp, sizeof(tmp), _("%s/private_key.prv"), silcgaim_silcdir()); | |
1087 gaim_prefs_add_string("/plugins/prpl/silc/privkey", tmp); | |
1088 gaim_prefs_add_string("/plugins/prpl/silc/vcard", ""); | |
1089 } | |
1090 | |
1091 GAIM_INIT_PLUGIN(silc, init_plugin, info); |