1 /*
2 * novell.c
3 *
4 * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
18 *
19 */
20
21 #include "internal.h"
22 #include "accountopt.h"
23 #include "debug.h"
24 #include "prpl.h"
25 #include "server.h"
26 #include "nmuser.h"
27 #include "notify.h"
28 #include "util.h"
29 #include "sslconn.h"
30 #include "request.h"
31 #include "network.h"
32 #include "privacy.h"
33 #include "status.h"
34 #include "version.h"
35
36 #define DEFAULT_PORT 8300
37 #define NOVELL_CONNECT_STEPS 4
38 #define NM_ROOT_FOLDER_NAME "GroupWise Messenger"
39
40 #define NOVELL_STATUS_TYPE_AVAILABLE "available"
41 #define NOVELL_STATUS_TYPE_AWAY "away"
42 #define NOVELL_STATUS_TYPE_BUSY "busy"
43 #define NOVELL_STATUS_TYPE_OFFLINE "offline"
44 #define NOVELL_STATUS_TYPE_IDLE "idle"
45 #define NOVELL_STATUS_TYPE_APPEAR_OFFLINE "appearoffline"
46
47 static PurplePlugin *my_protocol = NULL;
48
49 static gboolean
50 _is_disconnect_error(NMERR_T err);
51
52 static gboolean
53 _check_for_disconnect(NMUser * user, NMERR_T err);
54
55 static void
56 _send_message(NMUser * user, NMMessage * message);
57
58 static void
59 _update_buddy_status(NMUser *user, PurpleBuddy * buddy, int status, int gmt);
60
61 static void
62 _remove_purple_buddies(NMUser * user);
63
64 static void
65 _add_contacts_to_purple_blist(NMUser * user, NMFolder * folder);
66
67 static void
68 _add_purple_buddies(NMUser * user);
69
70 static void
71 _sync_contact_list(NMUser *user);
72
73 static void
74 _sync_privacy_lists(NMUser *user);
75
76 static void
77 _show_info(PurpleConnection * gc, NMUserRecord * user_record, char * name);
78
79 const char *
80 _get_conference_name(int id);
81
82 /*******************************************************************************
83 * Response callbacks
84 *******************************************************************************/
85
86 /* Handle login response */
87 static void
_login_resp_cb(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)88 _login_resp_cb(NMUser * user, NMERR_T ret_code,
89 gpointer resp_data, gpointer user_data)
90 {
91 PurpleConnection *gc;
92 const char *alias;
93 NMERR_T rc;
94
95 if (user == NULL)
96 return;
97
98 gc = purple_account_get_connection(user->client_data);
99 if (gc == NULL)
100 return;
101
102 if (ret_code == NM_OK) {
103
104 /* Set alias for user if not set (use Full Name) */
105 alias = purple_account_get_alias(user->client_data);
106 if (alias == NULL || *alias == '\0') {
107 alias = nm_user_record_get_full_name(user->user_record);
108
109 if (alias)
110 purple_account_set_alias(user->client_data, alias);
111 }
112
113 /* Tell Purple that we are connected */
114 purple_connection_set_state(gc, PURPLE_CONNECTED);
115
116 _sync_contact_list(user);
117
118 rc = nm_send_set_status(user, NM_STATUS_AVAILABLE, NULL, NULL, NULL,
119 NULL);
120 _check_for_disconnect(user, rc);
121
122 } else {
123 PurpleConnectionError reason;
124 char *err = g_strdup_printf(_("Unable to login: %s"),
125 nm_error_to_string (ret_code));
126
127 switch (ret_code) {
128 case NMERR_AUTHENTICATION_FAILED:
129 case NMERR_CREDENTIALS_MISSING:
130 case NMERR_PASSWORD_INVALID:
131 /* Don't attempt to auto-reconnect if our
132 * password was invalid.
133 */
134 if (!purple_account_get_remember_password(gc->account))
135 purple_account_set_password(gc->account, NULL);
136 reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
137 break;
138 default:
139 /* FIXME: There are other reasons login could fail */
140 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
141 }
142
143 purple_connection_error_reason(gc, reason, err);
144 g_free(err);
145 }
146 }
147
148 /* Handle getstatus response*/
149 static void
_get_status_resp_cb(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)150 _get_status_resp_cb(NMUser * user, NMERR_T ret_code,
151 gpointer resp_data, gpointer user_data)
152 {
153 PurpleBuddy *buddy;
154 GSList *buddies;
155 GSList *bnode;
156 NMUserRecord *user_record = (NMUserRecord *) resp_data;
157 int status;
158
159 if (user == NULL || user_record == NULL)
160 return;
161
162 if (ret_code == NM_OK) {
163
164 /* Find all Purple buddies and update their statuses */
165 const char *name = nm_user_record_get_display_id(user_record);
166
167 if (name) {
168 buddies = purple_find_buddies((PurpleAccount *) user->client_data, name);
169 for (bnode = buddies; bnode; bnode = bnode->next) {
170 buddy = (PurpleBuddy *) bnode->data;
171 if (buddy) {
172 status = nm_user_record_get_status(user_record);
173 _update_buddy_status(user, buddy, status, time(0));
174 }
175 }
176 g_slist_free(buddies);
177 }
178
179 } else {
180
181 purple_debug(PURPLE_DEBUG_INFO, "novell",
182 "_get_status_resp_cb(): rc = 0x%X\n", ret_code);
183
184 }
185 }
186
187 /* Show an error if the rename failed */
188 static void
_rename_contact_resp_cb(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)189 _rename_contact_resp_cb(NMUser * user, NMERR_T ret_code,
190 gpointer resp_data, gpointer user_data)
191 {
192 if (ret_code != NM_OK) {
193 purple_debug(PURPLE_DEBUG_INFO, "novell",
194 "_rename_contact_resp_cb(): rc = 0x%X\n", ret_code);
195 }
196 }
197
198 /* Handle the getdetails response and send the message */
199 static void
_get_details_resp_send_msg(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)200 _get_details_resp_send_msg(NMUser * user, NMERR_T ret_code,
201 gpointer resp_data, gpointer user_data)
202 {
203 PurpleConversation *gconv;
204 PurpleConnection *gc;
205 NMUserRecord *user_record = NULL;
206 NMContact *cntct = NULL;
207 NMConference *conf;
208 NMMessage *msg = user_data;
209 const char *dn = NULL;
210 const char *name;
211
212 if (user == NULL || msg == NULL)
213 return;
214
215 if (ret_code == NM_OK) {
216 user_record = (NMUserRecord *) resp_data;
217 if (user_record) {
218
219 /* Set the title for the conversation */
220 /* XXX - Should this be PURPLE_CONV_TYPE_IM? */
221 gconv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
222 nm_user_record_get_display_id(user_record),
223 (PurpleAccount *) user->client_data);
224 if (gconv) {
225
226 dn = nm_user_record_get_dn(user_record);
227 if (dn) {
228 cntct = nm_find_contact(user, dn);
229 }
230
231 if (cntct) {
232 purple_conversation_set_title(gconv,
233 nm_contact_get_display_name(cntct));
234 } else {
235
236 /* Not in the contact list, try to user full name */
237 name = (char *) nm_user_record_get_full_name(user_record);
238 if (name)
239 purple_conversation_set_title(gconv, name);
240 }
241 }
242
243 /* Add the user record to particpant list */
244 conf = nm_message_get_conference(msg);
245 if (conf) {
246 nm_conference_add_participant(conf, user_record);
247 _send_message(user, msg);
248 }
249 }
250
251 } else {
252
253 gc = purple_account_get_connection(user->client_data);
254 if (gc != NULL) {
255 char *err = g_strdup_printf(_("Unable to send message."
256 " Could not get details for user (%s)."),
257 nm_error_to_string (ret_code));
258
259 purple_notify_error(gc, NULL, err, NULL);
260 g_free(err);
261 }
262
263 if (msg)
264 nm_release_message(msg);
265 }
266 }
267
268 /* Set up the new PurpleBuddy based on the response from getdetails */
269 static void
_get_details_resp_setup_buddy(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)270 _get_details_resp_setup_buddy(NMUser * user, NMERR_T ret_code,
271 gpointer resp_data, gpointer user_data)
272 {
273 NMUserRecord *user_record;
274 NMContact *contact;
275 PurpleBuddy *buddy;
276 const char *alias;
277 NMERR_T rc = NM_OK;
278
279 if (user == NULL || resp_data == NULL || user_data == NULL)
280 return;
281
282 contact = user_data;
283
284 if (ret_code == NM_OK) {
285 user_record = resp_data;
286
287 buddy = nm_contact_get_data(contact);
288
289 nm_contact_set_user_record(contact, user_record);
290
291 /* Set the display id */
292 purple_blist_rename_buddy(buddy,
293 nm_user_record_get_display_id(user_record));
294
295 alias = purple_buddy_get_alias(buddy);
296 if (alias == NULL || *alias == '\0' || purple_strequal(alias, purple_buddy_get_name(buddy))) {
297 purple_blist_alias_buddy(buddy,
298 nm_user_record_get_full_name(user_record));
299
300 /* Tell the server about the new display name */
301 rc = nm_send_rename_contact(user, contact,
302 nm_user_record_get_full_name(user_record),
303 NULL, NULL);
304 _check_for_disconnect(user, rc);
305
306 }
307
308
309 /* Get initial status for the buddy */
310 rc = nm_send_get_status(user, resp_data, _get_status_resp_cb, NULL);
311 _check_for_disconnect(user, rc);
312
313 /* nm_release_contact(contact);*/
314
315 }
316
317 if (contact)
318 nm_release_contact(contact);
319 }
320
321 /* Add the new contact into the PurpleBuddy list */
322 static void
_create_contact_resp_cb(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)323 _create_contact_resp_cb(NMUser * user, NMERR_T ret_code,
324 gpointer resp_data, gpointer user_data)
325 {
326 NMContact *tmp_contact = (NMContact *) user_data;
327 NMContact *new_contact = NULL;
328 NMFolder *folder = NULL;
329 PurpleGroup *group;
330 PurpleBuddy *buddy;
331 const char *folder_name = NULL;
332 NMERR_T rc = NM_OK;
333
334 if (user == NULL)
335 return;
336
337 if (ret_code == NM_OK) {
338
339 new_contact = (NMContact *) resp_data;
340 if (new_contact == NULL || tmp_contact == NULL)
341 return;
342
343 /* Get the userid and folder name for the new contact */
344 folder = nm_find_folder_by_id(user,
345 nm_contact_get_parent_id(new_contact));
346 if (folder) {
347 folder_name = nm_folder_get_name(folder);
348 }
349
350 if (folder_name == NULL || *folder_name == '\0')
351 folder_name = NM_ROOT_FOLDER_NAME;
352
353 /* Re-add the buddy now that we got the okay from the server */
354 if (folder_name && (group = purple_find_group(folder_name))) {
355
356 const char *alias = nm_contact_get_display_name(tmp_contact);
357 const char *display_id = nm_contact_get_display_id(new_contact);
358
359 if (display_id == NULL)
360 display_id = nm_contact_get_dn(new_contact);
361
362 if (alias && !purple_strequal(alias, display_id)) {
363
364 /* The user requested an alias, tell the server about it. */
365 rc = nm_send_rename_contact(user, new_contact, alias,
366 _rename_contact_resp_cb, NULL);
367 _check_for_disconnect(user, rc);
368
369 } else {
370
371 alias = "";
372
373 }
374
375 /* Add it to the purple buddy list if it is not there */
376 buddy = purple_find_buddy_in_group(user->client_data, display_id, group);
377 if (buddy == NULL) {
378 buddy = purple_buddy_new(user->client_data, display_id, alias);
379 purple_blist_add_buddy(buddy, NULL, group, NULL);
380 }
381
382 /* Save the new buddy as part of the contact object */
383 nm_contact_set_data(new_contact, (gpointer) buddy);
384
385 /* We need details for the user before we can setup the
386 * new Purple buddy. We always call this because the
387 * 'createcontact' response fields do not always contain
388 * everything that we need.
389 */
390 nm_contact_add_ref(new_contact);
391
392 rc = nm_send_get_details(user, nm_contact_get_dn(new_contact),
393 _get_details_resp_setup_buddy, new_contact);
394 _check_for_disconnect(user, rc);
395
396 }
397
398 } else {
399 PurpleConnection *gc = purple_account_get_connection(user->client_data);
400 const char *name = nm_contact_get_dn(tmp_contact);
401 char *err;
402
403 err =
404 g_strdup_printf(_("Unable to add %s to your buddy list (%s)."),
405 name, nm_error_to_string (ret_code));
406 purple_notify_error(gc, NULL, err, NULL);
407 g_free(err);
408
409 }
410
411 if (tmp_contact)
412 nm_release_contact(tmp_contact);
413 }
414
415 /* Show an error if we failed to send the message */
416 static void
_send_message_resp_cb(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)417 _send_message_resp_cb(NMUser * user, NMERR_T ret_code,
418 gpointer resp_data, gpointer user_data)
419 {
420 PurpleConnection *gc;
421 char *err = NULL;
422
423 if (user == NULL)
424 return;
425
426 if (ret_code != NM_OK) {
427 gc = purple_account_get_connection(user->client_data);
428
429 /* TODO: Improve this! message to who or for what conference? */
430 err = g_strdup_printf(_("Unable to send message (%s)."),
431 nm_error_to_string (ret_code));
432 purple_notify_error(gc, NULL, err, NULL);
433 g_free(err);
434 }
435 }
436
437 /* Show an error if the remove failed */
438 static void
_remove_contact_resp_cb(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)439 _remove_contact_resp_cb(NMUser * user, NMERR_T ret_code,
440 gpointer resp_data, gpointer user_data)
441 {
442 if (ret_code != NM_OK) {
443 /* TODO: Display an error? */
444
445 purple_debug(PURPLE_DEBUG_INFO, "novell",
446 "_remove_contact_resp_cb(): rc = 0x%x\n", ret_code);
447 }
448 }
449
450 /* Show an error if the remove failed */
451 static void
_remove_folder_resp_cb(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)452 _remove_folder_resp_cb(NMUser * user, NMERR_T ret_code,
453 gpointer resp_data, gpointer user_data)
454 {
455 if (ret_code != NM_OK) {
456 /* TODO: Display an error? */
457
458 purple_debug(PURPLE_DEBUG_INFO, "novell",
459 "_remove_folder_resp_cb(): rc = 0x%x\n", ret_code);
460 }
461 }
462
463 /* Show an error if the move failed */
464 static void
_move_contact_resp_cb(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)465 _move_contact_resp_cb(NMUser * user, NMERR_T ret_code,
466 gpointer resp_data, gpointer user_data)
467 {
468 if (ret_code != NM_OK) {
469 /* TODO: Display an error? */
470
471 purple_debug(PURPLE_DEBUG_INFO, "novell",
472 "_move_contact_resp_cb(): rc = 0x%x\n", ret_code);
473 }
474 }
475
476 /* Show an error if the rename failed */
477 static void
_rename_folder_resp_cb(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)478 _rename_folder_resp_cb(NMUser * user, NMERR_T ret_code,
479 gpointer resp_data, gpointer user_data)
480 {
481 if (ret_code != NM_OK) {
482 /* TODO: Display an error? */
483
484 purple_debug(PURPLE_DEBUG_INFO, "novell",
485 "_rename_folder_resp_cb(): rc = 0x%x\n", ret_code);
486 }
487 }
488
489 static void
_sendinvite_resp_cb(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)490 _sendinvite_resp_cb(NMUser *user, NMERR_T ret_code,
491 gpointer resp_data, gpointer user_data)
492 {
493 char *err;
494 PurpleConnection *gc;
495
496 if (user == NULL)
497 return;
498
499 if (ret_code != NM_OK) {
500 gc = purple_account_get_connection(user->client_data);
501 err = g_strdup_printf(_("Unable to invite user (%s)."), nm_error_to_string(ret_code));
502 purple_notify_error(gc, NULL, err, NULL);
503 g_free(err);
504
505 purple_debug(PURPLE_DEBUG_INFO, "novell",
506 "_sendinvite_resp_cb(): rc = 0x%x\n", ret_code);
507 }
508
509 }
510
511 /* If the createconf was successful attempt to send the message,
512 * otherwise display an error message to the user.
513 */
514 static void
_createconf_resp_send_msg(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)515 _createconf_resp_send_msg(NMUser * user, NMERR_T ret_code,
516 gpointer resp_data, gpointer user_data)
517 {
518 NMConference *conf;
519 NMMessage *msg = user_data;
520
521 if (user == NULL || msg == NULL)
522 return;
523
524 if (ret_code == NM_OK) {
525 _send_message(user, msg);
526 } else {
527
528 if ((conf = nm_message_get_conference(msg))) {
529
530 PurpleConnection *gc = purple_account_get_connection(user->client_data);
531 const char *name = NULL;
532 char *err;
533 NMUserRecord *ur;
534
535 ur = nm_conference_get_participant(conf, 0);
536 if (ur)
537 name = nm_user_record_get_userid(ur);
538
539 if (name)
540 err = g_strdup_printf(_("Unable to send message to %s."
541 " Could not create the conference (%s)."),
542 name,
543 nm_error_to_string (ret_code));
544 else
545 err = g_strdup_printf(_("Unable to send message."
546 " Could not create the conference (%s)."),
547 nm_error_to_string (ret_code));
548
549 purple_notify_error(gc, NULL, err, NULL);
550 g_free(err);
551 }
552
553 if (msg)
554 nm_release_message(msg);
555 }
556 }
557
558 /* Move contact to newly created folder */
559 static void
_create_folder_resp_move_contact(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)560 _create_folder_resp_move_contact(NMUser * user, NMERR_T ret_code,
561 gpointer resp_data, gpointer user_data)
562 {
563 NMContact *contact = user_data;
564 NMFolder *new_folder;
565 char *folder_name = resp_data;
566 NMERR_T rc = NM_OK;
567
568 if (user == NULL || folder_name == NULL || contact == NULL) {
569
570 if (folder_name)
571 g_free(folder_name);
572
573 return;
574 }
575
576 if (ret_code == NM_OK || ret_code == NMERR_DUPLICATE_FOLDER) {
577 new_folder = nm_find_folder(user, folder_name);
578 if (new_folder) {
579
580 /* Tell the server to move the contact to the new folder */
581 /* rc = nm_send_move_contact(user, contact, new_folder,
582 _move_contact_resp_cb, NULL); */
583
584 rc = nm_send_create_contact(user, new_folder, contact,
585 NULL, NULL);
586
587 _check_for_disconnect(user, rc);
588
589 }
590 } else {
591 PurpleConnection *gc = purple_account_get_connection(user->client_data);
592 char *err = g_strdup_printf(_("Unable to move user %s"
593 " to folder %s in the server side list."
594 " Error while creating folder (%s)."),
595 nm_contact_get_dn(contact),
596 folder_name,
597 nm_error_to_string (ret_code));
598
599 purple_notify_error(gc, NULL, err, NULL);
600 g_free(err);
601 }
602
603 if (folder_name)
604 g_free(folder_name);
605 }
606
607 /* Add contact to newly create folder */
608 static void
_create_folder_resp_add_contact(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)609 _create_folder_resp_add_contact(NMUser * user, NMERR_T ret_code,
610 gpointer resp_data, gpointer user_data)
611 {
612 NMContact *contact = (NMContact *) user_data;
613 NMFolder *folder;
614 char *folder_name = (char *) resp_data;
615 NMERR_T rc = NM_OK;
616
617 if (user == NULL || folder_name == NULL || contact == NULL) {
618
619 if (contact)
620 nm_release_contact(contact);
621
622 if (folder_name)
623 g_free(folder_name);
624
625 return;
626 }
627
628 if (ret_code == NM_OK || ret_code == NMERR_DUPLICATE_FOLDER) {
629 folder = nm_find_folder(user, folder_name);
630 if (folder) {
631
632 rc = nm_send_create_contact(user, folder, contact,
633 _create_contact_resp_cb, contact);
634 _check_for_disconnect(user, rc);
635 }
636 } else {
637 PurpleConnection *gc = purple_account_get_connection(user->client_data);
638 const char *name = nm_contact_get_dn(contact);
639 char *err =
640 g_strdup_printf(_("Unable to add %s to your buddy list."
641 " Error creating folder in server side list (%s)."),
642 name, nm_error_to_string (ret_code));
643
644 purple_notify_error(gc, NULL, err, NULL);
645
646 nm_release_contact(contact);
647 g_free(err);
648 }
649
650 g_free(folder_name);
651 }
652
653 static void
_join_conf_resp_cb(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)654 _join_conf_resp_cb(NMUser * user, NMERR_T ret_code,
655 gpointer resp_data, gpointer user_data)
656 {
657 PurpleConversation *chat;
658 PurpleConnection *gc;
659 NMUserRecord *ur;
660 NMConference *conference = user_data;
661 const char *name, *conf_name;
662 int i, count;
663
664 if (user == NULL || conference == NULL)
665 return;
666
667 gc = purple_account_get_connection(user->client_data);
668
669 if (ret_code == NM_OK) {
670 conf_name = _get_conference_name(++user->conference_count);
671 chat = serv_got_joined_chat(gc, user->conference_count, conf_name);
672 if (chat) {
673
674 nm_conference_set_data(conference, (gpointer) chat);
675
676 count = nm_conference_get_participant_count(conference);
677 for (i = 0; i < count; i++) {
678 ur = nm_conference_get_participant(conference, i);
679 if (ur) {
680 name = nm_user_record_get_display_id(ur);
681 purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat), name, NULL,
682 PURPLE_CBFLAGS_NONE, TRUE);
683 }
684 }
685 }
686 }
687 }
688
689 /* Show info returned by getdetails */
690 static void
_get_details_resp_show_info(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)691 _get_details_resp_show_info(NMUser * user, NMERR_T ret_code,
692 gpointer resp_data, gpointer user_data)
693 {
694 PurpleConnection *gc;
695 NMUserRecord *user_record;
696 char *name;
697 char *err;
698
699 if (user == NULL)
700 return;
701
702 name = user_data;
703
704 if (ret_code == NM_OK) {
705 user_record = (NMUserRecord *) resp_data;
706 if (user_record) {
707 _show_info(purple_account_get_connection(user->client_data),
708 user_record, g_strdup(name));
709 }
710 } else {
711 gc = purple_account_get_connection(user->client_data);
712 err =
713 g_strdup_printf(_("Could not get details for user %s (%s)."),
714 name, nm_error_to_string (ret_code));
715 purple_notify_error(gc, NULL, err, NULL);
716 g_free(err);
717 }
718
719 if (name)
720 g_free(name);
721 }
722
723 /* Handle get details response add to privacy list */
724 static void
_get_details_resp_add_privacy_item(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)725 _get_details_resp_add_privacy_item(NMUser *user, NMERR_T ret_code,
726 gpointer resp_data, gpointer user_data)
727 {
728 PurpleConnection *gc;
729 NMUserRecord *user_record = resp_data;
730 char *err;
731 gboolean allowed = GPOINTER_TO_INT(user_data);
732 const char *display_id;
733
734 if (user == NULL)
735 return;
736
737 gc = purple_account_get_connection(user->client_data);
738 display_id = nm_user_record_get_display_id(user_record);
739
740 if (ret_code == NM_OK) {
741
742 if (allowed) {
743
744 if (!g_slist_find_custom(gc->account->permit,
745 display_id, (GCompareFunc)purple_utf8_strcasecmp)) {
746 purple_privacy_permit_add(gc->account, display_id, TRUE);
747 }
748
749 } else {
750
751 if (!g_slist_find_custom(gc->account->permit,
752 display_id, (GCompareFunc)purple_utf8_strcasecmp)) {
753 purple_privacy_deny_add(gc->account, display_id, TRUE);
754 }
755 }
756
757 } else {
758
759 err = g_strdup_printf(_("Unable to add user to privacy list (%s)."),
760 nm_error_to_string(ret_code));
761 purple_notify_error(gc, NULL, err, NULL);
762 g_free(err);
763
764 }
765 }
766
767 /* Handle response to create privacy item request */
768 static void
_create_privacy_item_deny_resp_cb(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)769 _create_privacy_item_deny_resp_cb(NMUser *user, NMERR_T ret_code,
770 gpointer resp_data, gpointer user_data)
771 {
772 PurpleConnection *gc;
773 NMUserRecord *user_record;
774 char *who = user_data;
775 char *err;
776 NMERR_T rc = NM_OK;
777 const char *display_id = NULL;
778
779 if (user == NULL)
780 return;
781
782 gc = purple_account_get_connection(user->client_data);
783
784 if (ret_code == NM_OK) {
785
786 user_record = nm_find_user_record(user, who);
787 if (user_record)
788 display_id = nm_user_record_get_display_id(user_record);
789
790 if (display_id) {
791
792 if (!g_slist_find_custom(gc->account->deny,
793 display_id, (GCompareFunc)purple_utf8_strcasecmp)) {
794
795 purple_privacy_deny_add(gc->account, display_id, TRUE);
796 }
797
798 } else {
799 rc = nm_send_get_details(user, who,
800 _get_details_resp_add_privacy_item,
801 (gpointer)FALSE);
802 _check_for_disconnect(user, rc);
803 }
804 } else {
805
806 err = g_strdup_printf(_("Unable to add %s to deny list (%s)."),
807 who, nm_error_to_string(ret_code));
808 purple_notify_error(gc, NULL, err, NULL);
809 g_free(err);
810
811 }
812
813 if (who)
814 g_free(who);
815
816 }
817
818 /* Handle response to create privacy item request */
819 static void
_create_privacy_item_permit_resp_cb(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)820 _create_privacy_item_permit_resp_cb(NMUser *user, NMERR_T ret_code,
821 gpointer resp_data, gpointer user_data)
822 {
823 PurpleConnection *gc;
824 NMUserRecord *user_record;
825 char *who = user_data;
826 char *err;
827 NMERR_T rc = NM_OK;
828 const char *display_id = NULL;
829
830 if (user == NULL)
831 return;
832
833 gc = purple_account_get_connection(user->client_data);
834
835 if (ret_code == NM_OK) {
836
837 user_record = nm_find_user_record(user, who);
838 if (user_record)
839 display_id = nm_user_record_get_display_id(user_record);
840
841 if (display_id) {
842
843 if (!g_slist_find_custom(gc->account->permit,
844 display_id,
845 (GCompareFunc)purple_utf8_strcasecmp)) {
846
847 purple_privacy_permit_add(gc->account, display_id, TRUE);
848 }
849
850 } else {
851 rc = nm_send_get_details(user, who,
852 _get_details_resp_add_privacy_item,
853 (gpointer)TRUE);
854 _check_for_disconnect(user, rc);
855 }
856
857 } else {
858
859 err = g_strdup_printf(_("Unable to add %s to permit list (%s)."), who,
860 nm_error_to_string(ret_code));
861 purple_notify_error(gc, NULL, err, NULL);
862 g_free(err);
863
864 }
865
866 if (who)
867 g_free(who);
868 }
869
870 static void
_get_details_send_privacy_create(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)871 _get_details_send_privacy_create(NMUser *user, NMERR_T ret_code,
872 gpointer resp_data, gpointer user_data)
873 {
874 NMERR_T rc = NM_OK;
875 PurpleConnection *gc;
876 NMUserRecord *user_record = resp_data;
877 char *err;
878 gboolean allowed = GPOINTER_TO_INT(user_data);
879 const char *dn, *display_id;
880
881 if (user == NULL)
882 return;
883
884 gc = purple_account_get_connection(user->client_data);
885 dn = nm_user_record_get_dn(user_record);
886 display_id = nm_user_record_get_display_id(user_record);
887
888 if (ret_code == NM_OK) {
889
890 if (allowed) {
891 rc = nm_send_create_privacy_item(user, dn, TRUE,
892 _create_privacy_item_permit_resp_cb,
893 g_strdup(display_id));
894 _check_for_disconnect(user, rc);
895
896 } else {
897 rc = nm_send_create_privacy_item(user, dn, FALSE,
898 _create_privacy_item_deny_resp_cb,
899 g_strdup(display_id));
900 _check_for_disconnect(user, rc);
901 }
902
903 } else {
904
905 err = g_strdup_printf(_("Unable to add user to privacy list (%s)."),
906 nm_error_to_string(ret_code));
907 purple_notify_error(gc, NULL, err, NULL);
908 g_free(err);
909
910 }
911 }
912
913 static void
_remove_privacy_item_resp_cb(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)914 _remove_privacy_item_resp_cb(NMUser *user, NMERR_T ret_code,
915 gpointer resp_data, gpointer user_data)
916 {
917 PurpleConnection *gc;
918 char *who = user_data;
919 char *err;
920
921 if (user == NULL)
922 return;
923
924 if (ret_code != NM_OK) {
925
926 gc = purple_account_get_connection(user->client_data);
927 err = g_strdup_printf(_("Unable to remove %s from privacy list (%s)."), who,
928 nm_error_to_string(ret_code));
929 purple_notify_error(gc, NULL, err, NULL);
930 g_free(err);
931 }
932
933 if (who)
934 g_free(who);
935 }
936
937 static void
_set_privacy_default_resp_cb(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)938 _set_privacy_default_resp_cb(NMUser *user, NMERR_T ret_code,
939 gpointer resp_data, gpointer user_data)
940 {
941 PurpleConnection *gc;
942 char *err;
943
944 if (user == NULL)
945 return;
946
947 if (ret_code != NM_OK) {
948
949 gc = purple_account_get_connection(user->client_data);
950 err = g_strdup_printf(_("Unable to change server side privacy settings (%s)."),
951 nm_error_to_string(ret_code));
952 purple_notify_error(gc, NULL, err, NULL);
953 g_free(err);
954
955 }
956 }
957
958 /* Handle get details response add to privacy list */
959 static void
_get_details_resp_send_invite(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)960 _get_details_resp_send_invite(NMUser *user, NMERR_T ret_code,
961 gpointer resp_data, gpointer user_data)
962 {
963 NMERR_T rc = NM_OK;
964 PurpleConnection *gc;
965 NMUserRecord *user_record = resp_data;
966 char *err;
967 GSList *cnode;
968 NMConference *conference;
969 gpointer chat;
970 long id = (long) user_data;
971
972 if (user == NULL)
973 return;
974
975 gc = purple_account_get_connection(user->client_data);
976
977 if (ret_code == NM_OK) {
978
979 for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
980 conference = cnode->data;
981 if (conference && (chat = nm_conference_get_data(conference))) {
982 if (purple_conv_chat_get_id(PURPLE_CONV_CHAT(chat)) == id) {
983 rc = nm_send_conference_invite(user, conference, user_record,
984 NULL, _sendinvite_resp_cb, NULL);
985 _check_for_disconnect(user, rc);
986 break;
987 }
988 }
989 }
990
991 } else {
992
993 err = g_strdup_printf(_("Unable to invite user (%s)."), nm_error_to_string(ret_code));
994 purple_notify_error(gc, NULL, err, NULL);
995 g_free(err);
996
997 }
998 }
999
1000 static void
_createconf_resp_send_invite(NMUser * user,NMERR_T ret_code,gpointer resp_data,gpointer user_data)1001 _createconf_resp_send_invite(NMUser * user, NMERR_T ret_code,
1002 gpointer resp_data, gpointer user_data)
1003 {
1004 NMERR_T rc = NM_OK;
1005 NMConference *conference = resp_data;
1006 NMUserRecord *user_record = user_data;
1007 PurpleConnection *gc;
1008 char *err;
1009
1010 if (user == NULL)
1011 return;
1012
1013
1014
1015 if (ret_code == NM_OK) {
1016 rc = nm_send_conference_invite(user, conference, user_record,
1017 NULL, _sendinvite_resp_cb, NULL);
1018 _check_for_disconnect(user, rc);
1019 } else {
1020 err = g_strdup_printf(_("Unable to create conference (%s)."), nm_error_to_string(ret_code));
1021 gc = purple_account_get_connection(user->client_data);
1022 purple_notify_error(gc, NULL, err, NULL);
1023 g_free(err);
1024 }
1025 }
1026
1027 /*******************************************************************************
1028 * Helper functions
1029 ******************************************************************************/
1030
1031 static char *
_user_agent_string(void)1032 _user_agent_string(void)
1033 {
1034
1035 #if !defined(_WIN32)
1036
1037 const char *sysname = "";
1038 const char *release = "";
1039 struct utsname u;
1040
1041 if (uname(&u) == 0) {
1042 sysname = u.sysname;
1043 release = u.release;
1044 } else {
1045 sysname = "Linux";
1046 release = "Unknown";
1047 }
1048
1049 return g_strdup_printf("Purple/%s (%s; %s)", VERSION, sysname, release);
1050
1051 #else
1052
1053 const char *sysname = "";
1054 OSVERSIONINFO os_info;
1055 SYSTEM_INFO sys_info;
1056
1057 os_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1058 GetVersionEx(&os_info);
1059 GetSystemInfo(&sys_info);
1060
1061 if (os_info.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1062 switch (os_info.dwMajorVersion) {
1063 case 3:
1064 case 4:
1065 sysname = "Windows NT";
1066 break;
1067 case 5:
1068 switch (os_info.dwMinorVersion) {
1069 case 0:
1070 sysname = "Windows 2000";
1071 break;
1072 case 1:
1073 sysname = "Windows XP";
1074 break;
1075 case 2:
1076 sysname = "Windows Server 2003";
1077 break;
1078 default:
1079 sysname = "Windows";
1080 break;
1081 }
1082 break;
1083 default:
1084 sysname = "Windows";
1085 break;
1086 }
1087
1088 } else if (os_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1089 switch (os_info.dwMinorVersion) {
1090 case 0:
1091 sysname = "Windows 95";
1092 break;
1093 case 10:
1094 sysname = "Windows 98";
1095 break;
1096 case 90:
1097 sysname = "Windows ME";
1098 break;
1099 default:
1100 sysname = "Windows";
1101 break;
1102 }
1103 } else {
1104 sysname = "Windows";
1105 }
1106
1107 return g_strdup_printf("Purple/%s (%s; %ld.%ld)", VERSION, sysname,
1108 os_info.dwMajorVersion, os_info.dwMinorVersion);
1109
1110 #endif
1111
1112
1113 }
1114
1115 static gboolean
_is_disconnect_error(NMERR_T err)1116 _is_disconnect_error(NMERR_T err)
1117 {
1118 return (err == NMERR_TCP_WRITE ||
1119 err == NMERR_TCP_READ || err == NMERR_PROTOCOL);
1120 }
1121
1122 static gboolean
_check_for_disconnect(NMUser * user,NMERR_T err)1123 _check_for_disconnect(NMUser * user, NMERR_T err)
1124 {
1125 PurpleConnection *gc = purple_account_get_connection(user->client_data);
1126
1127 if (_is_disconnect_error(err)) {
1128
1129 purple_connection_error_reason(gc,
1130 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
1131 _("Error communicating with server. Closing connection."));
1132 return TRUE;
1133
1134 }
1135
1136 return FALSE;
1137 }
1138
1139 /* Check to see if the conference is instantiated, if so send the message.
1140 * If not send the create conference -- the response handler for the createconf
1141 * will call this function again.
1142 */
1143 static void
_send_message(NMUser * user,NMMessage * message)1144 _send_message(NMUser * user, NMMessage * message)
1145 {
1146 NMConference *conf;
1147 NMERR_T rc = NM_OK;
1148
1149 conf = nm_message_get_conference(message);
1150 if (conf) {
1151 /* We have a conference make sure that the
1152 server knows about it already. */
1153 if (nm_conference_is_instantiated(conf)) {
1154
1155 /* We have everything that we need...finally! */
1156 rc = nm_send_message(user, message, _send_message_resp_cb);
1157 _check_for_disconnect(user, rc);
1158
1159 nm_release_message(message);
1160
1161 } else {
1162 rc = nm_send_create_conference(user, conf, _createconf_resp_send_msg, message);
1163 _check_for_disconnect(user, rc);
1164 }
1165 }
1166 }
1167
1168 /*
1169 * Update the status of the given buddy in the Purple buddy list
1170 */
1171 static void
_update_buddy_status(NMUser * user,PurpleBuddy * buddy,int novellstatus,int gmt)1172 _update_buddy_status(NMUser *user, PurpleBuddy * buddy, int novellstatus, int gmt)
1173 {
1174 PurpleAccount *account;
1175 const char *status_id;
1176 const char *text = NULL;
1177 const char *dn;
1178 const char *name;
1179 int idle = 0;
1180
1181 account = purple_buddy_get_account(buddy);
1182 name = purple_buddy_get_name(buddy);
1183
1184 switch (novellstatus) {
1185 case NM_STATUS_AVAILABLE:
1186 status_id = NOVELL_STATUS_TYPE_AVAILABLE;
1187 break;
1188 case NM_STATUS_AWAY:
1189 status_id = NOVELL_STATUS_TYPE_AWAY;
1190 break;
1191 case NM_STATUS_BUSY:
1192 status_id = NOVELL_STATUS_TYPE_BUSY;
1193 break;
1194 case NM_STATUS_OFFLINE:
1195 status_id = NOVELL_STATUS_TYPE_OFFLINE;
1196 break;
1197 case NM_STATUS_AWAY_IDLE:
1198 status_id = NOVELL_STATUS_TYPE_AWAY;
1199 idle = gmt;
1200 break;
1201 default:
1202 status_id = NOVELL_STATUS_TYPE_OFFLINE;
1203 break;
1204 }
1205
1206 /* Get status text for the user */
1207 dn = nm_lookup_dn(user, name);
1208 if (dn) {
1209 NMUserRecord *user_record = nm_find_user_record(user, dn);
1210 if (user_record) {
1211 text = nm_user_record_get_status_text(user_record);
1212 }
1213 }
1214
1215 purple_prpl_got_user_status(account, name, status_id,
1216 "message", text, NULL);
1217 purple_prpl_got_user_idle(account, name,
1218 (novellstatus == NM_STATUS_AWAY_IDLE), idle);
1219 }
1220
1221 /* Iterate through the cached Purple buddy list and remove buddies
1222 * that are not in the server side list.
1223 */
1224 static void
_remove_purple_buddies(NMUser * user)1225 _remove_purple_buddies(NMUser *user)
1226 {
1227 PurpleBlistNode *gnode;
1228 PurpleBlistNode *cnode;
1229 PurpleBlistNode *bnode;
1230 PurpleGroup *group;
1231 PurpleBuddy *buddy;
1232 GSList *rem_list = NULL;
1233 GSList *l;
1234 NMFolder *folder = NULL;
1235 const char *gname = NULL;
1236
1237 for (gnode = purple_blist_get_root(); gnode;
1238 gnode = purple_blist_node_get_sibling_next(gnode)) {
1239 if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
1240 continue;
1241 group = (PurpleGroup *) gnode;
1242 gname = purple_group_get_name(group);
1243 for (cnode = purple_blist_node_get_first_child(gnode);
1244 cnode;
1245 cnode = purple_blist_node_get_sibling_next(cnode)) {
1246 if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
1247 continue;
1248 for (bnode = purple_blist_node_get_first_child(cnode);
1249 bnode;
1250 bnode = purple_blist_node_get_sibling_next(bnode)) {
1251 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
1252 continue;
1253 buddy = (PurpleBuddy *) bnode;
1254 if (purple_buddy_get_account(buddy) == user->client_data) {
1255 if (purple_strequal(gname, NM_ROOT_FOLDER_NAME))
1256 gname = "";
1257 folder = nm_find_folder(user, gname);
1258 if (folder == NULL ||
1259 !nm_folder_find_contact_by_display_id(folder, purple_buddy_get_name(buddy))) {
1260 rem_list = g_slist_append(rem_list, buddy);
1261 }
1262 }
1263 }
1264 }
1265 }
1266
1267 if (rem_list) {
1268 for (l = rem_list; l; l = l->next) {
1269 purple_blist_remove_buddy(l->data);
1270 }
1271 g_slist_free(rem_list);
1272 }
1273 }
1274
1275 /* Add all of the contacts in the given folder to the Purple buddy list */
1276 static void
_add_contacts_to_purple_blist(NMUser * user,NMFolder * folder)1277 _add_contacts_to_purple_blist(NMUser * user, NMFolder * folder)
1278 {
1279 NMUserRecord *user_record = NULL;
1280 NMContact *contact = NULL;
1281 PurpleBuddy *buddy = NULL;
1282 PurpleGroup *group;
1283 NMERR_T cnt = 0, i;
1284 const char *name = NULL;
1285 const char *fname = NULL;
1286 int status = 0;
1287
1288 /* If this is the root folder give it a name. Purple does not have the concept of
1289 * a root folder.
1290 */
1291 fname = nm_folder_get_name(folder);
1292 if (fname == NULL || *fname == '\0') {
1293 fname = NM_ROOT_FOLDER_NAME;
1294 }
1295
1296 /* Does the Purple group exist already? */
1297 group = purple_find_group(fname);
1298 if (group == NULL) {
1299 group = purple_group_new(fname);
1300 purple_blist_add_group(group, NULL);
1301 }
1302
1303 /* Get each contact for this folder */
1304 cnt = nm_folder_get_contact_count(folder);
1305 for (i = 0; i < cnt; i++) {
1306 contact = nm_folder_get_contact(folder, i);
1307 if (contact) {
1308
1309 name = nm_contact_get_display_id(contact);
1310 if (name) {
1311
1312 buddy = purple_find_buddy_in_group(user->client_data, name, group);
1313 if (buddy == NULL) {
1314 /* Add it to the purple buddy list */
1315 buddy = purple_buddy_new(user->client_data,
1316 name,
1317 nm_contact_get_display_name(contact));
1318
1319 purple_blist_add_buddy(buddy, NULL, group, NULL);
1320 }
1321
1322 /* Set the initial status for the buddy */
1323 user_record = nm_contact_get_user_record(contact);
1324 if (user_record) {
1325 status = nm_user_record_get_status(user_record);
1326 }
1327 _update_buddy_status(user, buddy, status, time(0));
1328
1329 /* Save the new buddy as part of the contact object */
1330 nm_contact_set_data(contact, (gpointer) buddy);
1331 }
1332
1333 } else {
1334 /* NULL contact. This should not happen, but
1335 * let's break out of the loop.
1336 */
1337 break;
1338 }
1339 }
1340 }
1341
1342 /* Add all of the server side contacts to the Purple buddy list. */
1343 static void
_add_purple_buddies(NMUser * user)1344 _add_purple_buddies(NMUser * user)
1345 {
1346 int cnt = 0, i;
1347 NMFolder *root_folder = NULL;
1348 NMFolder *folder = NULL;
1349
1350 root_folder = nm_get_root_folder(user);
1351 if (root_folder) {
1352
1353 /* Add sub-folders and contacts to sub-folders...
1354 * iterate throught the sub-folders in reverse order
1355 * because Purple adds the folders to the front -- so we
1356 * want to add the first folder last
1357 */
1358 cnt = nm_folder_get_subfolder_count(root_folder);
1359 for (i = cnt-1; i >= 0; i--) {
1360 folder = nm_folder_get_subfolder(root_folder, i);
1361 if (folder) {
1362 _add_contacts_to_purple_blist(user, folder);
1363 }
1364 }
1365
1366 /* Add contacts for the root folder */
1367 _add_contacts_to_purple_blist(user, root_folder);
1368 }
1369 }
1370
1371 static void
_sync_contact_list(NMUser * user)1372 _sync_contact_list(NMUser *user)
1373 {
1374 /* Remove all buddies from the local list that are
1375 * not in the server side list and add all buddies
1376 * from the server side list that are not in
1377 * the local list
1378 */
1379 _remove_purple_buddies(user);
1380 _add_purple_buddies(user);
1381 user->clist_synched = TRUE;
1382 }
1383
1384 static void
_sync_privacy_lists(NMUser * user)1385 _sync_privacy_lists(NMUser *user)
1386 {
1387 GSList *node = NULL, *rem_list = NULL;
1388 PurpleConnection *gc;
1389 const char *name, *dn;
1390 NMUserRecord *user_record;
1391
1392 if (user == NULL)
1393 return;
1394
1395 gc = purple_account_get_connection(user->client_data);
1396 if (gc == NULL)
1397 return;
1398
1399 /* Set the Purple privacy setting */
1400 if (user->default_deny) {
1401 if (user->allow_list == NULL) {
1402 gc->account->perm_deny = PURPLE_PRIVACY_DENY_ALL;
1403 } else {
1404 gc->account->perm_deny = PURPLE_PRIVACY_ALLOW_USERS;
1405 }
1406 } else {
1407 if (user->deny_list == NULL) {
1408 gc->account->perm_deny = PURPLE_PRIVACY_ALLOW_ALL;
1409 } else {
1410 gc->account->perm_deny = PURPLE_PRIVACY_DENY_USERS;
1411 }
1412 }
1413
1414 /* Add stuff */
1415 for (node = user->allow_list; node; node = node->next) {
1416 user_record = nm_find_user_record(user, (char *)node->data);
1417 if (user_record)
1418 name = nm_user_record_get_display_id(user_record);
1419 else
1420 name =(char *)node->data;
1421
1422 if (!g_slist_find_custom(gc->account->permit,
1423 name, (GCompareFunc)purple_utf8_strcasecmp)) {
1424 purple_privacy_permit_add(gc->account, name , TRUE);
1425 }
1426 }
1427
1428 for (node = user->deny_list; node; node = node->next) {
1429 user_record = nm_find_user_record(user, (char *)node->data);
1430 if (user_record)
1431 name = nm_user_record_get_display_id(user_record);
1432 else
1433 name =(char *)node->data;
1434
1435 if (!g_slist_find_custom(gc->account->deny,
1436 name, (GCompareFunc)purple_utf8_strcasecmp)) {
1437 purple_privacy_deny_add(gc->account, name, TRUE);
1438 }
1439 }
1440
1441
1442 /* Remove stuff */
1443 for (node = gc->account->permit; node; node = node->next) {
1444 dn = nm_lookup_dn(user, (char *)node->data);
1445 if (dn != NULL &&
1446 !g_slist_find_custom(user->allow_list,
1447 dn, (GCompareFunc)purple_utf8_strcasecmp)) {
1448 rem_list = g_slist_append(rem_list, node->data);
1449 }
1450 }
1451
1452 if (rem_list) {
1453 for (node = rem_list; node; node = node->next) {
1454 purple_privacy_permit_remove(gc->account, (char *)node->data, TRUE);
1455 }
1456 g_slist_free(rem_list);
1457 rem_list = NULL;
1458 }
1459
1460 for (node = gc->account->deny; node; node = node->next) {
1461 dn = nm_lookup_dn(user, (char *)node->data);
1462 if (dn != NULL &&
1463 !g_slist_find_custom(user->deny_list,
1464 dn, (GCompareFunc)purple_utf8_strcasecmp)) {
1465 rem_list = g_slist_append(rem_list, node->data);
1466 }
1467 }
1468
1469 if (rem_list) {
1470 for (node = rem_list; node; node = node->next) {
1471 purple_privacy_deny_remove(gc->account, (char *)node->data, TRUE);
1472 }
1473 g_slist_free(rem_list);
1474 }
1475 }
1476
1477 /* Map known property tags to user-friendly strings */
1478 static const char *
_map_property_tag(const char * tag)1479 _map_property_tag(const char *tag)
1480 {
1481 if (tag == NULL) return NULL;
1482
1483 if (purple_strequal(tag, "telephoneNumber"))
1484 return _("Telephone Number");
1485 else if (purple_strequal(tag, "L"))
1486 return _("Location");
1487 else if (purple_strequal(tag, "OU"))
1488 return _("Department");
1489 else if (purple_strequal(tag, "personalTitle"))
1490 return _("Personal Title");
1491 else if (purple_strequal(tag, "Title"))
1492 return _("Job Title");
1493 else if (purple_strequal(tag, "mailstop"))
1494 return _("Mailstop");
1495 else if (purple_strequal(tag, "Internet EMail Address"))
1496 return _("Email Address");
1497 else
1498 return tag;
1499 }
1500
1501 /* Display a dialog box showing the properties for the given user record */
1502 static void
_show_info(PurpleConnection * gc,NMUserRecord * user_record,char * name)1503 _show_info(PurpleConnection * gc, NMUserRecord * user_record, char * name)
1504 {
1505 PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
1506 int count, i;
1507 NMProperty *property;
1508 const char *tag, *value;
1509
1510 tag = _("User ID");
1511 value = nm_user_record_get_userid(user_record);
1512 if (value) {
1513 purple_notify_user_info_add_pair(user_info, tag, value);
1514 }
1515
1516 /* tag = _("DN");
1517 value = nm_user_record_get_dn(user_record);
1518 if (value) {
1519 purple_notify_user_info_add_pair(user_info, tag, value);
1520 }
1521 */
1522
1523 tag = _("Full name");
1524 value = nm_user_record_get_full_name(user_record);
1525 if (value) {
1526 purple_notify_user_info_add_pair(user_info, tag, value);
1527 }
1528
1529 count = nm_user_record_get_property_count(user_record);
1530 for (i = 0; i < count; i++) {
1531 property = nm_user_record_get_property(user_record, i);
1532 if (property) {
1533 tag = _map_property_tag(nm_property_get_tag(property));
1534 value = nm_property_get_value(property);
1535 if (tag && value) {
1536 purple_notify_user_info_add_pair(user_info, tag, value);
1537 }
1538 nm_release_property(property);
1539 }
1540 }
1541
1542 purple_notify_userinfo(gc, name, user_info, NULL, NULL);
1543 purple_notify_user_info_destroy(user_info);
1544
1545 g_free(name);
1546 }
1547
1548 /* Send a join conference, the first item in the parms list is the
1549 * NMUser object and the second item is the conference to join.
1550 * This callback is passed to purple_request_action when we ask the
1551 * user if they want to join the conference.
1552 */
1553 static void
_join_conference_cb(GSList * parms)1554 _join_conference_cb(GSList * parms)
1555 {
1556 NMUser *user;
1557 NMConference *conference;
1558 NMERR_T rc = NM_OK;
1559
1560 if (parms == NULL || g_slist_length(parms) != 2)
1561 return;
1562
1563 user = g_slist_nth_data(parms, 0);
1564 conference = g_slist_nth_data(parms, 1);
1565
1566 if (user && conference) {
1567 rc = nm_send_join_conference(user, conference,
1568 _join_conf_resp_cb, conference);
1569 _check_for_disconnect(user, rc);
1570 }
1571
1572 g_slist_free(parms);
1573 }
1574
1575 /* Send a reject conference, the first item in the parms list is the
1576 * NMUser object and the second item is the conference to reject.
1577 * This callback is passed to purple_request_action when we ask the
1578 * user if they want to joing the conference.
1579 */
1580 static void
_reject_conference_cb(GSList * parms)1581 _reject_conference_cb(GSList * parms)
1582 {
1583 NMUser *user;
1584 NMConference *conference;
1585 NMERR_T rc = NM_OK;
1586
1587 if (parms == NULL || g_slist_length(parms) != 2)
1588 return;
1589
1590 user = g_slist_nth_data(parms, 0);
1591 conference = g_slist_nth_data(parms, 1);
1592
1593 if (user && conference) {
1594 rc = nm_send_reject_conference(user, conference, NULL, NULL);
1595 _check_for_disconnect(user, rc);
1596 }
1597
1598 g_slist_free(parms);
1599 }
1600
1601 static void
_initiate_conference_cb(PurpleBlistNode * node,gpointer ignored)1602 _initiate_conference_cb(PurpleBlistNode *node, gpointer ignored)
1603 {
1604 PurpleBuddy *buddy;
1605 PurpleConnection *gc;
1606
1607 NMUser *user;
1608 const char *conf_name;
1609 PurpleConversation *chat = NULL;
1610 NMUserRecord *user_record;
1611 NMConference *conference;
1612
1613 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
1614
1615 buddy = (PurpleBuddy *) node;
1616 gc = purple_account_get_connection(purple_buddy_get_account(buddy));
1617
1618 user = gc->proto_data;
1619 if (user == NULL)
1620 return;
1621
1622 /* We should already have a userrecord for the buddy */
1623 user_record = nm_find_user_record(user, purple_buddy_get_name(buddy));
1624 if (user_record == NULL)
1625 return;
1626
1627 conf_name = _get_conference_name(++user->conference_count);
1628 chat = serv_got_joined_chat(gc, user->conference_count, conf_name);
1629 if (chat) {
1630
1631 conference = nm_create_conference(NULL);
1632 nm_conference_set_data(conference, (gpointer) chat);
1633 nm_send_create_conference(user, conference, _createconf_resp_send_invite, user_record);
1634 nm_release_conference(conference);
1635 }
1636 }
1637
1638 const char *
_get_conference_name(int id)1639 _get_conference_name(int id)
1640 {
1641 static char *name = NULL;
1642
1643 if (name)
1644 g_free(name);
1645
1646 name = g_strdup_printf(_("GroupWise Conference %d"), id);
1647
1648 return name;
1649 }
1650
1651 static void
_show_privacy_locked_error(PurpleConnection * gc,NMUser * user)1652 _show_privacy_locked_error(PurpleConnection *gc, NMUser *user)
1653 {
1654 char *err;
1655
1656 err = g_strdup_printf(_("Unable to change server side privacy settings (%s)."),
1657 nm_error_to_string(NMERR_ADMIN_LOCKED));
1658 purple_notify_error(gc, NULL, err, NULL);
1659 g_free(err);
1660 }
1661
1662 /*******************************************************************************
1663 * Connect and recv callbacks
1664 ******************************************************************************/
1665
1666 static void
novell_ssl_connect_error(PurpleSslConnection * gsc,PurpleSslErrorType error,gpointer data)1667 novell_ssl_connect_error(PurpleSslConnection * gsc,
1668 PurpleSslErrorType error, gpointer data)
1669 {
1670 PurpleConnection *gc;
1671 NMUser *user;
1672
1673 gc = data;
1674 user = gc->proto_data;
1675 user->conn->ssl_conn->data = NULL;
1676
1677 purple_connection_ssl_error (gc, error);
1678 }
1679
1680 static void
novell_ssl_recv_cb(gpointer data,PurpleSslConnection * gsc,PurpleInputCondition condition)1681 novell_ssl_recv_cb(gpointer data, PurpleSslConnection * gsc,
1682 PurpleInputCondition condition)
1683 {
1684 PurpleConnection *gc = data;
1685 NMUser *user;
1686 NMERR_T rc;
1687
1688 if (gc == NULL)
1689 return;
1690
1691 user = gc->proto_data;
1692 if (user == NULL)
1693 return;
1694
1695 rc = nm_process_new_data(user);
1696 if (rc != NM_OK) {
1697
1698 if (_is_disconnect_error(rc)) {
1699
1700 purple_connection_error_reason(gc,
1701 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
1702 _("Error communicating with server. Closing connection."));
1703 } else {
1704 purple_debug(PURPLE_DEBUG_INFO, "novell",
1705 "Error processing event or response (%d).\n", rc);
1706 }
1707 }
1708 }
1709
1710 static void
novell_ssl_connected_cb(gpointer data,PurpleSslConnection * gsc,PurpleInputCondition cond)1711 novell_ssl_connected_cb(gpointer data, PurpleSslConnection * gsc,
1712 PurpleInputCondition cond)
1713 {
1714 PurpleConnection *gc = data;
1715 NMUser *user;
1716 NMConn *conn;
1717 NMERR_T rc = 0;
1718 const char *pwd = NULL;
1719 const char *my_addr = NULL;
1720 char *ua = NULL;
1721
1722 if (gc == NULL || gsc == NULL)
1723 return;
1724
1725 user = gc->proto_data;
1726 if ((user == NULL) || (conn = user->conn) == NULL)
1727 return;
1728
1729 purple_connection_update_progress(gc, _("Authenticating..."),
1730 2, NOVELL_CONNECT_STEPS);
1731
1732 my_addr = purple_network_get_my_ip(gsc->fd);
1733 pwd = purple_connection_get_password(gc);
1734 ua = _user_agent_string();
1735
1736 rc = nm_send_login(user, pwd, my_addr, ua, _login_resp_cb, NULL);
1737 if (rc == NM_OK) {
1738 conn->connected = TRUE;
1739 purple_ssl_input_add(gsc, novell_ssl_recv_cb, gc);
1740 } else {
1741 purple_connection_error_reason(gc,
1742 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
1743 _("Unable to connect"));
1744 }
1745
1746 purple_connection_update_progress(gc, _("Waiting for response..."),
1747 3, NOVELL_CONNECT_STEPS);
1748
1749 g_free(ua);
1750 }
1751
1752 /*******************************************************************************
1753 * Event callback and event handlers
1754 ******************************************************************************/
1755
1756 static void
_evt_receive_message(NMUser * user,NMEvent * event)1757 _evt_receive_message(NMUser * user, NMEvent * event)
1758 {
1759 NMUserRecord *user_record = NULL;
1760 NMContact *contact = NULL;
1761 PurpleConversation *gconv;
1762 NMConference *conference;
1763 PurpleMessageFlags flags;
1764 char *text = NULL;
1765
1766 text = g_markup_escape_text(nm_event_get_text(event), -1);
1767
1768 conference = nm_event_get_conference(event);
1769 if (conference) {
1770
1771 PurpleConversation *chat = nm_conference_get_data(conference);
1772
1773 /* Is this a single person 'conversation' or a conference? */
1774 if (chat == NULL && nm_conference_get_participant_count(conference) == 1) {
1775
1776 user_record = nm_find_user_record(user, nm_event_get_source(event));
1777 if (user_record) {
1778
1779 flags = 0;
1780 if (nm_event_get_type(event) == NMEVT_RECEIVE_AUTOREPLY)
1781 flags |= PURPLE_MESSAGE_AUTO_RESP;
1782
1783 serv_got_im(purple_account_get_connection(user->client_data),
1784 nm_user_record_get_display_id(user_record),
1785 text, flags,
1786 nm_event_get_gmt(event));
1787
1788 gconv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
1789 nm_user_record_get_display_id(user_record),
1790 (PurpleAccount *) user->client_data);
1791 if (gconv) {
1792
1793 contact = nm_find_contact(user, nm_event_get_source(event));
1794 if (contact) {
1795
1796 purple_conversation_set_title(
1797 gconv, nm_contact_get_display_name(contact));
1798
1799
1800 } else {
1801
1802 const char *name =
1803 nm_user_record_get_full_name(user_record);
1804
1805 if (name == NULL)
1806 name = nm_user_record_get_userid(user_record);
1807
1808 purple_conversation_set_title(gconv, name);
1809 }
1810
1811 }
1812
1813 } else {
1814 /* this should not happen, see the event code.
1815 * the event code will get the contact details from
1816 * the server if it does not have them before calling
1817 * the event callback.
1818 */
1819 }
1820
1821 } else if (chat) {
1822
1823 /* get the contact for send if we have one */
1824 NMContact *contact = nm_find_contact(user,
1825 nm_event_get_source(event));
1826
1827 /* get the user record for the sender */
1828 user_record = nm_find_user_record(user, nm_event_get_source(event));
1829 if (user_record) {
1830 const char *name = nm_contact_get_display_name(contact);
1831
1832 if (name == NULL) {
1833 name = nm_user_record_get_full_name(user_record);
1834 if (name == NULL)
1835 name = nm_user_record_get_display_id(user_record);
1836 }
1837
1838 serv_got_chat_in(purple_account_get_connection(user->client_data),
1839 purple_conv_chat_get_id(PURPLE_CONV_CHAT(chat)),
1840 name, 0, text, nm_event_get_gmt(event));
1841 }
1842 }
1843 }
1844
1845 g_free(text);
1846 }
1847
1848 static void
_evt_conference_left(NMUser * user,NMEvent * event)1849 _evt_conference_left(NMUser * user, NMEvent * event)
1850 {
1851 PurpleConversation *chat;
1852 NMConference *conference;
1853
1854 conference = nm_event_get_conference(event);
1855 if (conference) {
1856 chat = nm_conference_get_data(conference);
1857 if (chat) {
1858 NMUserRecord *ur = nm_find_user_record(user,
1859 nm_event_get_source(event));
1860
1861 if (ur)
1862 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(chat),
1863 nm_user_record_get_display_id(ur),
1864 NULL);
1865 }
1866 }
1867 }
1868
1869 static void
_evt_conference_invite_notify(NMUser * user,NMEvent * event)1870 _evt_conference_invite_notify(NMUser * user, NMEvent * event)
1871 {
1872 PurpleConversation *gconv;
1873 NMConference *conference;
1874 NMUserRecord *user_record = NULL;
1875 char *str = NULL;
1876
1877 user_record = nm_find_user_record(user, nm_event_get_source(event));
1878 conference = nm_event_get_conference(event);
1879 if (user_record && conference) {
1880 gconv = nm_conference_get_data(conference);
1881 str = g_strdup_printf(_("%s has been invited to this conversation."),
1882 nm_user_record_get_display_id(user_record));
1883 purple_conversation_write(gconv, NULL, str,
1884 PURPLE_MESSAGE_SYSTEM, time(NULL));
1885 g_free(str);
1886 }
1887 }
1888
1889 static void
_evt_conference_invite(NMUser * user,NMEvent * event)1890 _evt_conference_invite(NMUser * user, NMEvent * event)
1891 {
1892 NMUserRecord *ur;
1893 PurpleConnection *gc;
1894 GSList *parms = NULL;
1895 const char *title = NULL;
1896 const char *secondary = NULL;
1897 const char *name = NULL;
1898 char *primary = NULL;
1899 time_t gmt;
1900
1901 ur = nm_find_user_record(user, nm_event_get_source(event));
1902 if (ur)
1903 name = nm_user_record_get_full_name(ur);
1904
1905 if (name == NULL)
1906 name = nm_event_get_source(event);
1907
1908 gmt = nm_event_get_gmt(event);
1909 title = _("Invitation to Conversation");
1910 primary = g_strdup_printf(_("Invitation from: %s\n\nSent: %s"),
1911 name, purple_date_format_full(localtime(&gmt)));
1912 secondary = _("Would you like to join the conversation?");
1913
1914 /* Set up parms list for the callbacks
1915 * We need to send the NMUser object and
1916 * the NMConference object to the callbacks
1917 */
1918 parms = NULL;
1919 parms = g_slist_append(parms, user);
1920 parms = g_slist_append(parms, nm_event_get_conference(event));
1921
1922 /* Prompt the user */
1923 /* TODO: Would it be better to use serv_got_chat_invite() here? */
1924 gc = purple_account_get_connection(user->client_data);
1925 purple_request_action(gc, title, primary, secondary,
1926 PURPLE_DEFAULT_ACTION_NONE,
1927 purple_connection_get_account(gc), name, NULL,
1928 parms, 2,
1929 _("Yes"), G_CALLBACK(_join_conference_cb),
1930 _("No"), G_CALLBACK(_reject_conference_cb));
1931
1932 g_free(primary);
1933 }
1934
1935
1936 static void
_evt_conference_joined(NMUser * user,NMEvent * event)1937 _evt_conference_joined(NMUser * user, NMEvent * event)
1938 {
1939 PurpleConversation *chat = NULL;
1940 PurpleConnection *gc;
1941 NMConference *conference = NULL;
1942 NMUserRecord *ur = NULL;
1943 const char *name;
1944 const char *conf_name;
1945
1946 gc = purple_account_get_connection(user->client_data);
1947 if (gc == NULL)
1948 return;
1949
1950 conference = nm_event_get_conference(event);
1951 if (conference) {
1952 chat = nm_conference_get_data(conference);
1953 if (nm_conference_get_participant_count(conference) == 2 && chat == NULL) {
1954 ur = nm_conference_get_participant(conference, 0);
1955 if (ur) {
1956 conf_name = _get_conference_name(++user->conference_count);
1957 chat =
1958 serv_got_joined_chat(gc, user->conference_count, conf_name);
1959 if (chat) {
1960
1961 nm_conference_set_data(conference, (gpointer) chat);
1962
1963 name = nm_user_record_get_display_id(ur);
1964 purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat), name, NULL,
1965 PURPLE_CBFLAGS_NONE, TRUE);
1966
1967 }
1968 }
1969 }
1970
1971 if (chat != NULL) {
1972 ur = nm_find_user_record(user, nm_event_get_source(event));
1973 if (ur) {
1974 name = nm_user_record_get_display_id(ur);
1975 if (!purple_conv_chat_find_user(PURPLE_CONV_CHAT(chat), name)) {
1976 purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat), name, NULL,
1977 PURPLE_CBFLAGS_NONE, TRUE);
1978 }
1979 }
1980 }
1981 }
1982 }
1983
1984 static void
_evt_status_change(NMUser * user,NMEvent * event)1985 _evt_status_change(NMUser * user, NMEvent * event)
1986 {
1987 PurpleBuddy *buddy = NULL;
1988 GSList *buddies;
1989 GSList *bnode;
1990 NMUserRecord *user_record;
1991 const char *display_id;
1992 int status;
1993
1994 user_record = nm_event_get_user_record(event);
1995 if (user_record) {
1996
1997 /* Retrieve new status */
1998 status = nm_user_record_get_status(user_record);
1999
2000 /* Update status for buddy in all folders */
2001 display_id = nm_user_record_get_display_id(user_record);
2002 buddies = purple_find_buddies(user->client_data, display_id);
2003 for (bnode = buddies; bnode; bnode = bnode->next) {
2004 buddy = (PurpleBuddy *) bnode->data;
2005 if (buddy) {
2006 _update_buddy_status(user, buddy, status, nm_event_get_gmt(event));
2007 }
2008 }
2009
2010 g_slist_free(buddies);
2011
2012 }
2013 }
2014
2015 static void
_evt_user_disconnect(NMUser * user,NMEvent * event)2016 _evt_user_disconnect(NMUser * user, NMEvent * event)
2017 {
2018 PurpleConnection *gc;
2019 PurpleAccount *account = user->client_data;
2020
2021 gc = purple_account_get_connection(account);
2022 if (gc)
2023 {
2024 if (!purple_account_get_remember_password(account))
2025 purple_account_set_password(account, NULL);
2026 purple_connection_error_reason(gc,
2027 PURPLE_CONNECTION_ERROR_NAME_IN_USE,
2028 _("You have signed on from another location"));
2029 }
2030 }
2031
2032 static void
_evt_user_typing(NMUser * user,NMEvent * event)2033 _evt_user_typing(NMUser * user, NMEvent * event)
2034 {
2035 PurpleConnection *gc;
2036 NMUserRecord *user_record = NULL;
2037
2038 gc = purple_account_get_connection((PurpleAccount *) user->client_data);
2039 if (gc) {
2040 user_record = nm_find_user_record(user, nm_event_get_source(event));
2041 if (user_record) {
2042 serv_got_typing(gc, nm_user_record_get_display_id(user_record),
2043 30, PURPLE_TYPING);
2044 }
2045 }
2046 }
2047
2048 static void
_evt_user_not_typing(NMUser * user,NMEvent * event)2049 _evt_user_not_typing(NMUser * user, NMEvent * event)
2050 {
2051 PurpleConnection *gc;
2052 NMUserRecord *user_record;
2053
2054 gc = purple_account_get_connection((PurpleAccount *) user->client_data);
2055 if (gc) {
2056 user_record = nm_find_user_record(user, nm_event_get_source(event));
2057 if (user_record) {
2058 serv_got_typing_stopped(gc,
2059 nm_user_record_get_display_id(user_record));
2060 }
2061 }
2062 }
2063
2064 static void
_evt_undeliverable_status(NMUser * user,NMEvent * event)2065 _evt_undeliverable_status(NMUser * user, NMEvent * event)
2066 {
2067 NMUserRecord *ur;
2068 PurpleConversation *gconv;
2069 char *str;
2070
2071 ur = nm_find_user_record(user, nm_event_get_source(event));
2072 if (ur) {
2073 /* XXX - Should this be PURPLE_CONV_TYPE_IM? */
2074 gconv =
2075 purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
2076 nm_user_record_get_display_id(ur),
2077 user->client_data);
2078 if (gconv) {
2079 const char *name = nm_user_record_get_full_name(ur);
2080
2081 if (name == NULL) {
2082 name = nm_user_record_get_display_id(ur);
2083 }
2084 str = g_strdup_printf(_("%s appears to be offline and did not receive"
2085 " the message that you just sent."), name);
2086 purple_conversation_write(gconv, NULL, str,
2087 PURPLE_MESSAGE_SYSTEM, time(NULL));
2088 g_free(str);
2089 }
2090 }
2091 }
2092
2093 static void
_event_callback(NMUser * user,NMEvent * event)2094 _event_callback(NMUser * user, NMEvent * event)
2095 {
2096 if (user == NULL || event == NULL)
2097 return;
2098
2099 switch (nm_event_get_type(event)) {
2100 case NMEVT_STATUS_CHANGE:
2101 _evt_status_change(user, event);
2102 break;
2103 case NMEVT_RECEIVE_AUTOREPLY:
2104 case NMEVT_RECEIVE_MESSAGE:
2105 _evt_receive_message(user, event);
2106 break;
2107 case NMEVT_USER_DISCONNECT:
2108 _evt_user_disconnect(user, event);
2109 break;
2110 case NMEVT_USER_TYPING:
2111 _evt_user_typing(user, event);
2112 break;
2113 case NMEVT_USER_NOT_TYPING:
2114 _evt_user_not_typing(user, event);
2115 break;
2116 case NMEVT_SERVER_DISCONNECT:
2117 /* Nothing to do? */
2118 break;
2119 case NMEVT_INVALID_RECIPIENT:
2120 break;
2121 case NMEVT_UNDELIVERABLE_STATUS:
2122 _evt_undeliverable_status(user, event);
2123 break;
2124 case NMEVT_CONFERENCE_INVITE_NOTIFY:
2125 /* Someone else has been invited to join a
2126 * conference that we are currently a part of
2127 */
2128 _evt_conference_invite_notify(user, event);
2129 break;
2130 case NMEVT_CONFERENCE_INVITE:
2131 /* We have been invited to join a conference */
2132 _evt_conference_invite(user, event);
2133 break;
2134 case NMEVT_CONFERENCE_JOINED:
2135 /* Some one has joined a conference that we
2136 * are a part of
2137 */
2138 _evt_conference_joined(user, event);
2139 break;
2140 case NMEVT_CONFERENCE_LEFT:
2141 /* Someone else has left a conference that we
2142 * are currently a part of
2143 */
2144 _evt_conference_left(user, event);
2145 break;
2146 default:
2147 purple_debug(PURPLE_DEBUG_INFO, "novell",
2148 "_event_callback(): unhandled event, %d\n",
2149 nm_event_get_type(event));
2150 break;
2151 }
2152 }
2153
2154 /*******************************************************************************
2155 * Prpl Ops
2156 ******************************************************************************/
2157
2158 static void
novell_login(PurpleAccount * account)2159 novell_login(PurpleAccount * account)
2160 {
2161 PurpleConnection *gc;
2162 NMUser *user = NULL;
2163 const char *server;
2164 const char *name;
2165 int port;
2166
2167 if (account == NULL)
2168 return;
2169
2170 gc = purple_account_get_connection(account);
2171 if (gc == NULL)
2172 return;
2173
2174 server = purple_account_get_string(account, "server", NULL);
2175 if (server == NULL || *server == '\0') {
2176
2177 /* TODO: Would be nice to prompt if not set!
2178 * purple_request_fields(gc, _("Server Address"),...);
2179 */
2180
2181 /* ...but for now just error out with a nice message. */
2182 purple_connection_error_reason(gc,
2183 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
2184 _("Unable to connect to server. Please enter the "
2185 "address of the server to which you wish to connect."));
2186 return;
2187 }
2188
2189 port = purple_account_get_int(account, "port", DEFAULT_PORT);
2190 name = purple_account_get_username(account);
2191
2192 user = nm_initialize_user(name, server, port, account, _event_callback);
2193 if (user && user->conn) {
2194 /* save user */
2195 gc->proto_data = user;
2196
2197 /* connect to the server */
2198 purple_connection_update_progress(gc, _("Connecting"),
2199 1, NOVELL_CONNECT_STEPS);
2200
2201 user->conn->use_ssl = TRUE;
2202
2203 user->conn->ssl_conn = g_new0(NMSSLConn, 1);
2204 user->conn->ssl_conn->read = (nm_ssl_read_cb) purple_ssl_read;
2205 user->conn->ssl_conn->write = (nm_ssl_write_cb) purple_ssl_write;
2206
2207 user->conn->ssl_conn->data = purple_ssl_connect(user->client_data,
2208 user->conn->addr, user->conn->port,
2209 novell_ssl_connected_cb, novell_ssl_connect_error, gc);
2210 if (user->conn->ssl_conn->data == NULL) {
2211 purple_connection_error_reason(gc,
2212 PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
2213 _("SSL support unavailable"));
2214 }
2215 }
2216 }
2217
2218 static void
novell_close(PurpleConnection * gc)2219 novell_close(PurpleConnection * gc)
2220 {
2221 NMUser *user;
2222 NMConn *conn;
2223
2224 if (gc == NULL)
2225 return;
2226
2227 user = gc->proto_data;
2228 if (user) {
2229 conn = user->conn;
2230 if (conn && conn->ssl_conn) {
2231 purple_ssl_close(user->conn->ssl_conn->data);
2232 }
2233 nm_deinitialize_user(user);
2234 }
2235 gc->proto_data = NULL;
2236 }
2237
2238 static int
novell_send_im(PurpleConnection * gc,const char * name,const char * message_body,PurpleMessageFlags flags)2239 novell_send_im(PurpleConnection * gc, const char *name,
2240 const char *message_body, PurpleMessageFlags flags)
2241 {
2242 NMUserRecord *user_record = NULL;
2243 NMConference *conf = NULL;
2244 NMMessage *message;
2245 NMUser *user;
2246 const char *dn = NULL;
2247 char *plain;
2248 gboolean done = TRUE, created_conf = FALSE;
2249 NMERR_T rc = NM_OK;
2250
2251 if (gc == NULL || name == NULL ||
2252 message_body == NULL || *message_body == '\0')
2253 return 0;
2254
2255 user = gc->proto_data;
2256 if (user == NULL)
2257 return 0;
2258
2259 /* Create a new message */
2260 plain = purple_unescape_html(message_body);
2261 message = nm_create_message(plain);
2262 g_free(plain);
2263
2264 /* Need to get the DN for the buddy so we can look up the convo */
2265 dn = nm_lookup_dn(user, name);
2266
2267 /* Do we already know about the sender? */
2268 user_record = nm_find_user_record(user, dn);
2269 if (user_record) {
2270
2271 /* Do we already have an instantiated conference? */
2272 conf = nm_find_conversation(user, dn);
2273 if (conf == NULL) {
2274
2275 /* If not, create a blank conference */
2276 conf = nm_create_conference(NULL);
2277 created_conf = TRUE;
2278
2279 nm_conference_add_participant(conf, user_record);
2280 }
2281
2282 nm_message_set_conference(message, conf);
2283
2284 /* Make sure conference is instantiated */
2285 if (!nm_conference_is_instantiated(conf)) {
2286
2287 /* It is not, so send the createconf. We will
2288 * have to finish sending the message when we
2289 * get the response with the new conference guid.
2290 */
2291 rc = nm_send_create_conference(user, conf, _createconf_resp_send_msg, message);
2292 _check_for_disconnect(user, rc);
2293
2294 done = FALSE;
2295 }
2296
2297 } else {
2298
2299 /* If we don't have details for the user, then we don't have
2300 * a conference yet. So create one and send the getdetails
2301 * to the server. We will have to finish sending the message
2302 * when we get the response from the server.
2303 */
2304 conf = nm_create_conference(NULL);
2305 created_conf = TRUE;
2306
2307 nm_message_set_conference(message, conf);
2308
2309 rc = nm_send_get_details(user, name, _get_details_resp_send_msg, message);
2310 _check_for_disconnect(user, rc);
2311
2312 done = FALSE;
2313 }
2314
2315 if (done) {
2316
2317 /* Did we find everything we needed? */
2318 rc = nm_send_message(user, message, _send_message_resp_cb);
2319 _check_for_disconnect(user, rc);
2320
2321 nm_release_message(message);
2322 }
2323
2324 if (created_conf && conf)
2325 nm_release_conference(conf);
2326
2327 return 1;
2328 }
2329
2330 static unsigned int
novell_send_typing(PurpleConnection * gc,const char * name,PurpleTypingState state)2331 novell_send_typing(PurpleConnection * gc, const char *name, PurpleTypingState state)
2332 {
2333 NMConference *conf = NULL;
2334 NMUser *user;
2335 const char *dn = NULL;
2336 NMERR_T rc = NM_OK;
2337
2338 if (gc == NULL || name == NULL)
2339 return 0;
2340
2341 user = gc->proto_data;
2342 if (user == NULL)
2343 return 0;
2344
2345 /* Need to get the DN for the buddy so we can look up the convo */
2346 dn = nm_lookup_dn(user, name);
2347 if (dn) {
2348
2349 /* Now find the conference in our list */
2350 conf = nm_find_conversation(user, dn);
2351 if (conf) {
2352
2353 rc = nm_send_typing(user, conf,
2354 ((state == PURPLE_TYPING) ? TRUE : FALSE), NULL);
2355 _check_for_disconnect(user, rc);
2356
2357 }
2358
2359 }
2360
2361 return 0;
2362 }
2363
2364 static void
novell_convo_closed(PurpleConnection * gc,const char * who)2365 novell_convo_closed(PurpleConnection * gc, const char *who)
2366 {
2367 NMUser *user;
2368 NMConference *conf;
2369 const char *dn;
2370 NMERR_T rc = NM_OK;
2371
2372 if (gc == NULL || who == NULL)
2373 return;
2374
2375 user = gc->proto_data;
2376 if (user && (dn = nm_lookup_dn(user, who))) {
2377 conf = nm_find_conversation(user, dn);
2378 if (conf) {
2379 rc = nm_send_leave_conference(user, conf, NULL, NULL);
2380 _check_for_disconnect(user, rc);
2381 }
2382 }
2383 }
2384
2385 static void
novell_chat_leave(PurpleConnection * gc,int id)2386 novell_chat_leave(PurpleConnection * gc, int id)
2387 {
2388 NMConference *conference;
2389 NMUser *user;
2390 PurpleConversation *chat;
2391 GSList *cnode;
2392 NMERR_T rc = NM_OK;
2393
2394 if (gc == NULL)
2395 return;
2396
2397 user = gc->proto_data;
2398 if (user == NULL)
2399 return;
2400
2401 for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
2402 conference = cnode->data;
2403 if (conference && (chat = nm_conference_get_data(conference))) {
2404 if (purple_conv_chat_get_id(PURPLE_CONV_CHAT(chat)) == id) {
2405 rc = nm_send_leave_conference(user, conference, NULL, NULL);
2406 _check_for_disconnect(user, rc);
2407 break;
2408 }
2409 }
2410 }
2411
2412 serv_got_chat_left(gc, id);
2413 }
2414
2415 static void
novell_chat_invite(PurpleConnection * gc,int id,const char * message,const char * who)2416 novell_chat_invite(PurpleConnection *gc, int id,
2417 const char *message, const char *who)
2418 {
2419 NMConference *conference;
2420 NMUser *user;
2421 PurpleConversation *chat;
2422 GSList *cnode;
2423 NMERR_T rc = NM_OK;
2424 NMUserRecord *user_record = NULL;
2425
2426 if (gc == NULL)
2427 return;
2428
2429 user = gc->proto_data;
2430 if (user == NULL)
2431 return;
2432
2433 user_record = nm_find_user_record(user, who);
2434 if (user_record == NULL) {
2435 rc = nm_send_get_details(user, who, _get_details_resp_send_invite, GINT_TO_POINTER(id));
2436 _check_for_disconnect(user, rc);
2437 return;
2438 }
2439
2440 for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
2441 conference = cnode->data;
2442 if (conference && (chat = nm_conference_get_data(conference))) {
2443 if (purple_conv_chat_get_id(PURPLE_CONV_CHAT(chat)) == id) {
2444 rc = nm_send_conference_invite(user, conference, user_record,
2445 message, _sendinvite_resp_cb, NULL);
2446 _check_for_disconnect(user, rc);
2447 break;
2448 }
2449 }
2450 }
2451 }
2452
2453 static int
novell_chat_send(PurpleConnection * gc,int id,const char * text,PurpleMessageFlags flags)2454 novell_chat_send(PurpleConnection * gc, int id, const char *text, PurpleMessageFlags flags)
2455 {
2456 NMConference *conference;
2457 PurpleConversation *chat;
2458 GSList *cnode;
2459 NMMessage *message;
2460 NMUser *user;
2461 NMERR_T rc = NM_OK;
2462 const char *name;
2463 char *str, *plain;
2464
2465 if (gc == NULL || text == NULL)
2466 return -1;
2467
2468 user = gc->proto_data;
2469 if (user == NULL)
2470 return -1;
2471
2472 plain = purple_unescape_html(text);
2473 message = nm_create_message(plain);
2474 g_free(plain);
2475
2476 for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
2477 conference = cnode->data;
2478 if (conference && (chat = nm_conference_get_data(conference))) {
2479 if (purple_conv_chat_get_id(PURPLE_CONV_CHAT(chat)) == id) {
2480
2481 nm_message_set_conference(message, conference);
2482
2483 /* check to see if the conference is instatiated yet */
2484 if (!nm_conference_is_instantiated(conference)) {
2485 nm_message_add_ref(message);
2486 nm_send_create_conference(user, conference, _createconf_resp_send_msg, message);
2487 } else {
2488 rc = nm_send_message(user, message, _send_message_resp_cb);
2489 }
2490
2491 nm_release_message(message);
2492
2493 if (!_check_for_disconnect(user, rc)) {
2494
2495 /* Use the account alias if it is set */
2496 name = purple_account_get_alias(user->client_data);
2497 if (name == NULL || *name == '\0') {
2498
2499 /* If there is no account alias, try full name */
2500 name = nm_user_record_get_full_name(user->user_record);
2501 if (name == NULL || *name == '\0') {
2502
2503 /* Fall back to the username that we are signed in with */
2504 name = purple_account_get_username(user->client_data);
2505 }
2506 }
2507
2508 serv_got_chat_in(gc, id, name, flags, text, time(NULL));
2509 return 0;
2510 } else
2511 return -1;
2512
2513 }
2514 }
2515 }
2516
2517
2518 /* The conference was not found, must be closed */
2519 chat = purple_find_chat(gc, id);
2520 if (chat) {
2521 str = g_strdup(_("This conference has been closed."
2522 " No more messages can be sent."));
2523 purple_conversation_write(chat, NULL, str, PURPLE_MESSAGE_SYSTEM, time(NULL));
2524 g_free(str);
2525 }
2526
2527 if (message)
2528 nm_release_message(message);
2529
2530 return -1;
2531 }
2532
2533 static void
novell_add_buddy(PurpleConnection * gc,PurpleBuddy * buddy,PurpleGroup * group)2534 novell_add_buddy(PurpleConnection * gc, PurpleBuddy *buddy, PurpleGroup * group)
2535 {
2536 NMFolder *folder = NULL;
2537 NMContact *contact;
2538 NMUser *user;
2539 NMERR_T rc = NM_OK;
2540 const char *alias, *gname, *bname;
2541
2542 if (gc == NULL || buddy == NULL || group == NULL)
2543 return;
2544
2545 user = (NMUser *) purple_connection_get_protocol_data(gc);
2546 if (user == NULL)
2547 return;
2548
2549 /* If we haven't synched the contact list yet, ignore
2550 * the add_buddy calls. Server side list is the master.
2551 */
2552 if (!user->clist_synched)
2553 return;
2554
2555 /* Don't re-add a buddy that is already on our contact list */
2556 if (nm_find_user_record(user, purple_buddy_get_name(buddy)) != NULL)
2557 return;
2558
2559 contact = nm_create_contact();
2560 nm_contact_set_dn(contact, purple_buddy_get_name(buddy));
2561
2562 /* Remove the PurpleBuddy (we will add it back after adding it
2563 * to the server side list). Save the alias if there is one.
2564 */
2565 alias = purple_buddy_get_alias(buddy);
2566 bname = purple_buddy_get_name(buddy);
2567 if (alias && !purple_strequal(alias, bname))
2568 nm_contact_set_display_name(contact, alias);
2569
2570 purple_blist_remove_buddy(buddy);
2571 buddy = NULL;
2572
2573 gname = purple_group_get_name(group);
2574 if (purple_strequal(gname, NM_ROOT_FOLDER_NAME)) {
2575 gname = "";
2576 }
2577
2578 folder = nm_find_folder(user, gname);
2579 if (folder) {
2580
2581 /* We have everything that we need, so send the createcontact */
2582 rc = nm_send_create_contact(user, folder, contact,
2583 _create_contact_resp_cb, contact);
2584
2585 } else {
2586
2587 /* Need to create the folder before we can add the contact */
2588 rc = nm_send_create_folder(user, gname,
2589 _create_folder_resp_add_contact, contact);
2590 }
2591
2592 _check_for_disconnect(user, rc);
2593
2594 }
2595
2596 static void
novell_remove_buddy(PurpleConnection * gc,PurpleBuddy * buddy,PurpleGroup * group)2597 novell_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
2598 {
2599 NMContact *contact;
2600 NMFolder *folder;
2601 NMUser *user;
2602 const char *dn, *gname;
2603 NMERR_T rc = NM_OK;
2604
2605 if (gc == NULL || buddy == NULL || group == NULL)
2606 return;
2607
2608 user = (NMUser *) gc->proto_data;
2609 if (user && (dn = nm_lookup_dn(user, purple_buddy_get_name(buddy)))) {
2610 gname = purple_group_get_name(group);
2611 if (purple_strequal(gname, NM_ROOT_FOLDER_NAME)) {
2612 gname = "";
2613 }
2614 folder = nm_find_folder(user, gname);
2615 if (folder) {
2616 contact = nm_folder_find_contact(folder, dn);
2617 if (contact) {
2618
2619 /* Remove the buddy from the contact */
2620 nm_contact_set_data(contact, NULL);
2621
2622 /* Tell the server to remove the contact */
2623 rc = nm_send_remove_contact(user, folder, contact,
2624 _remove_contact_resp_cb, NULL);
2625 _check_for_disconnect(user, rc);
2626 }
2627 }
2628 }
2629 }
2630
2631 static void
novell_remove_group(PurpleConnection * gc,PurpleGroup * group)2632 novell_remove_group(PurpleConnection * gc, PurpleGroup *group)
2633 {
2634 NMUser *user;
2635 NMERR_T rc = NM_OK;
2636
2637 if (gc == NULL || group == NULL)
2638 return;
2639
2640 user = (NMUser *) gc->proto_data;
2641 if (user) {
2642 NMFolder *folder = nm_find_folder(user, purple_group_get_name(group));
2643
2644 if (folder) {
2645 rc = nm_send_remove_folder(user, folder,
2646 _remove_folder_resp_cb, NULL);
2647 _check_for_disconnect(user, rc);
2648 }
2649 }
2650 }
2651
2652 static void
novell_alias_buddy(PurpleConnection * gc,const char * name,const char * alias)2653 novell_alias_buddy(PurpleConnection * gc, const char *name, const char *alias)
2654 {
2655 NMContact *contact;
2656 NMUser *user;
2657 GList *contacts = NULL;
2658 GList *cnode = NULL;
2659 const char *dn = NULL, *fname = NULL;
2660 NMERR_T rc = NM_OK;
2661
2662 if (gc == NULL || name == NULL || alias == NULL)
2663 return;
2664
2665 user = (NMUser *) gc->proto_data;
2666 if (user && (dn = nm_lookup_dn(user, name))) {
2667
2668 /* Alias all of instances of the contact */
2669 contacts = nm_find_contacts(user, dn);
2670 for (cnode = contacts; cnode != NULL; cnode = cnode->next) {
2671 contact = (NMContact *) cnode->data;
2672 if (contact) {
2673 PurpleGroup *group = NULL;
2674 PurpleBuddy *buddy;
2675 NMFolder *folder;
2676
2677 /* Alias the Purple buddy? */
2678 folder = nm_find_folder_by_id(user,
2679 nm_contact_get_parent_id(contact));
2680 if (folder) {
2681 fname = nm_folder_get_name(folder);
2682 if (*fname == '\0') {
2683 fname = NM_ROOT_FOLDER_NAME;
2684 }
2685 group = purple_find_group(fname);
2686 }
2687
2688 if (group) {
2689 const char *balias;
2690 buddy = purple_find_buddy_in_group(user->client_data,
2691 name, group);
2692 balias = buddy ? purple_buddy_get_local_buddy_alias(buddy) : NULL;
2693 if (balias && !purple_strequal(balias, alias))
2694 purple_blist_alias_buddy(buddy, alias);
2695 }
2696
2697 /* Tell the server to alias the contact */
2698 rc = nm_send_rename_contact(user, contact, alias,
2699 _rename_contact_resp_cb, NULL);
2700 _check_for_disconnect(user, rc);
2701 }
2702 }
2703 if (contacts)
2704 g_list_free(contacts);
2705 }
2706 }
2707
2708 static void
novell_group_buddy(PurpleConnection * gc,const char * name,const char * old_group_name,const char * new_group_name)2709 novell_group_buddy(PurpleConnection * gc,
2710 const char *name, const char *old_group_name,
2711 const char *new_group_name)
2712 {
2713 NMFolder *old_folder;
2714 NMFolder *new_folder;
2715 NMContact *contact;
2716 NMUser *user;
2717 const char *dn;
2718 NMERR_T rc = NM_OK;
2719
2720 if (gc == NULL || name == NULL ||
2721 old_group_name == NULL || new_group_name == NULL)
2722 return;
2723
2724 user = (NMUser *) gc->proto_data;
2725 if (user && (dn = nm_lookup_dn(user, name))) {
2726
2727 /* Find the old folder */
2728 if (purple_strequal(old_group_name, NM_ROOT_FOLDER_NAME)) {
2729 old_folder = nm_get_root_folder(user);
2730 if (nm_folder_find_contact(old_folder, dn) == NULL)
2731 old_folder = nm_find_folder(user, old_group_name);
2732 } else {
2733 old_folder = nm_find_folder(user, old_group_name);
2734 }
2735
2736 if (old_folder && (contact = nm_folder_find_contact(old_folder, dn))) {
2737
2738 /* Find the new folder */
2739 new_folder = nm_find_folder(user, new_group_name);
2740 if (new_folder == NULL) {
2741 if (purple_strequal(new_group_name, NM_ROOT_FOLDER_NAME))
2742 new_folder = nm_get_root_folder(user);
2743 }
2744
2745 if (new_folder) {
2746
2747 /* Tell the server to move the contact to the new folder */
2748 rc = nm_send_move_contact(user, contact, new_folder,
2749 _move_contact_resp_cb, NULL);
2750
2751 } else {
2752
2753 nm_contact_add_ref(contact);
2754
2755 /* Remove the old contact first */
2756 nm_send_remove_contact(user, old_folder, contact,
2757 _remove_contact_resp_cb, NULL);
2758
2759 /* New folder does not exist yet, so create it */
2760 rc = nm_send_create_folder(user, new_group_name,
2761 _create_folder_resp_move_contact,
2762 contact);
2763 }
2764
2765 _check_for_disconnect(user, rc);
2766 }
2767 }
2768 }
2769
2770 static void
novell_rename_group(PurpleConnection * gc,const char * old_name,PurpleGroup * group,GList * moved_buddies)2771 novell_rename_group(PurpleConnection * gc, const char *old_name,
2772 PurpleGroup *group, GList *moved_buddies)
2773 {
2774 NMERR_T rc = NM_OK;
2775 NMFolder *folder;
2776 NMUser *user;
2777
2778 if (gc == NULL || old_name == NULL || group == NULL || moved_buddies == NULL) {
2779 return;
2780 }
2781
2782 user = gc->proto_data;
2783 if (user) {
2784 const char *gname = purple_group_get_name(group);
2785 /* Does new folder exist already? */
2786 if (nm_find_folder(user, gname)) {
2787 /* purple_blist_rename_group() adds the buddies
2788 * to the new group and removes the old group...
2789 * so there is nothing more to do here.
2790 */
2791 return;
2792 }
2793
2794 if (purple_strequal(old_name, NM_ROOT_FOLDER_NAME)) {
2795 /* Can't rename the root folder ... need to revisit this */
2796 return;
2797 }
2798
2799 folder = nm_find_folder(user, old_name);
2800 if (folder) {
2801 rc = nm_send_rename_folder(user, folder, gname,
2802 _rename_folder_resp_cb, NULL);
2803 _check_for_disconnect(user, rc);
2804 }
2805 }
2806 }
2807
2808 static const char *
novell_list_icon(PurpleAccount * account,PurpleBuddy * buddy)2809 novell_list_icon(PurpleAccount * account, PurpleBuddy * buddy)
2810 {
2811 return "novell";
2812 }
2813
2814 static void
novell_tooltip_text(PurpleBuddy * buddy,PurpleNotifyUserInfo * user_info,gboolean full)2815 novell_tooltip_text(PurpleBuddy * buddy, PurpleNotifyUserInfo * user_info, gboolean full)
2816 {
2817 NMUserRecord *user_record = NULL;
2818 PurpleConnection *gc;
2819 NMUser *user;
2820 int status = 0;
2821 const char *status_str = NULL;
2822 const char *text = NULL;
2823
2824 if (buddy == NULL)
2825 return;
2826
2827 gc = purple_account_get_connection(purple_buddy_get_account(buddy));
2828 if (gc == NULL || (user = gc->proto_data) == NULL)
2829 return;
2830
2831 if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
2832 user_record = nm_find_user_record(user, purple_buddy_get_name(buddy));
2833 if (user_record) {
2834 status = nm_user_record_get_status(user_record);
2835 text = nm_user_record_get_status_text(user_record);
2836 /* No custom text, so default it ... */
2837 switch (status) {
2838 case NM_STATUS_AVAILABLE:
2839 status_str = _("Available");
2840 break;
2841 case NM_STATUS_AWAY:
2842 status_str = _("Away");
2843 break;
2844 case NM_STATUS_BUSY:
2845 status_str = _("Busy");
2846 break;
2847 case NM_STATUS_AWAY_IDLE:
2848 status_str = _("Idle");
2849 break;
2850 case NM_STATUS_OFFLINE:
2851 status_str = _("Offline");
2852 break;
2853 default:
2854 status_str = _("Unknown");
2855 break;
2856 }
2857
2858 purple_notify_user_info_add_pair(user_info, _("Status"), status_str);
2859
2860 if (text)
2861 purple_notify_user_info_add_pair(user_info, _("Message"), text);
2862 }
2863 }
2864 }
2865
2866 static void
novell_set_idle(PurpleConnection * gc,int time)2867 novell_set_idle(PurpleConnection * gc, int time)
2868 {
2869 NMUser *user;
2870 NMERR_T rc = NM_OK;
2871 const char *id = NULL;
2872 PurpleStatus *status = NULL;
2873
2874 if (gc == NULL)
2875 return;
2876
2877 user = gc->proto_data;
2878 if (user == NULL)
2879 return;
2880
2881 status = purple_account_get_active_status(purple_connection_get_account(gc));
2882 id = purple_status_get_id(status);
2883
2884 /* Only go idle if active status is available */
2885 if (purple_strequal(id, NOVELL_STATUS_TYPE_AVAILABLE)) {
2886 if (time > 0) {
2887 rc = nm_send_set_status(user, NM_STATUS_AWAY_IDLE, NULL, NULL, NULL, NULL);
2888 } else {
2889 rc = nm_send_set_status(user, NM_STATUS_AVAILABLE, NULL, NULL, NULL, NULL);
2890 }
2891 }
2892
2893 _check_for_disconnect(user, rc);
2894 }
2895
2896 static void
novell_get_info(PurpleConnection * gc,const char * name)2897 novell_get_info(PurpleConnection * gc, const char *name)
2898 {
2899 NMUserRecord *user_record;
2900 NMUser *user;
2901 NMERR_T rc;
2902
2903 if (gc == NULL || name == NULL)
2904 return;
2905
2906 user = (NMUser *) gc->proto_data;
2907 if (user) {
2908
2909 user_record = nm_find_user_record(user, name);
2910 if (user_record) {
2911 _show_info(gc, user_record, g_strdup(name));
2912
2913 } else {
2914 rc = nm_send_get_details(user, name,
2915 _get_details_resp_show_info, g_strdup(name));
2916
2917 _check_for_disconnect(user, rc);
2918
2919 }
2920
2921 }
2922 }
2923
2924 static char *
novell_status_text(PurpleBuddy * buddy)2925 novell_status_text(PurpleBuddy * buddy)
2926 {
2927 const char *text = NULL;
2928 const char *dn = NULL;
2929 PurpleAccount *account;
2930
2931 account = buddy ? purple_buddy_get_account(buddy) : NULL;
2932 if (buddy && account) {
2933 PurpleConnection *gc = purple_account_get_connection(account);
2934
2935 if (gc && gc->proto_data) {
2936 NMUser *user = gc->proto_data;
2937
2938 dn = nm_lookup_dn(user, purple_buddy_get_name(buddy));
2939 if (dn) {
2940 NMUserRecord *user_record = nm_find_user_record(user, dn);
2941
2942 if (user_record) {
2943 text = nm_user_record_get_status_text(user_record);
2944 if (text)
2945 return g_strdup(text);
2946 }
2947 }
2948 }
2949 }
2950
2951 return NULL;
2952 }
2953
2954 static GList *
novell_status_types(PurpleAccount * account)2955 novell_status_types(PurpleAccount *account)
2956 {
2957 GList *status_types = NULL;
2958 PurpleStatusType *type;
2959
2960 g_return_val_if_fail(account != NULL, NULL);
2961
2962 type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, NOVELL_STATUS_TYPE_AVAILABLE,
2963 NULL, TRUE, TRUE, FALSE,
2964 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
2965 NULL);
2966 status_types = g_list_append(status_types, type);
2967
2968 type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, NOVELL_STATUS_TYPE_AWAY,
2969 NULL, TRUE, TRUE, FALSE,
2970 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
2971 NULL);
2972 status_types = g_list_append(status_types, type);
2973
2974 type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE, NOVELL_STATUS_TYPE_BUSY,
2975 _("Busy"), TRUE, TRUE, FALSE,
2976 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
2977 NULL);
2978 status_types = g_list_append(status_types, type);
2979
2980 type = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE, NOVELL_STATUS_TYPE_APPEAR_OFFLINE,
2981 NULL, TRUE, TRUE, FALSE);
2982 status_types = g_list_append(status_types, type);
2983
2984 type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE);
2985 status_types = g_list_append(status_types, type);
2986
2987 return status_types;
2988 }
2989
2990 static void
novell_set_status(PurpleAccount * account,PurpleStatus * status)2991 novell_set_status(PurpleAccount *account, PurpleStatus *status)
2992 {
2993 PurpleConnection *gc;
2994 gboolean connected;
2995 PurplePresence *presence;
2996 PurpleStatusType *type;
2997 PurpleStatusPrimitive primitive;
2998 NMUser *user;
2999 NMSTATUS_T novellstatus = NM_STATUS_AVAILABLE;
3000 NMERR_T rc = NM_OK;
3001 const char *msg = NULL;
3002 char *text = NULL;
3003
3004 connected = purple_account_is_connected(account);
3005 presence = purple_status_get_presence(status);
3006 type = purple_status_get_type(status);
3007 primitive = purple_status_type_get_primitive(type);
3008
3009 /*
3010 * We don't have any independent statuses, so we don't need to
3011 * do anything when a status is deactivated (because another
3012 * status is about to be activated).
3013 */
3014 if (!purple_status_is_active(status))
3015 return;
3016
3017 if (!connected)
3018 return;
3019
3020 gc = purple_account_get_connection(account);
3021 user = gc->proto_data;
3022 if (user == NULL)
3023 return;
3024
3025 if (primitive == PURPLE_STATUS_AVAILABLE) {
3026 novellstatus = NM_STATUS_AVAILABLE;
3027 } else if (primitive == PURPLE_STATUS_AWAY) {
3028 novellstatus = NM_STATUS_AWAY;
3029 } else if (primitive == PURPLE_STATUS_UNAVAILABLE) {
3030 novellstatus = NM_STATUS_BUSY;
3031 } else if (primitive == PURPLE_STATUS_INVISIBLE) {
3032 novellstatus = NM_STATUS_OFFLINE;
3033 } else if (purple_presence_is_idle(presence)) {
3034 novellstatus = NM_STATUS_AWAY_IDLE;
3035 } else {
3036 novellstatus = NM_STATUS_AVAILABLE;
3037 }
3038
3039 if (primitive == PURPLE_STATUS_AWAY || primitive == PURPLE_STATUS_AVAILABLE ||
3040 primitive == PURPLE_STATUS_UNAVAILABLE) {
3041 msg = purple_status_get_attr_string(status, "message");
3042 text = g_strdup(msg);
3043
3044 if (primitive == PURPLE_STATUS_AVAILABLE)
3045 msg = NULL; /* no auto replies for online status */
3046
3047 /* Don't want newlines in status text */
3048 purple_util_chrreplace(text, '\n', ' ');
3049 }
3050
3051 rc = nm_send_set_status(user, novellstatus, text, msg, NULL, NULL);
3052 _check_for_disconnect(user, rc);
3053
3054 if (text)
3055 g_free(text);
3056 }
3057
3058 static void
novell_add_permit(PurpleConnection * gc,const char * who)3059 novell_add_permit(PurpleConnection *gc, const char *who)
3060 {
3061 NMUser *user;
3062 NMERR_T rc = NM_OK;
3063 const char *name = who;
3064
3065 if (gc == NULL || who == NULL)
3066 return;
3067
3068 user = gc->proto_data;
3069 if (user == NULL)
3070 return;
3071
3072 /* Remove first -- we will add it back in when we get
3073 * the okay from the server
3074 */
3075 purple_privacy_permit_remove(gc->account, who, TRUE);
3076
3077 if (nm_user_is_privacy_locked(user)) {
3078 _show_privacy_locked_error(gc, user);
3079 _sync_privacy_lists(user);
3080 return;
3081 }
3082
3083 /* Work around for problem with un-typed, dotted contexts */
3084 if (strchr(who, '.')) {
3085 const char *dn = nm_lookup_dn(user, who);
3086 if (dn == NULL) {
3087 rc = nm_send_get_details(user, who, _get_details_send_privacy_create,
3088 (gpointer)TRUE);
3089 _check_for_disconnect(user, rc);
3090 return;
3091 } else {
3092 name = dn;
3093 }
3094 }
3095
3096 rc = nm_send_create_privacy_item(user, name, TRUE,
3097 _create_privacy_item_permit_resp_cb,
3098 g_strdup(who));
3099 _check_for_disconnect(user, rc);
3100 }
3101
3102 static void
novell_add_deny(PurpleConnection * gc,const char * who)3103 novell_add_deny(PurpleConnection *gc, const char *who)
3104 {
3105 NMUser *user;
3106 NMERR_T rc = NM_OK;
3107 const char *name = who;
3108
3109 if (gc == NULL || who == NULL)
3110 return;
3111
3112 user = gc->proto_data;
3113 if (user == NULL)
3114 return;
3115
3116 /* Remove first -- we will add it back in when we get
3117 * the okay from the server
3118 */
3119 purple_privacy_deny_remove(gc->account, who, TRUE);
3120
3121 if (nm_user_is_privacy_locked(user)) {
3122 _show_privacy_locked_error(gc, user);
3123 _sync_privacy_lists(user);
3124 return;
3125 }
3126
3127 /* Work around for problem with un-typed, dotted contexts */
3128 if (strchr(who, '.')) {
3129 const char *dn = nm_lookup_dn(user, who);
3130 if (dn == NULL) {
3131 rc = nm_send_get_details(user, who, _get_details_send_privacy_create,
3132 (gpointer)FALSE);
3133 _check_for_disconnect(user, rc);
3134 return;
3135 } else {
3136 name = dn;
3137 }
3138 }
3139
3140 rc = nm_send_create_privacy_item(user, name, FALSE,
3141 _create_privacy_item_deny_resp_cb,
3142 g_strdup(who));
3143 _check_for_disconnect(user, rc);
3144 }
3145
3146 static void
novell_rem_permit(PurpleConnection * gc,const char * who)3147 novell_rem_permit(PurpleConnection *gc, const char *who)
3148 {
3149 NMUser *user;
3150 NMERR_T rc = NM_OK;
3151 const char *dn = NULL;
3152
3153 if (gc == NULL || who == NULL)
3154 return;
3155
3156 user = gc->proto_data;
3157 if (user == NULL)
3158 return;
3159
3160 if (nm_user_is_privacy_locked(user)) {
3161 _show_privacy_locked_error(gc, user);
3162 _sync_privacy_lists(user);
3163 return;
3164 }
3165
3166 dn = nm_lookup_dn(user, who);
3167 if (dn == NULL)
3168 dn = who;
3169
3170 rc = nm_send_remove_privacy_item(user, dn, TRUE,
3171 _remove_privacy_item_resp_cb,
3172 g_strdup(who));
3173 _check_for_disconnect(user, rc);
3174 }
3175
3176 static void
novell_rem_deny(PurpleConnection * gc,const char * who)3177 novell_rem_deny(PurpleConnection *gc, const char *who)
3178 {
3179 NMUser *user;
3180 NMERR_T rc = NM_OK;
3181 const char *dn = NULL;
3182
3183 if (gc == NULL || who == NULL)
3184 return;
3185
3186 user = gc->proto_data;
3187 if (user == NULL)
3188 return;
3189
3190 if (nm_user_is_privacy_locked(user)) {
3191 _show_privacy_locked_error(gc, user);
3192 _sync_privacy_lists(user);
3193 return;
3194 }
3195
3196 dn = nm_lookup_dn(user, who);
3197 if (dn == NULL)
3198 dn = who;
3199
3200 rc = nm_send_remove_privacy_item(user, dn, FALSE,
3201 _remove_privacy_item_resp_cb,
3202 g_strdup(who));
3203 _check_for_disconnect(user, rc);
3204 }
3205
3206 static void
novell_set_permit_deny(PurpleConnection * gc)3207 novell_set_permit_deny(PurpleConnection *gc)
3208 {
3209 NMERR_T rc = NM_OK;
3210 const char *dn, *name = NULL;
3211 NMUserRecord *user_record = NULL;
3212 GSList *node = NULL, *copy = NULL;
3213 NMUser *user;
3214 int i, j, num_contacts, num_folders;
3215 NMContact *contact;
3216 NMFolder *folder = NULL;
3217
3218 if (gc == NULL)
3219 return;
3220
3221 user = gc->proto_data;
3222 if (user == NULL)
3223 return;
3224
3225 if (user->privacy_synched == FALSE) {
3226 _sync_privacy_lists(user);
3227 user->privacy_synched = TRUE;
3228 return;
3229 }
3230
3231 if (nm_user_is_privacy_locked(user)) {
3232 _show_privacy_locked_error(gc, user);
3233 _sync_privacy_lists(user);
3234 return;
3235 }
3236
3237 switch (gc->account->perm_deny) {
3238
3239 case PURPLE_PRIVACY_ALLOW_ALL:
3240 rc = nm_send_set_privacy_default(user, FALSE,
3241 _set_privacy_default_resp_cb, NULL);
3242 _check_for_disconnect(user, rc);
3243
3244 /* clear server side deny list */
3245 if (rc == NM_OK) {
3246 copy = g_slist_copy(user->deny_list);
3247 for (node = copy; node && node->data; node = node->next) {
3248 rc = nm_send_remove_privacy_item(user, (const char *)node->data,
3249 FALSE, NULL, NULL);
3250 if (_check_for_disconnect(user, rc))
3251 break;
3252 }
3253 g_slist_free(copy);
3254 g_slist_free(user->deny_list);
3255 user->deny_list = NULL;
3256 }
3257 break;
3258
3259 case PURPLE_PRIVACY_DENY_ALL:
3260 rc = nm_send_set_privacy_default(user, TRUE,
3261 _set_privacy_default_resp_cb, NULL);
3262 _check_for_disconnect(user, rc);
3263
3264 /* clear server side allow list */
3265 if (rc == NM_OK) {
3266 copy = g_slist_copy(user->allow_list);
3267 for (node = copy; node && node->data; node = node->next) {
3268 rc = nm_send_remove_privacy_item(user, (const char *)node->data,
3269 TRUE, NULL, NULL);
3270 if (_check_for_disconnect(user, rc))
3271 break;
3272 }
3273 g_slist_free(copy);
3274 g_slist_free(user->allow_list);
3275 user->allow_list = NULL;
3276 }
3277 break;
3278
3279 case PURPLE_PRIVACY_ALLOW_USERS:
3280
3281 rc = nm_send_set_privacy_default(user, TRUE,
3282 _set_privacy_default_resp_cb, NULL);
3283 _check_for_disconnect(user, rc);
3284
3285 /* sync allow lists */
3286 if (rc == NM_OK) {
3287
3288 for (node = user->allow_list; node; node = node->next) {
3289 user_record = nm_find_user_record(user, (char *)node->data);
3290 if (user_record) {
3291 name = nm_user_record_get_display_id(user_record);
3292
3293 if (!g_slist_find_custom(gc->account->permit,
3294 name, (GCompareFunc)purple_utf8_strcasecmp)) {
3295 purple_privacy_permit_add(gc->account, name , TRUE);
3296 }
3297 }
3298 }
3299
3300 for (node = gc->account->permit; node; node = node->next) {
3301 dn = nm_lookup_dn(user, (char *)node->data);
3302 if (dn) {
3303
3304 if (!g_slist_find_custom(user->allow_list,
3305 dn, (GCompareFunc)purple_utf8_strcasecmp)) {
3306 rc = nm_send_create_privacy_item(user, dn, TRUE,
3307 _create_privacy_item_deny_resp_cb,
3308 g_strdup(dn));
3309 if (_check_for_disconnect(user, rc))
3310 return;
3311 }
3312 } else {
3313 purple_privacy_permit_remove(gc->account, (char *)node->data, TRUE);
3314 }
3315 }
3316 }
3317 break;
3318
3319 case PURPLE_PRIVACY_DENY_USERS:
3320
3321 /* set to default allow */
3322 rc = nm_send_set_privacy_default(user, FALSE,
3323 _set_privacy_default_resp_cb, NULL);
3324 _check_for_disconnect(user, rc);
3325
3326 /* sync deny lists */
3327 if (rc == NM_OK) {
3328
3329 for (node = user->deny_list; node; node = node->next) {
3330 user_record = nm_find_user_record(user, (char *)node->data);
3331 if (user_record) {
3332 name = nm_user_record_get_display_id(user_record);
3333
3334 if (!g_slist_find_custom(gc->account->deny,
3335 name, (GCompareFunc)purple_utf8_strcasecmp)) {
3336 purple_privacy_deny_add(gc->account, name , TRUE);
3337 }
3338 }
3339 }
3340
3341 for (node = gc->account->deny; node; node = node->next) {
3342
3343 name = NULL;
3344 dn = nm_lookup_dn(user, (char *)node->data);
3345 if (dn) {
3346 user_record = nm_find_user_record(user, dn);
3347 name = nm_user_record_get_display_id(user_record);
3348
3349 if (!g_slist_find_custom(user->deny_list,
3350 dn, (GCompareFunc)purple_utf8_strcasecmp)) {
3351 rc = nm_send_create_privacy_item(user, dn, FALSE,
3352 _create_privacy_item_deny_resp_cb,
3353 g_strdup(name));
3354 if (_check_for_disconnect(user, rc))
3355 return;
3356 }
3357 } else {
3358 purple_privacy_deny_remove(gc->account, (char *)node->data, TRUE);
3359 }
3360 }
3361
3362 }
3363 break;
3364
3365 case PURPLE_PRIVACY_ALLOW_BUDDYLIST:
3366
3367 /* remove users from allow list that are not in buddy list */
3368 copy = g_slist_copy(user->allow_list);
3369 for (node = copy; node && node->data; node = node->next) {
3370 if (!nm_find_contacts(user, node->data)) {
3371 rc = nm_send_remove_privacy_item(user, (const char *)node->data,
3372 TRUE, NULL, NULL);
3373 if (_check_for_disconnect(user, rc))
3374 return;
3375 }
3376 }
3377 g_slist_free(copy);
3378
3379 /* add all buddies to allow list */
3380 num_contacts = nm_folder_get_contact_count(user->root_folder);
3381 for (i = 0; i < num_contacts; i++) {
3382 contact = nm_folder_get_contact(user->root_folder, i);
3383 dn = nm_contact_get_dn(contact);
3384 if (dn && !g_slist_find_custom(user->allow_list,
3385 dn, (GCompareFunc)purple_utf8_strcasecmp))
3386 {
3387 rc = nm_send_create_privacy_item(user, dn, TRUE,
3388 _create_privacy_item_deny_resp_cb,
3389 g_strdup(dn));
3390 if (_check_for_disconnect(user, rc))
3391 return;
3392 }
3393
3394 }
3395
3396 num_folders = nm_folder_get_subfolder_count(user->root_folder);
3397 for (i = 0; i < num_folders; i++) {
3398 folder = nm_folder_get_subfolder(user->root_folder, i);
3399 num_contacts = nm_folder_get_contact_count(folder);
3400 for (j = 0; j < num_contacts; j++) {
3401 contact = nm_folder_get_contact(folder, j);
3402 dn = nm_contact_get_dn(contact);
3403 if (dn && !g_slist_find_custom(user->allow_list,
3404 dn, (GCompareFunc)purple_utf8_strcasecmp))
3405 {
3406 rc = nm_send_create_privacy_item(user, dn, TRUE,
3407 _create_privacy_item_deny_resp_cb,
3408 g_strdup(dn));
3409 if (_check_for_disconnect(user, rc))
3410 return;
3411 }
3412 }
3413 }
3414
3415 /* set to default deny */
3416 rc = nm_send_set_privacy_default(user, TRUE,
3417 _set_privacy_default_resp_cb, NULL);
3418 if (_check_for_disconnect(user, rc))
3419 break;
3420
3421 break;
3422 }
3423 }
3424
3425 static GList *
novell_blist_node_menu(PurpleBlistNode * node)3426 novell_blist_node_menu(PurpleBlistNode *node)
3427 {
3428 GList *list = NULL;
3429 PurpleMenuAction *act;
3430
3431 if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
3432 act = purple_menu_action_new(_("Initiate _Chat"),
3433 PURPLE_CALLBACK(_initiate_conference_cb),
3434 NULL, NULL);
3435 list = g_list_append(list, act);
3436 }
3437
3438 return list;
3439 }
3440
3441 static void
novell_keepalive(PurpleConnection * gc)3442 novell_keepalive(PurpleConnection *gc)
3443 {
3444 NMUser *user;
3445 NMERR_T rc = NM_OK;
3446
3447 if (gc == NULL)
3448 return;
3449
3450 user = gc->proto_data;
3451 if (user == NULL)
3452 return;
3453
3454 rc = nm_send_keepalive(user, NULL, NULL);
3455 _check_for_disconnect(user, rc);
3456 }
3457
3458 static PurplePluginProtocolInfo prpl_info = {
3459 0,
3460 NULL, /* user_splits */
3461 NULL, /* protocol_options */
3462 NO_BUDDY_ICONS, /* icon_spec */
3463 novell_list_icon, /* list_icon */
3464 NULL, /* list_emblems */
3465 novell_status_text, /* status_text */
3466 novell_tooltip_text, /* tooltip_text */
3467 novell_status_types, /* status_types */
3468 novell_blist_node_menu, /* blist_node_menu */
3469 NULL, /* chat_info */
3470 NULL, /* chat_info_defaults */
3471 novell_login, /* login */
3472 novell_close, /* close */
3473 novell_send_im, /* send_im */
3474 NULL, /* set_info */
3475 novell_send_typing, /* send_typing */
3476 novell_get_info, /* get_info */
3477 novell_set_status, /* set_status */
3478 novell_set_idle, /* set_idle */
3479 NULL, /* change_passwd */
3480 novell_add_buddy, /* add_buddy */
3481 NULL, /* add_buddies */
3482 novell_remove_buddy, /* remove_buddy */
3483 NULL, /* remove_buddies */
3484 novell_add_permit, /* add_permit */
3485 novell_add_deny, /* add_deny */
3486 novell_rem_permit, /* rem_permit */
3487 novell_rem_deny, /* rem_deny */
3488 novell_set_permit_deny, /* set_permit_deny */
3489 NULL, /* join_chat */
3490 NULL, /* reject_chat */
3491 NULL, /* get_chat_name */
3492 novell_chat_invite, /* chat_invite */
3493 novell_chat_leave, /* chat_leave */
3494 NULL, /* chat_whisper */
3495 novell_chat_send, /* chat_send */
3496 novell_keepalive, /* keepalive */
3497 NULL, /* register_user */
3498 NULL, /* get_cb_info */
3499 NULL, /* get_cb_away */
3500 novell_alias_buddy, /* alias_buddy */
3501 novell_group_buddy, /* group_buddy */
3502 novell_rename_group, /* rename_group */
3503 NULL, /* buddy_free */
3504 novell_convo_closed, /* convo_closed */
3505 purple_normalize_nocase, /* normalize */
3506 NULL, /* set_buddy_icon */
3507 novell_remove_group, /* remove_group */
3508 NULL, /* get_cb_real_name */
3509 NULL, /* set_chat_topic */
3510 NULL, /* find_blist_chat */
3511 NULL, /* roomlist_get_list */
3512 NULL, /* roomlist_cancel */
3513 NULL, /* roomlist_expand_category */
3514 NULL, /* can_receive_file */
3515 NULL, /* send_file */
3516 NULL, /* new_xfer */
3517 NULL, /* offline_message */
3518 NULL, /* whiteboard_prpl_ops */
3519 NULL, /* send_raw */
3520 NULL, /* roomlist_room_serialize */
3521 NULL, /* unregister_user */
3522 NULL, /* send_attention */
3523 NULL, /* get_attention_types */
3524 sizeof(PurplePluginProtocolInfo), /* struct_size */
3525 NULL, /* get_account_text_table */
3526 NULL, /* initiate_media */
3527 NULL, /* get_media_caps */
3528 NULL, /* get_moods */
3529 NULL, /* set_public_alias */
3530 NULL, /* get_public_alias */
3531 NULL, /* add_buddy_with_invite */
3532 NULL, /* add_buddies_with_invite */
3533 NULL, /* get_cb_alias */
3534 NULL, /* chat_can_receive_file */
3535 NULL, /* chat_send_file */
3536 };
3537
3538 static PurplePluginInfo info = {
3539 PURPLE_PLUGIN_MAGIC,
3540 PURPLE_MAJOR_VERSION,
3541 PURPLE_MINOR_VERSION,
3542 PURPLE_PLUGIN_PROTOCOL, /**< type */
3543 NULL, /**< ui_requirement */
3544 0, /**< flags */
3545 NULL, /**< dependencies */
3546 PURPLE_PRIORITY_DEFAULT, /**< priority */
3547 "prpl-novell", /**< id */
3548 "GroupWise", /**< name */
3549 DISPLAY_VERSION, /**< version */
3550 /** summary */
3551 N_("Novell GroupWise Messenger Protocol Plugin"),
3552 /** description */
3553 N_("Novell GroupWise Messenger Protocol Plugin"),
3554 NULL, /**< author */
3555 PURPLE_WEBSITE, /**< homepage */
3556
3557 NULL, /**< load */
3558 NULL, /**< unload */
3559 NULL, /**< destroy */
3560
3561 NULL, /**< ui_info */
3562 &prpl_info, /**< extra_info */
3563 NULL,
3564 NULL,
3565
3566 /* padding */
3567 NULL,
3568 NULL,
3569 NULL,
3570 NULL
3571 };
3572
3573 static void
init_plugin(PurplePlugin * plugin)3574 init_plugin(PurplePlugin * plugin)
3575 {
3576 PurpleAccountOption *option;
3577
3578 option = purple_account_option_string_new(_("Server address"), "server", NULL);
3579 prpl_info.protocol_options =
3580 g_list_append(prpl_info.protocol_options, option);
3581
3582 option = purple_account_option_int_new(_("Server port"), "port", DEFAULT_PORT);
3583 prpl_info.protocol_options =
3584 g_list_append(prpl_info.protocol_options, option);
3585
3586 my_protocol = plugin;
3587 }
3588
3589 PURPLE_INIT_PLUGIN(novell, init_plugin, info);
3590