mark PurpleImageClass as private
[pidgin-git.git] / libpurple / protocols / novell / novell.c
blob45a827371a27a1bd959a445c460872091bdc8413
1 /*
2 * novell.c
4 * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
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.
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.
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
21 #include "internal.h"
23 #include "accountopt.h"
24 #include "action.h"
25 #include "debug.h"
26 #include "plugins.h"
27 #include "server.h"
28 #include "nmuser.h"
29 #include "notify.h"
30 #include "novell.h"
31 #include "util.h"
32 #include "sslconn.h"
33 #include "request.h"
34 #include "network.h"
35 #include "status.h"
36 #include "version.h"
38 #define DEFAULT_PORT 8300
39 #define NOVELL_CONNECT_STEPS 4
40 #define NM_ROOT_FOLDER_NAME "GroupWise Messenger"
42 #define NOVELL_STATUS_TYPE_AVAILABLE "available"
43 #define NOVELL_STATUS_TYPE_AWAY "away"
44 #define NOVELL_STATUS_TYPE_BUSY "busy"
45 #define NOVELL_STATUS_TYPE_OFFLINE "offline"
46 #define NOVELL_STATUS_TYPE_IDLE "idle"
47 #define NOVELL_STATUS_TYPE_APPEAR_OFFLINE "appearoffline"
49 static PurpleProtocol *my_protocol = NULL;
51 static gboolean
52 _is_disconnect_error(NMERR_T err);
54 static gboolean
55 _check_for_disconnect(NMUser * user, NMERR_T err);
57 static void
58 _send_message(NMUser * user, NMMessage * message);
60 static void
61 _update_buddy_status(NMUser *user, PurpleBuddy * buddy, int status, int gmt);
63 static void
64 _remove_purple_buddies(NMUser * user);
66 static void
67 _add_contacts_to_purple_blist(NMUser * user, NMFolder * folder);
69 static void
70 _add_purple_buddies(NMUser * user);
72 static void
73 _sync_contact_list(NMUser *user);
75 static void
76 _sync_privacy_lists(NMUser *user);
78 static void
79 _show_info(PurpleConnection * gc, NMUserRecord * user_record, char * name);
81 const char *
82 _get_conference_name(int id);
84 /*******************************************************************************
85 * Response callbacks
86 *******************************************************************************/
88 /* Handle login response */
89 static void
90 _login_resp_cb(NMUser * user, NMERR_T ret_code,
91 gpointer resp_data, gpointer user_data)
93 PurpleConnection *gc;
94 const char *alias;
95 NMERR_T rc;
97 if (user == NULL)
98 return;
100 gc = purple_account_get_connection(user->client_data);
101 if (gc == NULL)
102 return;
104 if (ret_code == NM_OK) {
106 /* Set alias for user if not set (use Full Name) */
107 alias = purple_account_get_private_alias(user->client_data);
108 if (alias == NULL || *alias == '\0') {
109 alias = nm_user_record_get_full_name(user->user_record);
111 if (alias)
112 purple_account_set_private_alias(user->client_data, alias);
115 /* Tell Purple that we are connected */
116 purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
118 _sync_contact_list(user);
120 rc = nm_send_set_status(user, NM_STATUS_AVAILABLE, NULL, NULL, NULL,
121 NULL);
122 _check_for_disconnect(user, rc);
124 } else {
125 PurpleConnectionError reason;
126 char *err = g_strdup_printf(_("Unable to login: %s"),
127 nm_error_to_string (ret_code));
129 switch (ret_code) {
130 case NMERR_AUTHENTICATION_FAILED:
131 case NMERR_CREDENTIALS_MISSING:
132 case NMERR_PASSWORD_INVALID:
133 /* Don't attempt to auto-reconnect if our
134 * password was invalid.
136 if (!purple_account_get_remember_password(purple_connection_get_account(gc)))
137 purple_account_set_password(purple_connection_get_account(gc), NULL, NULL, NULL);
138 reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
139 break;
140 default:
141 /* FIXME: There are other reasons login could fail */
142 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
145 purple_connection_error(gc, reason, err);
146 g_free(err);
150 /* Handle getstatus response*/
151 static void
152 _get_status_resp_cb(NMUser * user, NMERR_T ret_code,
153 gpointer resp_data, gpointer user_data)
155 PurpleBuddy *buddy;
156 GSList *buddies;
157 GSList *bnode;
158 NMUserRecord *user_record = (NMUserRecord *) resp_data;
159 int status;
161 if (user == NULL || user_record == NULL)
162 return;
164 if (ret_code == NM_OK) {
166 /* Find all Purple buddies and update their statuses */
167 const char *name = nm_user_record_get_display_id(user_record);
169 if (name) {
170 buddies = purple_blist_find_buddies((PurpleAccount *) user->client_data, name);
171 for (bnode = buddies; bnode; bnode = bnode->next) {
172 buddy = (PurpleBuddy *) bnode->data;
173 if (buddy) {
174 status = nm_user_record_get_status(user_record);
175 _update_buddy_status(user, buddy, status, time(0));
178 g_slist_free(buddies);
181 } else {
183 purple_debug(PURPLE_DEBUG_INFO, "novell",
184 "_get_status_resp_cb(): rc = 0x%X\n", ret_code);
189 /* Show an error if the rename failed */
190 static void
191 _rename_contact_resp_cb(NMUser * user, NMERR_T ret_code,
192 gpointer resp_data, gpointer user_data)
194 if (ret_code != NM_OK) {
195 purple_debug(PURPLE_DEBUG_INFO, "novell",
196 "_rename_contact_resp_cb(): rc = 0x%X\n", ret_code);
200 /* Handle the getdetails response and send the message */
201 static void
202 _get_details_resp_send_msg(NMUser * user, NMERR_T ret_code,
203 gpointer resp_data, gpointer user_data)
205 PurpleConversation *gconv;
206 PurpleConnection *gc;
207 NMUserRecord *user_record = NULL;
208 NMContact *cntct = NULL;
209 NMConference *conf;
210 NMMessage *msg = user_data;
211 const char *dn = NULL;
212 const char *name;
214 if (user == NULL || msg == NULL)
215 return;
217 if (ret_code == NM_OK) {
218 user_record = (NMUserRecord *) resp_data;
219 if (user_record) {
221 /* Set the title for the conversation */
222 /* XXX - Should this be find_im_with_account? */
223 gconv = purple_conversations_find_with_account(nm_user_record_get_display_id(user_record),
224 (PurpleAccount *) user->client_data);
225 if (gconv) {
227 dn = nm_user_record_get_dn(user_record);
228 if (dn) {
229 cntct = nm_find_contact(user, dn);
232 if (cntct) {
233 purple_conversation_set_title(gconv,
234 nm_contact_get_display_name(cntct));
235 } else {
237 /* Not in the contact list, try to user full name */
238 name = (char *) nm_user_record_get_full_name(user_record);
239 if (name)
240 purple_conversation_set_title(gconv, name);
244 /* Add the user record to particpant list */
245 conf = nm_message_get_conference(msg);
246 if (conf) {
247 nm_conference_add_participant(conf, user_record);
248 _send_message(user, msg);
252 } else {
254 gc = purple_account_get_connection(user->client_data);
255 if (gc != NULL) {
256 char *err = g_strdup_printf(_("Unable to send message."
257 " Could not get details for user (%s)."),
258 nm_error_to_string (ret_code));
260 purple_notify_error(gc, NULL, err, NULL,
261 purple_request_cpar_from_connection(gc));
262 g_free(err);
265 if (msg)
266 nm_release_message(msg);
270 /* Set up the new PurpleBuddy based on the response from getdetails */
271 static void
272 _get_details_resp_setup_buddy(NMUser * user, NMERR_T ret_code,
273 gpointer resp_data, gpointer user_data)
275 NMUserRecord *user_record;
276 NMContact *contact;
277 PurpleBuddy *buddy;
278 const char *alias;
279 NMERR_T rc = NM_OK;
281 if (user == NULL || resp_data == NULL || user_data == NULL)
282 return;
284 contact = user_data;
286 if (ret_code == NM_OK) {
287 user_record = resp_data;
289 buddy = nm_contact_get_data(contact);
291 nm_contact_set_user_record(contact, user_record);
293 /* Set the display id */
294 purple_buddy_set_name(buddy,
295 nm_user_record_get_display_id(user_record));
297 alias = purple_buddy_get_alias(buddy);
298 if (alias == NULL || *alias == '\0' || purple_strequal(alias, purple_buddy_get_name(buddy))) {
299 purple_buddy_set_local_alias(buddy,
300 nm_user_record_get_full_name(user_record));
302 /* Tell the server about the new display name */
303 rc = nm_send_rename_contact(user, contact,
304 nm_user_record_get_full_name(user_record),
305 NULL, NULL);
306 _check_for_disconnect(user, rc);
311 /* Get initial status for the buddy */
312 rc = nm_send_get_status(user, resp_data, _get_status_resp_cb, NULL);
313 _check_for_disconnect(user, rc);
315 /* nm_release_contact(contact);*/
319 if (contact)
320 nm_release_contact(contact);
323 /* Add the new contact into the PurpleBuddy list */
324 static void
325 _create_contact_resp_cb(NMUser * user, NMERR_T ret_code,
326 gpointer resp_data, gpointer user_data)
328 NMContact *tmp_contact = (NMContact *) user_data;
329 NMContact *new_contact = NULL;
330 NMFolder *folder = NULL;
331 PurpleGroup *group;
332 PurpleBuddy *buddy;
333 const char *folder_name = NULL;
334 NMERR_T rc = NM_OK;
336 if (user == NULL)
337 return;
339 if (ret_code == NM_OK) {
341 new_contact = (NMContact *) resp_data;
342 if (new_contact == NULL || tmp_contact == NULL)
343 return;
345 /* Get the userid and folder name for the new contact */
346 folder = nm_find_folder_by_id(user,
347 nm_contact_get_parent_id(new_contact));
348 if (folder) {
349 folder_name = nm_folder_get_name(folder);
352 if (folder_name == NULL || *folder_name == '\0')
353 folder_name = NM_ROOT_FOLDER_NAME;
355 /* Re-add the buddy now that we got the okay from the server */
356 if (folder_name && (group = purple_blist_find_group(folder_name))) {
358 const char *alias = nm_contact_get_display_name(tmp_contact);
359 const char *display_id = nm_contact_get_display_id(new_contact);
361 if (display_id == NULL)
362 display_id = nm_contact_get_dn(new_contact);
364 if (alias && !purple_strequal(alias, display_id)) {
366 /* The user requested an alias, tell the server about it. */
367 rc = nm_send_rename_contact(user, new_contact, alias,
368 _rename_contact_resp_cb, NULL);
369 _check_for_disconnect(user, rc);
371 } else {
373 alias = "";
377 /* Add it to the purple buddy list if it is not there */
378 buddy = purple_blist_find_buddy_in_group(user->client_data, display_id, group);
379 if (buddy == NULL) {
380 buddy = purple_buddy_new(user->client_data, display_id, alias);
381 purple_blist_add_buddy(buddy, NULL, group, NULL);
384 /* Save the new buddy as part of the contact object */
385 nm_contact_set_data(new_contact, (gpointer) buddy);
387 /* We need details for the user before we can setup the
388 * new Purple buddy. We always call this because the
389 * 'createcontact' response fields do not always contain
390 * everything that we need.
392 nm_contact_add_ref(new_contact);
394 rc = nm_send_get_details(user, nm_contact_get_dn(new_contact),
395 _get_details_resp_setup_buddy, new_contact);
396 _check_for_disconnect(user, rc);
400 } else {
401 PurpleConnection *gc = purple_account_get_connection(user->client_data);
402 const char *name = nm_contact_get_dn(tmp_contact);
403 char *err;
405 err =
406 g_strdup_printf(_("Unable to add %s to your buddy list (%s)."),
407 name, nm_error_to_string (ret_code));
408 purple_notify_error(gc, NULL, err, NULL,
409 purple_request_cpar_from_connection(gc));
410 g_free(err);
414 if (tmp_contact)
415 nm_release_contact(tmp_contact);
418 /* Show an error if we failed to send the message */
419 static void
420 _send_message_resp_cb(NMUser * user, NMERR_T ret_code,
421 gpointer resp_data, gpointer user_data)
423 PurpleConnection *gc;
424 char *err = NULL;
426 if (user == NULL)
427 return;
429 if (ret_code != NM_OK) {
430 gc = purple_account_get_connection(user->client_data);
432 /* TODO: Improve this! message to who or for what conference? */
433 err = g_strdup_printf(_("Unable to send message (%s)."),
434 nm_error_to_string (ret_code));
435 purple_notify_error(gc, NULL, err, NULL,
436 purple_request_cpar_from_connection(gc));
437 g_free(err);
441 /* Show an error if the remove failed */
442 static void
443 _remove_contact_resp_cb(NMUser * user, NMERR_T ret_code,
444 gpointer resp_data, gpointer user_data)
446 if (ret_code != NM_OK) {
447 /* TODO: Display an error? */
449 purple_debug(PURPLE_DEBUG_INFO, "novell",
450 "_remove_contact_resp_cb(): rc = 0x%x\n", ret_code);
454 /* Show an error if the remove failed */
455 static void
456 _remove_folder_resp_cb(NMUser * user, NMERR_T ret_code,
457 gpointer resp_data, gpointer user_data)
459 if (ret_code != NM_OK) {
460 /* TODO: Display an error? */
462 purple_debug(PURPLE_DEBUG_INFO, "novell",
463 "_remove_folder_resp_cb(): rc = 0x%x\n", ret_code);
467 /* Show an error if the move failed */
468 static void
469 _move_contact_resp_cb(NMUser * user, NMERR_T ret_code,
470 gpointer resp_data, gpointer user_data)
472 if (ret_code != NM_OK) {
473 /* TODO: Display an error? */
475 purple_debug(PURPLE_DEBUG_INFO, "novell",
476 "_move_contact_resp_cb(): rc = 0x%x\n", ret_code);
480 /* Show an error if the rename failed */
481 static void
482 _rename_folder_resp_cb(NMUser * user, NMERR_T ret_code,
483 gpointer resp_data, gpointer user_data)
485 if (ret_code != NM_OK) {
486 /* TODO: Display an error? */
488 purple_debug(PURPLE_DEBUG_INFO, "novell",
489 "_rename_folder_resp_cb(): rc = 0x%x\n", ret_code);
493 static void
494 _sendinvite_resp_cb(NMUser *user, NMERR_T ret_code,
495 gpointer resp_data, gpointer user_data)
497 char *err;
498 PurpleConnection *gc;
500 if (user == NULL)
501 return;
503 if (ret_code != NM_OK) {
504 gc = purple_account_get_connection(user->client_data);
505 err = g_strdup_printf(_("Unable to invite user (%s)."), nm_error_to_string(ret_code));
506 purple_notify_error(gc, NULL, err, NULL,
507 purple_request_cpar_from_connection(gc));
508 g_free(err);
510 purple_debug(PURPLE_DEBUG_INFO, "novell",
511 "_sendinvite_resp_cb(): rc = 0x%x\n", ret_code);
516 /* If the createconf was successful attempt to send the message,
517 * otherwise display an error message to the user.
519 static void
520 _createconf_resp_send_msg(NMUser * user, NMERR_T ret_code,
521 gpointer resp_data, gpointer user_data)
523 NMConference *conf;
524 NMMessage *msg = user_data;
526 if (user == NULL || msg == NULL)
527 return;
529 if (ret_code == NM_OK) {
530 _send_message(user, msg);
531 } else {
533 if ((conf = nm_message_get_conference(msg))) {
535 PurpleConnection *gc = purple_account_get_connection(user->client_data);
536 const char *name = NULL;
537 char *err;
538 NMUserRecord *ur;
540 ur = nm_conference_get_participant(conf, 0);
541 if (ur)
542 name = nm_user_record_get_userid(ur);
544 if (name)
545 err = g_strdup_printf(_("Unable to send message to %s."
546 " Could not create the conference (%s)."),
547 name,
548 nm_error_to_string (ret_code));
549 else
550 err = g_strdup_printf(_("Unable to send message."
551 " Could not create the conference (%s)."),
552 nm_error_to_string (ret_code));
554 purple_notify_error(gc, NULL, err, NULL,
555 purple_request_cpar_from_connection(gc));
556 g_free(err);
559 if (msg)
560 nm_release_message(msg);
564 /* Move contact to newly created folder */
565 static void
566 _create_folder_resp_move_contact(NMUser * user, NMERR_T ret_code,
567 gpointer resp_data, gpointer user_data)
569 NMContact *contact = user_data;
570 NMFolder *new_folder;
571 char *folder_name = resp_data;
572 NMERR_T rc = NM_OK;
574 if (user == NULL || folder_name == NULL || contact == NULL) {
576 g_free(folder_name);
578 return;
581 if (ret_code == NM_OK || ret_code == NMERR_DUPLICATE_FOLDER) {
582 new_folder = nm_find_folder(user, folder_name);
583 if (new_folder) {
585 /* Tell the server to move the contact to the new folder */
586 /* rc = nm_send_move_contact(user, contact, new_folder,
587 _move_contact_resp_cb, NULL); */
589 rc = nm_send_create_contact(user, new_folder, contact,
590 NULL, NULL);
592 _check_for_disconnect(user, rc);
595 } else {
596 PurpleConnection *gc = purple_account_get_connection(user->client_data);
597 char *err = g_strdup_printf(_("Unable to move user %s"
598 " to folder %s in the server side list."
599 " Error while creating folder (%s)."),
600 nm_contact_get_dn(contact),
601 folder_name,
602 nm_error_to_string (ret_code));
604 purple_notify_error(gc, NULL, err, NULL,
605 purple_request_cpar_from_connection(gc));
606 g_free(err);
609 g_free(folder_name);
612 /* Add contact to newly create folder */
613 static void
614 _create_folder_resp_add_contact(NMUser * user, NMERR_T ret_code,
615 gpointer resp_data, gpointer user_data)
617 NMContact *contact = (NMContact *) user_data;
618 NMFolder *folder;
619 char *folder_name = (char *) resp_data;
620 NMERR_T rc = NM_OK;
622 if (user == NULL || folder_name == NULL || contact == NULL) {
624 if (contact)
625 nm_release_contact(contact);
627 g_free(folder_name);
629 return;
632 if (ret_code == NM_OK || ret_code == NMERR_DUPLICATE_FOLDER) {
633 folder = nm_find_folder(user, folder_name);
634 if (folder) {
636 rc = nm_send_create_contact(user, folder, contact,
637 _create_contact_resp_cb, contact);
638 _check_for_disconnect(user, rc);
640 } else {
641 PurpleConnection *gc = purple_account_get_connection(user->client_data);
642 const char *name = nm_contact_get_dn(contact);
643 char *err =
644 g_strdup_printf(_("Unable to add %s to your buddy list."
645 " Error creating folder in server side list (%s)."),
646 name, nm_error_to_string (ret_code));
648 purple_notify_error(gc, NULL, err, NULL,
649 purple_request_cpar_from_connection(gc));
651 nm_release_contact(contact);
652 g_free(err);
655 g_free(folder_name);
658 static void
659 _join_conf_resp_cb(NMUser * user, NMERR_T ret_code,
660 gpointer resp_data, gpointer user_data)
662 PurpleChatConversation *chat;
663 PurpleConnection *gc;
664 NMUserRecord *ur;
665 NMConference *conference = user_data;
666 const char *name, *conf_name;
667 int i, count;
669 if (user == NULL || conference == NULL)
670 return;
672 gc = purple_account_get_connection(user->client_data);
674 if (ret_code == NM_OK) {
675 conf_name = _get_conference_name(++user->conference_count);
676 chat = purple_serv_got_joined_chat(gc, user->conference_count, conf_name);
677 if (chat) {
679 nm_conference_set_data(conference, (gpointer) chat);
681 count = nm_conference_get_participant_count(conference);
682 for (i = 0; i < count; i++) {
683 ur = nm_conference_get_participant(conference, i);
684 if (ur) {
685 name = nm_user_record_get_display_id(ur);
686 purple_chat_conversation_add_user(chat, name, NULL,
687 PURPLE_CHAT_USER_NONE, TRUE);
694 /* Show info returned by getdetails */
695 static void
696 _get_details_resp_show_info(NMUser * user, NMERR_T ret_code,
697 gpointer resp_data, gpointer user_data)
699 PurpleConnection *gc;
700 NMUserRecord *user_record;
701 char *name;
702 char *err;
704 if (user == NULL)
705 return;
707 name = user_data;
709 if (ret_code == NM_OK) {
710 user_record = (NMUserRecord *) resp_data;
711 if (user_record) {
712 _show_info(purple_account_get_connection(user->client_data),
713 user_record, g_strdup(name));
715 } else {
716 gc = purple_account_get_connection(user->client_data);
717 err =
718 g_strdup_printf(_("Could not get details for user %s (%s)."),
719 name, nm_error_to_string (ret_code));
720 purple_notify_error(gc, NULL, err, NULL,
721 purple_request_cpar_from_connection(gc));
722 g_free(err);
725 g_free(name);
728 /* Handle get details response add to privacy list */
729 static void
730 _get_details_resp_add_privacy_item(NMUser *user, NMERR_T ret_code,
731 gpointer resp_data, gpointer user_data)
733 PurpleConnection *gc;
734 PurpleAccount *account;
735 NMUserRecord *user_record = resp_data;
736 char *err;
737 gboolean allowed = GPOINTER_TO_INT(user_data);
738 const char *display_id;
740 if (user == NULL)
741 return;
743 gc = purple_account_get_connection(user->client_data);
744 display_id = nm_user_record_get_display_id(user_record);
745 account = purple_connection_get_account(gc);
747 if (ret_code == NM_OK) {
749 if (allowed) {
751 if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
752 display_id, (GCompareFunc)purple_utf8_strcasecmp)) {
753 purple_account_privacy_permit_add(account, display_id, TRUE);
756 } else {
758 if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
759 display_id, (GCompareFunc)purple_utf8_strcasecmp)) {
760 purple_account_privacy_deny_add(account, display_id, TRUE);
764 } else {
766 err = g_strdup_printf(_("Unable to add user to privacy list (%s)."),
767 nm_error_to_string(ret_code));
768 purple_notify_error(gc, NULL, err, NULL,
769 purple_request_cpar_from_connection(gc));
770 g_free(err);
775 /* Handle response to create privacy item request */
776 static void
777 _create_privacy_item_deny_resp_cb(NMUser *user, NMERR_T ret_code,
778 gpointer resp_data, gpointer user_data)
780 PurpleConnection *gc;
781 PurpleAccount *account;
782 NMUserRecord *user_record;
783 char *who = user_data;
784 char *err;
785 NMERR_T rc = NM_OK;
786 const char *display_id = NULL;
788 if (user == NULL)
789 return;
791 gc = purple_account_get_connection(user->client_data);
792 account = purple_connection_get_account(gc);
794 if (ret_code == NM_OK) {
796 user_record = nm_find_user_record(user, who);
797 if (user_record)
798 display_id = nm_user_record_get_display_id(user_record);
800 if (display_id) {
802 if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
803 display_id, (GCompareFunc)purple_utf8_strcasecmp)) {
805 purple_account_privacy_deny_add(account, display_id, TRUE);
808 } else {
809 rc = nm_send_get_details(user, who,
810 _get_details_resp_add_privacy_item,
811 (gpointer)FALSE);
812 _check_for_disconnect(user, rc);
814 } else {
816 err = g_strdup_printf(_("Unable to add %s to deny list (%s)."),
817 who, nm_error_to_string(ret_code));
818 purple_notify_error(gc, NULL, err, NULL,
819 purple_request_cpar_from_connection(gc));
820 g_free(err);
824 g_free(who);
828 /* Handle response to create privacy item request */
829 static void
830 _create_privacy_item_permit_resp_cb(NMUser *user, NMERR_T ret_code,
831 gpointer resp_data, gpointer user_data)
833 PurpleConnection *gc;
834 PurpleAccount *account;
835 NMUserRecord *user_record;
836 char *who = user_data;
837 char *err;
838 NMERR_T rc = NM_OK;
839 const char *display_id = NULL;
841 if (user == NULL)
842 return;
844 gc = purple_account_get_connection(user->client_data);
845 account = purple_connection_get_account(gc);
847 if (ret_code == NM_OK) {
849 user_record = nm_find_user_record(user, who);
850 if (user_record)
851 display_id = nm_user_record_get_display_id(user_record);
853 if (display_id) {
855 if (!g_slist_find_custom(purple_account_privacy_get_permitted(account),
856 display_id,
857 (GCompareFunc)purple_utf8_strcasecmp)) {
859 purple_account_privacy_permit_add(account, display_id, TRUE);
862 } else {
863 rc = nm_send_get_details(user, who,
864 _get_details_resp_add_privacy_item,
865 (gpointer)TRUE);
866 _check_for_disconnect(user, rc);
869 } else {
871 err = g_strdup_printf(_("Unable to add %s to permit list (%s)."), who,
872 nm_error_to_string(ret_code));
873 purple_notify_error(gc, NULL, err, NULL,
874 purple_request_cpar_from_connection(gc));
875 g_free(err);
879 g_free(who);
882 static void
883 _get_details_send_privacy_create(NMUser *user, NMERR_T ret_code,
884 gpointer resp_data, gpointer user_data)
886 NMERR_T rc = NM_OK;
887 PurpleConnection *gc;
888 NMUserRecord *user_record = resp_data;
889 char *err;
890 gboolean allowed = GPOINTER_TO_INT(user_data);
891 const char *dn, *display_id;
893 if (user == NULL)
894 return;
896 gc = purple_account_get_connection(user->client_data);
897 dn = nm_user_record_get_dn(user_record);
898 display_id = nm_user_record_get_display_id(user_record);
900 if (ret_code == NM_OK) {
902 if (allowed) {
903 rc = nm_send_create_privacy_item(user, dn, TRUE,
904 _create_privacy_item_permit_resp_cb,
905 g_strdup(display_id));
906 _check_for_disconnect(user, rc);
908 } else {
909 rc = nm_send_create_privacy_item(user, dn, FALSE,
910 _create_privacy_item_deny_resp_cb,
911 g_strdup(display_id));
912 _check_for_disconnect(user, rc);
915 } else {
917 err = g_strdup_printf(_("Unable to add user to privacy list (%s)."),
918 nm_error_to_string(ret_code));
919 purple_notify_error(gc, NULL, err, NULL,
920 purple_request_cpar_from_connection(gc));
921 g_free(err);
926 static void
927 _remove_privacy_item_resp_cb(NMUser *user, NMERR_T ret_code,
928 gpointer resp_data, gpointer user_data)
930 PurpleConnection *gc;
931 char *who = user_data;
932 char *err;
934 if (user == NULL)
935 return;
937 if (ret_code != NM_OK) {
939 gc = purple_account_get_connection(user->client_data);
940 err = g_strdup_printf(_("Unable to remove %s from privacy list (%s)."), who,
941 nm_error_to_string(ret_code));
942 purple_notify_error(gc, NULL, err, NULL,
943 purple_request_cpar_from_connection(gc));
944 g_free(err);
947 g_free(who);
950 static void
951 _set_privacy_default_resp_cb(NMUser *user, NMERR_T ret_code,
952 gpointer resp_data, gpointer user_data)
954 PurpleConnection *gc;
955 char *err;
957 if (user == NULL)
958 return;
960 if (ret_code != NM_OK) {
962 gc = purple_account_get_connection(user->client_data);
963 err = g_strdup_printf(_("Unable to change server side privacy settings (%s)."),
964 nm_error_to_string(ret_code));
965 purple_notify_error(gc, NULL, err, NULL,
966 purple_request_cpar_from_connection(gc));
967 g_free(err);
972 /* Handle get details response add to privacy list */
973 static void
974 _get_details_resp_send_invite(NMUser *user, NMERR_T ret_code,
975 gpointer resp_data, gpointer user_data)
977 NMERR_T rc = NM_OK;
978 PurpleConnection *gc;
979 NMUserRecord *user_record = resp_data;
980 char *err;
981 GSList *cnode;
982 NMConference *conference;
983 gpointer chat;
984 long id = (long) user_data;
986 if (user == NULL)
987 return;
989 gc = purple_account_get_connection(user->client_data);
991 if (ret_code == NM_OK) {
993 for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
994 conference = cnode->data;
995 if (conference && (chat = nm_conference_get_data(conference))) {
996 if (purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(chat)) == id) {
997 rc = nm_send_conference_invite(user, conference, user_record,
998 NULL, _sendinvite_resp_cb, NULL);
999 _check_for_disconnect(user, rc);
1000 break;
1005 } else {
1007 err = g_strdup_printf(_("Unable to invite user (%s)."), nm_error_to_string(ret_code));
1008 purple_notify_error(gc, NULL, err, NULL,
1009 purple_request_cpar_from_connection(gc));
1010 g_free(err);
1015 static void
1016 _createconf_resp_send_invite(NMUser * user, NMERR_T ret_code,
1017 gpointer resp_data, gpointer user_data)
1019 NMERR_T rc = NM_OK;
1020 NMConference *conference = resp_data;
1021 NMUserRecord *user_record = user_data;
1022 PurpleConnection *gc;
1023 char *err;
1025 if (user == NULL)
1026 return;
1030 if (ret_code == NM_OK) {
1031 rc = nm_send_conference_invite(user, conference, user_record,
1032 NULL, _sendinvite_resp_cb, NULL);
1033 _check_for_disconnect(user, rc);
1034 } else {
1035 err = g_strdup_printf(_("Unable to create conference (%s)."), nm_error_to_string(ret_code));
1036 gc = purple_account_get_connection(user->client_data);
1037 purple_notify_error(gc, NULL, err, NULL,
1038 purple_request_cpar_from_connection(gc));
1039 g_free(err);
1043 /*******************************************************************************
1044 * Helper functions
1045 ******************************************************************************/
1047 static char *
1048 _user_agent_string(void)
1051 #if !defined(_WIN32)
1053 const char *sysname = "";
1054 const char *release = "";
1055 struct utsname u;
1057 if (uname(&u) == 0) {
1058 sysname = u.sysname;
1059 release = u.release;
1060 } else {
1061 sysname = "Linux";
1062 release = "Unknown";
1065 return g_strdup_printf("Purple/%s (%s; %s)", VERSION, sysname, release);
1067 #else
1069 const char *sysname = "";
1070 OSVERSIONINFO os_info;
1071 SYSTEM_INFO sys_info;
1073 os_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1074 GetVersionEx(&os_info);
1075 GetSystemInfo(&sys_info);
1077 if (os_info.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1078 switch (os_info.dwMajorVersion) {
1079 case 3:
1080 case 4:
1081 sysname = "Windows NT";
1082 break;
1083 case 5:
1084 switch (os_info.dwMinorVersion) {
1085 case 0:
1086 sysname = "Windows 2000";
1087 break;
1088 case 1:
1089 sysname = "Windows XP";
1090 break;
1091 case 2:
1092 sysname = "Windows Server 2003";
1093 break;
1094 default:
1095 sysname = "Windows";
1096 break;
1098 break;
1099 default:
1100 sysname = "Windows";
1101 break;
1104 } else if (os_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1105 switch (os_info.dwMinorVersion) {
1106 case 0:
1107 sysname = "Windows 95";
1108 break;
1109 case 10:
1110 sysname = "Windows 98";
1111 break;
1112 case 90:
1113 sysname = "Windows ME";
1114 break;
1115 default:
1116 sysname = "Windows";
1117 break;
1119 } else {
1120 sysname = "Windows";
1123 return g_strdup_printf("Purple/%s (%s; %ld.%ld)", VERSION, sysname,
1124 os_info.dwMajorVersion, os_info.dwMinorVersion);
1126 #endif
1131 static gboolean
1132 _is_disconnect_error(NMERR_T err)
1134 return (err == NMERR_TCP_WRITE ||
1135 err == NMERR_TCP_READ || err == NMERR_PROTOCOL);
1138 static gboolean
1139 _check_for_disconnect(NMUser * user, NMERR_T err)
1141 PurpleConnection *gc = purple_account_get_connection(user->client_data);
1143 if (_is_disconnect_error(err)) {
1145 purple_connection_error(gc,
1146 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
1147 _("Error communicating with server. Closing connection."));
1148 return TRUE;
1152 return FALSE;
1155 /* Check to see if the conference is instantiated, if so send the message.
1156 * If not send the create conference -- the response handler for the createconf
1157 * will call this function again.
1159 static void
1160 _send_message(NMUser * user, NMMessage * message)
1162 NMConference *conf;
1163 NMERR_T rc = NM_OK;
1165 conf = nm_message_get_conference(message);
1166 if (conf) {
1167 /* We have a conference make sure that the
1168 server knows about it already. */
1169 if (nm_conference_is_instantiated(conf)) {
1171 /* We have everything that we need...finally! */
1172 rc = nm_send_message(user, message, _send_message_resp_cb);
1173 _check_for_disconnect(user, rc);
1175 nm_release_message(message);
1177 } else {
1178 rc = nm_send_create_conference(user, conf, _createconf_resp_send_msg, message);
1179 _check_for_disconnect(user, rc);
1185 * Update the status of the given buddy in the Purple buddy list
1187 static void
1188 _update_buddy_status(NMUser *user, PurpleBuddy * buddy, int novellstatus, int gmt)
1190 PurpleAccount *account;
1191 const char *status_id;
1192 const char *text = NULL;
1193 const char *dn;
1194 const char *name;
1195 int idle = 0;
1197 account = purple_buddy_get_account(buddy);
1198 name = purple_buddy_get_name(buddy);
1200 switch (novellstatus) {
1201 case NM_STATUS_AVAILABLE:
1202 status_id = NOVELL_STATUS_TYPE_AVAILABLE;
1203 break;
1204 case NM_STATUS_AWAY:
1205 status_id = NOVELL_STATUS_TYPE_AWAY;
1206 break;
1207 case NM_STATUS_BUSY:
1208 status_id = NOVELL_STATUS_TYPE_BUSY;
1209 break;
1210 case NM_STATUS_OFFLINE:
1211 status_id = NOVELL_STATUS_TYPE_OFFLINE;
1212 break;
1213 case NM_STATUS_AWAY_IDLE:
1214 status_id = NOVELL_STATUS_TYPE_AWAY;
1215 idle = gmt;
1216 break;
1217 default:
1218 status_id = NOVELL_STATUS_TYPE_OFFLINE;
1219 break;
1222 /* Get status text for the user */
1223 dn = nm_lookup_dn(user, name);
1224 if (dn) {
1225 NMUserRecord *user_record = nm_find_user_record(user, dn);
1226 if (user_record) {
1227 text = nm_user_record_get_status_text(user_record);
1231 purple_protocol_got_user_status(account, name, status_id,
1232 "message", text, NULL);
1233 purple_protocol_got_user_idle(account, name,
1234 (novellstatus == NM_STATUS_AWAY_IDLE), idle);
1237 /* Iterate through the cached Purple buddy list and remove buddies
1238 * that are not in the server side list.
1240 static void
1241 _remove_purple_buddies(NMUser *user)
1243 PurpleBlistNode *gnode;
1244 PurpleBlistNode *cnode;
1245 PurpleBlistNode *bnode;
1246 PurpleGroup *group;
1247 PurpleBuddy *buddy;
1248 GSList *rem_list = NULL;
1249 GSList *l;
1250 NMFolder *folder = NULL;
1251 const char *gname = NULL;
1253 for (gnode = purple_blist_get_default_root(); gnode;
1254 gnode = purple_blist_node_get_sibling_next(gnode)) {
1255 if (!PURPLE_IS_GROUP(gnode))
1256 continue;
1257 group = (PurpleGroup *) gnode;
1258 gname = purple_group_get_name(group);
1259 for (cnode = purple_blist_node_get_first_child(gnode);
1260 cnode;
1261 cnode = purple_blist_node_get_sibling_next(cnode)) {
1262 if (!PURPLE_IS_CONTACT(cnode))
1263 continue;
1264 for (bnode = purple_blist_node_get_first_child(cnode);
1265 bnode;
1266 bnode = purple_blist_node_get_sibling_next(bnode)) {
1267 if (!PURPLE_IS_BUDDY(bnode))
1268 continue;
1269 buddy = (PurpleBuddy *) bnode;
1270 if (purple_buddy_get_account(buddy) == user->client_data) {
1271 if (purple_strequal(gname, NM_ROOT_FOLDER_NAME))
1272 gname = "";
1273 folder = nm_find_folder(user, gname);
1274 if (folder == NULL ||
1275 !nm_folder_find_contact_by_display_id(folder, purple_buddy_get_name(buddy))) {
1276 rem_list = g_slist_append(rem_list, buddy);
1283 if (rem_list) {
1284 for (l = rem_list; l; l = l->next) {
1285 purple_blist_remove_buddy(l->data);
1287 g_slist_free(rem_list);
1291 /* Add all of the contacts in the given folder to the Purple buddy list */
1292 static void
1293 _add_contacts_to_purple_blist(NMUser * user, NMFolder * folder)
1295 NMUserRecord *user_record = NULL;
1296 NMContact *contact = NULL;
1297 PurpleBuddy *buddy = NULL;
1298 PurpleGroup *group;
1299 NMERR_T cnt = 0, i;
1300 const char *name = NULL;
1301 const char *fname = NULL;
1302 int status = 0;
1304 /* If this is the root folder give it a name. Purple does not have the concept of
1305 * a root folder.
1307 fname = nm_folder_get_name(folder);
1308 if (fname == NULL || *fname == '\0') {
1309 fname = NM_ROOT_FOLDER_NAME;
1312 /* Does the Purple group exist already? */
1313 group = purple_blist_find_group(fname);
1314 if (group == NULL) {
1315 group = purple_group_new(fname);
1316 purple_blist_add_group(group, NULL);
1319 /* Get each contact for this folder */
1320 cnt = nm_folder_get_contact_count(folder);
1321 for (i = 0; i < cnt; i++) {
1322 contact = nm_folder_get_contact(folder, i);
1323 if (contact) {
1325 name = nm_contact_get_display_id(contact);
1326 if (name) {
1328 buddy = purple_blist_find_buddy_in_group(user->client_data, name, group);
1329 if (buddy == NULL) {
1330 /* Add it to the purple buddy list */
1331 buddy = purple_buddy_new(user->client_data,
1332 name,
1333 nm_contact_get_display_name(contact));
1335 purple_blist_add_buddy(buddy, NULL, group, NULL);
1338 /* Set the initial status for the buddy */
1339 user_record = nm_contact_get_user_record(contact);
1340 if (user_record) {
1341 status = nm_user_record_get_status(user_record);
1343 _update_buddy_status(user, buddy, status, time(0));
1345 /* Save the new buddy as part of the contact object */
1346 nm_contact_set_data(contact, (gpointer) buddy);
1349 } else {
1350 /* NULL contact. This should not happen, but
1351 * let's break out of the loop.
1353 break;
1358 /* Add all of the server side contacts to the Purple buddy list. */
1359 static void
1360 _add_purple_buddies(NMUser * user)
1362 int cnt = 0, i;
1363 NMFolder *root_folder = NULL;
1364 NMFolder *folder = NULL;
1366 root_folder = nm_get_root_folder(user);
1367 if (root_folder) {
1369 /* Add sub-folders and contacts to sub-folders...
1370 * iterate throught the sub-folders in reverse order
1371 * because Purple adds the folders to the front -- so we
1372 * want to add the first folder last
1374 cnt = nm_folder_get_subfolder_count(root_folder);
1375 for (i = cnt-1; i >= 0; i--) {
1376 folder = nm_folder_get_subfolder(root_folder, i);
1377 if (folder) {
1378 _add_contacts_to_purple_blist(user, folder);
1382 /* Add contacts for the root folder */
1383 _add_contacts_to_purple_blist(user, root_folder);
1387 static void
1388 _sync_contact_list(NMUser *user)
1390 /* Remove all buddies from the local list that are
1391 * not in the server side list and add all buddies
1392 * from the server side list that are not in
1393 * the local list
1395 _remove_purple_buddies(user);
1396 _add_purple_buddies(user);
1397 user->clist_synched = TRUE;
1400 static void
1401 _sync_privacy_lists(NMUser *user)
1403 GSList *node = NULL, *rem_list = NULL;
1404 PurpleConnection *gc;
1405 PurpleAccount *account;
1406 const char *name, *dn;
1407 NMUserRecord *user_record;
1409 if (user == NULL)
1410 return;
1412 gc = purple_account_get_connection(user->client_data);
1413 if (gc == NULL)
1414 return;
1416 account = purple_connection_get_account(gc);
1418 /* Set the Purple privacy setting */
1419 if (user->default_deny) {
1420 if (user->allow_list == NULL) {
1421 purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_DENY_ALL);
1422 } else {
1423 purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS);
1425 } else {
1426 if (user->deny_list == NULL) {
1427 purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL);
1428 } else {
1429 purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_DENY_USERS);
1433 /* Add stuff */
1434 for (node = user->allow_list; node; node = node->next) {
1435 user_record = nm_find_user_record(user, (char *)node->data);
1436 if (user_record)
1437 name = nm_user_record_get_display_id(user_record);
1438 else
1439 name =(char *)node->data;
1441 if (!g_slist_find_custom(purple_account_privacy_get_permitted(account),
1442 name, (GCompareFunc)purple_utf8_strcasecmp)) {
1443 purple_account_privacy_permit_add(account, name , TRUE);
1447 for (node = user->deny_list; node; node = node->next) {
1448 user_record = nm_find_user_record(user, (char *)node->data);
1449 if (user_record)
1450 name = nm_user_record_get_display_id(user_record);
1451 else
1452 name =(char *)node->data;
1454 if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
1455 name, (GCompareFunc)purple_utf8_strcasecmp)) {
1456 purple_account_privacy_deny_add(account, name, TRUE);
1461 /* Remove stuff */
1462 for (node = purple_account_privacy_get_permitted(account); node; node = node->next) {
1463 dn = nm_lookup_dn(user, (char *)node->data);
1464 if (dn != NULL &&
1465 !g_slist_find_custom(user->allow_list,
1466 dn, (GCompareFunc)purple_utf8_strcasecmp)) {
1467 rem_list = g_slist_append(rem_list, node->data);
1471 if (rem_list) {
1472 for (node = rem_list; node; node = node->next) {
1473 purple_account_privacy_permit_remove(account, (char *)node->data, TRUE);
1475 g_slist_free(rem_list);
1476 rem_list = NULL;
1479 for (node = purple_account_privacy_get_denied(account); node; node = node->next) {
1480 dn = nm_lookup_dn(user, (char *)node->data);
1481 if (dn != NULL &&
1482 !g_slist_find_custom(user->deny_list,
1483 dn, (GCompareFunc)purple_utf8_strcasecmp)) {
1484 rem_list = g_slist_append(rem_list, node->data);
1488 if (rem_list) {
1489 for (node = rem_list; node; node = node->next) {
1490 purple_account_privacy_deny_remove(account, (char *)node->data, TRUE);
1492 g_slist_free(rem_list);
1496 /* Map known property tags to user-friendly strings */
1497 static const char *
1498 _map_property_tag(const char *tag)
1500 if (tag == NULL) return NULL;
1502 if (purple_strequal(tag, "telephoneNumber"))
1503 return _("Telephone Number");
1504 else if (purple_strequal(tag, "L"))
1505 return _("Location");
1506 else if (purple_strequal(tag, "OU"))
1507 return _("Department");
1508 else if (purple_strequal(tag, "personalTitle"))
1509 return _("Personal Title");
1510 else if (purple_strequal(tag, "Title"))
1511 return _("Job Title");
1512 else if (purple_strequal(tag, "mailstop"))
1513 return _("Mailstop");
1514 else if (purple_strequal(tag, "Internet EMail Address"))
1515 return _("Email Address");
1516 else
1517 return tag;
1520 /* Display a dialog box showing the properties for the given user record */
1521 static void
1522 _show_info(PurpleConnection * gc, NMUserRecord * user_record, char * name)
1524 PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
1525 int count, i;
1526 NMProperty *property;
1527 const char *tag, *value;
1529 tag = _("User ID");
1530 value = nm_user_record_get_userid(user_record);
1531 if (value) {
1532 /* TODO: Check whether it's correct to call add_pair_html,
1533 or if we should be using add_pair_plaintext */
1534 purple_notify_user_info_add_pair_html(user_info, tag, value);
1537 tag = _("Full name");
1538 value = nm_user_record_get_full_name(user_record);
1539 if (value) {
1540 /* TODO: Check whether it's correct to call add_pair_html,
1541 or if we should be using add_pair_plaintext */
1542 purple_notify_user_info_add_pair_html(user_info, tag, value);
1545 count = nm_user_record_get_property_count(user_record);
1546 for (i = 0; i < count; i++) {
1547 property = nm_user_record_get_property(user_record, i);
1548 if (property) {
1549 tag = _map_property_tag(nm_property_get_tag(property));
1550 value = nm_property_get_value(property);
1551 if (tag && value) {
1552 /* TODO: Check whether it's correct to call add_pair_html,
1553 or if we should be using add_pair_plaintext */
1554 purple_notify_user_info_add_pair_html(user_info, tag, value);
1556 nm_release_property(property);
1560 purple_notify_userinfo(gc, name, user_info, NULL, NULL);
1561 purple_notify_user_info_destroy(user_info);
1563 g_free(name);
1566 /* Send a join conference, the first item in the parms list is the
1567 * NMUser object and the second item is the conference to join.
1568 * This callback is passed to purple_request_action when we ask the
1569 * user if they want to join the conference.
1571 static void
1572 _join_conference_cb(GSList * parms)
1574 NMUser *user;
1575 NMConference *conference;
1576 NMERR_T rc = NM_OK;
1578 if (parms == NULL || g_slist_length(parms) != 2)
1579 return;
1581 user = g_slist_nth_data(parms, 0);
1582 conference = g_slist_nth_data(parms, 1);
1584 if (user && conference) {
1585 rc = nm_send_join_conference(user, conference,
1586 _join_conf_resp_cb, conference);
1587 _check_for_disconnect(user, rc);
1590 g_slist_free(parms);
1593 /* Send a reject conference, the first item in the parms list is the
1594 * NMUser object and the second item is the conference to reject.
1595 * This callback is passed to purple_request_action when we ask the
1596 * user if they want to joing the conference.
1598 static void
1599 _reject_conference_cb(GSList * parms)
1601 NMUser *user;
1602 NMConference *conference;
1603 NMERR_T rc = NM_OK;
1605 if (parms == NULL || g_slist_length(parms) != 2)
1606 return;
1608 user = g_slist_nth_data(parms, 0);
1609 conference = g_slist_nth_data(parms, 1);
1611 if (user && conference) {
1612 rc = nm_send_reject_conference(user, conference, NULL, NULL);
1613 _check_for_disconnect(user, rc);
1616 g_slist_free(parms);
1619 static void
1620 _initiate_conference_cb(PurpleBlistNode *node, gpointer ignored)
1622 PurpleBuddy *buddy;
1623 PurpleConnection *gc;
1625 NMUser *user;
1626 const char *conf_name;
1627 PurpleChatConversation *chat = NULL;
1628 NMUserRecord *user_record;
1629 NMConference *conference;
1631 g_return_if_fail(PURPLE_IS_BUDDY(node));
1633 buddy = (PurpleBuddy *) node;
1634 gc = purple_account_get_connection(purple_buddy_get_account(buddy));
1636 user = purple_connection_get_protocol_data(gc);
1637 if (user == NULL)
1638 return;
1640 /* We should already have a userrecord for the buddy */
1641 user_record = nm_find_user_record(user, purple_buddy_get_name(buddy));
1642 if (user_record == NULL)
1643 return;
1645 conf_name = _get_conference_name(++user->conference_count);
1646 chat = purple_serv_got_joined_chat(gc, user->conference_count, conf_name);
1647 if (chat) {
1649 conference = nm_create_conference(NULL);
1650 nm_conference_set_data(conference, (gpointer) chat);
1651 nm_send_create_conference(user, conference, _createconf_resp_send_invite, user_record);
1652 nm_release_conference(conference);
1656 const char *
1657 _get_conference_name(int id)
1659 static char *name = NULL;
1661 g_free(name);
1663 name = g_strdup_printf(_("GroupWise Conference %d"), id);
1665 return name;
1668 static void
1669 _show_privacy_locked_error(PurpleConnection *gc, NMUser *user)
1671 char *err;
1673 err = g_strdup_printf(_("Unable to change server side privacy settings (%s)."),
1674 nm_error_to_string(NMERR_ADMIN_LOCKED));
1675 purple_notify_error(gc, NULL, err, NULL,
1676 purple_request_cpar_from_connection(gc));
1677 g_free(err);
1680 /*******************************************************************************
1681 * Connect and recv callbacks
1682 ******************************************************************************/
1684 static void
1685 novell_ssl_connect_error(PurpleSslConnection * gsc,
1686 PurpleSslErrorType error, gpointer data)
1688 PurpleConnection *gc;
1689 NMUser *user;
1691 gc = data;
1692 user = purple_connection_get_protocol_data(gc);
1693 user->conn->ssl_conn->data = NULL;
1695 purple_connection_ssl_error (gc, error);
1698 static void
1699 novell_ssl_recv_cb(gpointer data, PurpleSslConnection * gsc,
1700 PurpleInputCondition condition)
1702 PurpleConnection *gc = data;
1703 NMUser *user;
1704 NMERR_T rc;
1706 if (gc == NULL)
1707 return;
1709 user = purple_connection_get_protocol_data(gc);
1710 if (user == NULL)
1711 return;
1713 rc = nm_process_new_data(user);
1714 if (rc != NM_OK) {
1716 if (_is_disconnect_error(rc)) {
1718 purple_connection_error(gc,
1719 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
1720 _("Error communicating with server. Closing connection."));
1721 } else {
1722 purple_debug(PURPLE_DEBUG_INFO, "novell",
1723 "Error processing event or response (%d).\n", rc);
1728 static void
1729 novell_ssl_connected_cb(gpointer data, PurpleSslConnection * gsc,
1730 PurpleInputCondition cond)
1732 PurpleConnection *gc = data;
1733 NMUser *user;
1734 NMConn *conn;
1735 NMERR_T rc = 0;
1736 const char *pwd = NULL;
1737 const char *my_addr = NULL;
1738 char *ua = NULL;
1740 if (gc == NULL || gsc == NULL)
1741 return;
1743 user = purple_connection_get_protocol_data(gc);
1744 if ((user == NULL) || (conn = user->conn) == NULL)
1745 return;
1747 purple_connection_update_progress(gc, _("Authenticating..."),
1748 2, NOVELL_CONNECT_STEPS);
1750 my_addr = purple_network_get_my_ip(gsc->fd);
1751 pwd = purple_connection_get_password(gc);
1752 ua = _user_agent_string();
1754 rc = nm_send_login(user, pwd, my_addr, ua, _login_resp_cb, NULL);
1755 if (rc == NM_OK) {
1756 conn->connected = TRUE;
1757 purple_ssl_input_add(gsc, novell_ssl_recv_cb, gc);
1758 } else {
1759 purple_connection_error(gc,
1760 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
1761 _("Unable to connect"));
1764 purple_connection_update_progress(gc, _("Waiting for response..."),
1765 3, NOVELL_CONNECT_STEPS);
1767 g_free(ua);
1770 /*******************************************************************************
1771 * Event callback and event handlers
1772 ******************************************************************************/
1774 static void
1775 _evt_receive_message(NMUser * user, NMEvent * event)
1777 NMUserRecord *user_record = NULL;
1778 NMContact *contact = NULL;
1779 PurpleIMConversation *im;
1780 NMConference *conference;
1781 PurpleMessageFlags flags;
1782 char *text = NULL;
1784 text = g_markup_escape_text(nm_event_get_text(event), -1);
1786 conference = nm_event_get_conference(event);
1787 if (conference) {
1789 PurpleChatConversation *chat = nm_conference_get_data(conference);
1791 /* Is this a single person 'conversation' or a conference? */
1792 if (chat == NULL && nm_conference_get_participant_count(conference) == 1) {
1794 user_record = nm_find_user_record(user, nm_event_get_source(event));
1795 if (user_record) {
1797 flags = 0;
1798 if (nm_event_get_type(event) == NMEVT_RECEIVE_AUTOREPLY)
1799 flags |= PURPLE_MESSAGE_AUTO_RESP;
1801 purple_serv_got_im(purple_account_get_connection(user->client_data),
1802 nm_user_record_get_display_id(user_record),
1803 text, flags,
1804 nm_event_get_gmt(event));
1806 im = purple_conversations_find_im_with_account(
1807 nm_user_record_get_display_id(user_record),
1808 (PurpleAccount *) user->client_data);
1809 if (im) {
1811 contact = nm_find_contact(user, nm_event_get_source(event));
1812 if (contact) {
1814 purple_conversation_set_title(PURPLE_CONVERSATION(im),
1815 nm_contact_get_display_name(contact));
1818 } else {
1820 const char *name =
1821 nm_user_record_get_full_name(user_record);
1823 if (name == NULL)
1824 name = nm_user_record_get_userid(user_record);
1826 purple_conversation_set_title(PURPLE_CONVERSATION(im), name);
1831 } else {
1832 /* this should not happen, see the event code.
1833 * the event code will get the contact details from
1834 * the server if it does not have them before calling
1835 * the event callback.
1839 } else if (chat) {
1841 /* get the contact for send if we have one */
1842 NMContact *contact = nm_find_contact(user,
1843 nm_event_get_source(event));
1845 /* get the user record for the sender */
1846 user_record = nm_find_user_record(user, nm_event_get_source(event));
1847 if (user_record) {
1848 const char *name = nm_contact_get_display_name(contact);
1850 if (name == NULL) {
1851 name = nm_user_record_get_full_name(user_record);
1852 if (name == NULL)
1853 name = nm_user_record_get_display_id(user_record);
1856 purple_serv_got_chat_in(purple_account_get_connection(user->client_data),
1857 purple_chat_conversation_get_id(chat),
1858 name, PURPLE_MESSAGE_RECV, text, nm_event_get_gmt(event));
1863 g_free(text);
1866 static void
1867 _evt_conference_left(NMUser * user, NMEvent * event)
1869 PurpleChatConversation *chat;
1870 NMConference *conference;
1872 conference = nm_event_get_conference(event);
1873 if (conference) {
1874 chat = nm_conference_get_data(conference);
1875 if (chat) {
1876 NMUserRecord *ur = nm_find_user_record(user,
1877 nm_event_get_source(event));
1879 if (ur)
1880 purple_chat_conversation_remove_user(chat,
1881 nm_user_record_get_display_id(ur),
1882 NULL);
1887 static void
1888 _evt_conference_invite_notify(NMUser * user, NMEvent * event)
1890 PurpleConversation *gconv;
1891 NMConference *conference;
1892 NMUserRecord *user_record = NULL;
1893 char *str = NULL;
1895 user_record = nm_find_user_record(user, nm_event_get_source(event));
1896 conference = nm_event_get_conference(event);
1897 if (user_record && conference) {
1898 gconv = nm_conference_get_data(conference);
1899 str = g_strdup_printf(_("%s has been invited to this conversation."),
1900 nm_user_record_get_display_id(user_record));
1901 purple_conversation_write_system_message(gconv, str, 0);
1902 g_free(str);
1906 static void
1907 _evt_conference_invite(NMUser * user, NMEvent * event)
1909 NMUserRecord *ur;
1910 PurpleConnection *gc;
1911 GSList *parms = NULL;
1912 const char *title = NULL;
1913 const char *secondary = NULL;
1914 const char *name = NULL;
1915 char *primary = NULL;
1916 time_t gmt;
1918 ur = nm_find_user_record(user, nm_event_get_source(event));
1919 if (ur)
1920 name = nm_user_record_get_full_name(ur);
1922 if (name == NULL)
1923 name = nm_event_get_source(event);
1925 gmt = nm_event_get_gmt(event);
1926 title = _("Invitation to Conversation");
1927 primary = g_strdup_printf(_("Invitation from: %s\n\nSent: %s"),
1928 name, purple_date_format_full(localtime(&gmt)));
1929 secondary = _("Would you like to join the conversation?");
1931 /* Set up parms list for the callbacks
1932 * We need to send the NMUser object and
1933 * the NMConference object to the callbacks
1935 parms = NULL;
1936 parms = g_slist_append(parms, user);
1937 parms = g_slist_append(parms, nm_event_get_conference(event));
1939 /* Prompt the user */
1940 /* TODO: Would it be better to use purple_serv_got_chat_invite() here? */
1941 gc = purple_account_get_connection(user->client_data);
1942 purple_request_action(gc, title, primary, secondary,
1943 PURPLE_DEFAULT_ACTION_NONE,
1944 purple_request_cpar_from_connection(gc),
1945 parms, 2,
1946 _("Yes"), G_CALLBACK(_join_conference_cb),
1947 _("No"), G_CALLBACK(_reject_conference_cb));
1949 g_free(primary);
1953 static void
1954 _evt_conference_joined(NMUser * user, NMEvent * event)
1956 PurpleChatConversation *chat = NULL;
1957 PurpleConnection *gc;
1958 NMConference *conference = NULL;
1959 NMUserRecord *ur = NULL;
1960 const char *name;
1961 const char *conf_name;
1963 gc = purple_account_get_connection(user->client_data);
1964 if (gc == NULL)
1965 return;
1967 conference = nm_event_get_conference(event);
1968 if (conference) {
1969 chat = nm_conference_get_data(conference);
1970 if (nm_conference_get_participant_count(conference) == 2 && chat == NULL) {
1971 ur = nm_conference_get_participant(conference, 0);
1972 if (ur) {
1973 conf_name = _get_conference_name(++user->conference_count);
1974 chat =
1975 purple_serv_got_joined_chat(gc, user->conference_count, conf_name);
1976 if (chat) {
1978 nm_conference_set_data(conference, (gpointer) chat);
1980 name = nm_user_record_get_display_id(ur);
1981 purple_chat_conversation_add_user(chat, name, NULL,
1982 PURPLE_CHAT_USER_NONE, TRUE);
1988 if (chat != NULL) {
1989 ur = nm_find_user_record(user, nm_event_get_source(event));
1990 if (ur) {
1991 name = nm_user_record_get_display_id(ur);
1992 if (!purple_chat_conversation_has_user(chat, name)) {
1993 purple_chat_conversation_add_user(chat, name, NULL,
1994 PURPLE_CHAT_USER_NONE, TRUE);
2001 static void
2002 _evt_status_change(NMUser * user, NMEvent * event)
2004 PurpleBuddy *buddy = NULL;
2005 GSList *buddies;
2006 GSList *bnode;
2007 NMUserRecord *user_record;
2008 const char *display_id;
2009 int status;
2011 user_record = nm_event_get_user_record(event);
2012 if (user_record) {
2014 /* Retrieve new status */
2015 status = nm_user_record_get_status(user_record);
2017 /* Update status for buddy in all folders */
2018 display_id = nm_user_record_get_display_id(user_record);
2019 buddies = purple_blist_find_buddies(user->client_data, display_id);
2020 for (bnode = buddies; bnode; bnode = bnode->next) {
2021 buddy = (PurpleBuddy *) bnode->data;
2022 if (buddy) {
2023 _update_buddy_status(user, buddy, status, nm_event_get_gmt(event));
2027 g_slist_free(buddies);
2032 static void
2033 _evt_user_disconnect(NMUser * user, NMEvent * event)
2035 PurpleConnection *gc;
2036 PurpleAccount *account = user->client_data;
2038 gc = purple_account_get_connection(account);
2039 if (gc)
2041 if (!purple_account_get_remember_password(account))
2042 purple_account_set_password(account, NULL, NULL, NULL);
2043 purple_connection_error(gc,
2044 PURPLE_CONNECTION_ERROR_NAME_IN_USE,
2045 _("You have signed on from another location"));
2049 static void
2050 _evt_user_typing(NMUser * user, NMEvent * event)
2052 PurpleConnection *gc;
2053 NMUserRecord *user_record = NULL;
2055 gc = purple_account_get_connection((PurpleAccount *) user->client_data);
2056 if (gc) {
2057 user_record = nm_find_user_record(user, nm_event_get_source(event));
2058 if (user_record) {
2059 purple_serv_got_typing(gc, nm_user_record_get_display_id(user_record),
2060 30, PURPLE_IM_TYPING);
2065 static void
2066 _evt_user_not_typing(NMUser * user, NMEvent * event)
2068 PurpleConnection *gc;
2069 NMUserRecord *user_record;
2071 gc = purple_account_get_connection((PurpleAccount *) user->client_data);
2072 if (gc) {
2073 user_record = nm_find_user_record(user, nm_event_get_source(event));
2074 if (user_record) {
2075 purple_serv_got_typing_stopped(gc,
2076 nm_user_record_get_display_id(user_record));
2081 static void
2082 _evt_undeliverable_status(NMUser * user, NMEvent * event)
2084 NMUserRecord *ur;
2085 PurpleConversation *gconv;
2086 char *str;
2088 ur = nm_find_user_record(user, nm_event_get_source(event));
2089 if (ur) {
2090 /* XXX - Should this be PURPLE_CONV_TYPE_IM? */
2091 gconv =
2092 purple_conversations_find_with_account(nm_user_record_get_display_id(ur),
2093 user->client_data);
2094 if (gconv) {
2095 const char *name = nm_user_record_get_full_name(ur);
2097 if (name == NULL) {
2098 name = nm_user_record_get_display_id(ur);
2100 str = g_strdup_printf(_("%s appears to be offline and did not receive"
2101 " the message that you just sent."), name);
2102 purple_conversation_write_system_message(gconv, str, 0);
2103 g_free(str);
2108 static void
2109 _event_callback(NMUser * user, NMEvent * event)
2111 if (user == NULL || event == NULL)
2112 return;
2114 switch (nm_event_get_type(event)) {
2115 case NMEVT_STATUS_CHANGE:
2116 _evt_status_change(user, event);
2117 break;
2118 case NMEVT_RECEIVE_AUTOREPLY:
2119 case NMEVT_RECEIVE_MESSAGE:
2120 _evt_receive_message(user, event);
2121 break;
2122 case NMEVT_USER_DISCONNECT:
2123 _evt_user_disconnect(user, event);
2124 break;
2125 case NMEVT_USER_TYPING:
2126 _evt_user_typing(user, event);
2127 break;
2128 case NMEVT_USER_NOT_TYPING:
2129 _evt_user_not_typing(user, event);
2130 break;
2131 case NMEVT_SERVER_DISCONNECT:
2132 /* Nothing to do? */
2133 break;
2134 case NMEVT_INVALID_RECIPIENT:
2135 break;
2136 case NMEVT_UNDELIVERABLE_STATUS:
2137 _evt_undeliverable_status(user, event);
2138 break;
2139 case NMEVT_CONFERENCE_INVITE_NOTIFY:
2140 /* Someone else has been invited to join a
2141 * conference that we are currently a part of
2143 _evt_conference_invite_notify(user, event);
2144 break;
2145 case NMEVT_CONFERENCE_INVITE:
2146 /* We have been invited to join a conference */
2147 _evt_conference_invite(user, event);
2148 break;
2149 case NMEVT_CONFERENCE_JOINED:
2150 /* Some one has joined a conference that we
2151 * are a part of
2153 _evt_conference_joined(user, event);
2154 break;
2155 case NMEVT_CONFERENCE_LEFT:
2156 /* Someone else has left a conference that we
2157 * are currently a part of
2159 _evt_conference_left(user, event);
2160 break;
2161 default:
2162 purple_debug(PURPLE_DEBUG_INFO, "novell",
2163 "_event_callback(): unhandled event, %d\n",
2164 nm_event_get_type(event));
2165 break;
2169 /*******************************************************************************
2170 * Protocol Ops
2171 ******************************************************************************/
2173 static void
2174 novell_login(PurpleAccount * account)
2176 PurpleConnection *gc;
2177 NMUser *user = NULL;
2178 const char *server;
2179 const char *name;
2180 int port;
2182 if (account == NULL)
2183 return;
2185 gc = purple_account_get_connection(account);
2186 if (gc == NULL)
2187 return;
2189 purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_NO_IMAGES);
2191 server = purple_account_get_string(account, "server", NULL);
2192 if (server == NULL || *server == '\0') {
2194 /* TODO: Would be nice to prompt if not set!
2195 * purple_request_fields(gc, _("Server Address"),...);
2198 /* ...but for now just error out with a nice message. */
2199 purple_connection_error(gc,
2200 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
2201 _("Unable to connect to server. Please enter the "
2202 "address of the server to which you wish to connect."));
2203 return;
2206 port = purple_account_get_int(account, "port", DEFAULT_PORT);
2207 name = purple_account_get_username(account);
2209 user = nm_initialize_user(name, server, port, account, _event_callback);
2210 if (user && user->conn) {
2211 /* save user */
2212 purple_connection_set_protocol_data(gc, user);
2214 /* connect to the server */
2215 purple_connection_update_progress(gc, _("Connecting"),
2216 1, NOVELL_CONNECT_STEPS);
2218 user->conn->use_ssl = TRUE;
2220 user->conn->ssl_conn = g_new0(NMSSLConn, 1);
2221 user->conn->ssl_conn->read = (nm_ssl_read_cb) purple_ssl_read;
2222 user->conn->ssl_conn->write = (nm_ssl_write_cb) purple_ssl_write;
2224 user->conn->ssl_conn->data = purple_ssl_connect(user->client_data,
2225 user->conn->addr, user->conn->port,
2226 novell_ssl_connected_cb, novell_ssl_connect_error, gc);
2227 if (user->conn->ssl_conn->data == NULL) {
2228 purple_connection_error(gc,
2229 PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
2230 _("SSL support unavailable"));
2235 static void
2236 novell_close(PurpleConnection * gc)
2238 NMUser *user;
2239 NMConn *conn;
2241 if (gc == NULL)
2242 return;
2244 user = purple_connection_get_protocol_data(gc);
2245 if (user) {
2246 conn = user->conn;
2247 if (conn && conn->ssl_conn) {
2248 purple_ssl_close(user->conn->ssl_conn->data);
2250 nm_deinitialize_user(user);
2252 purple_connection_set_protocol_data(gc, NULL);
2255 static int
2256 novell_send_im(PurpleConnection *gc, PurpleMessage *msg)
2258 NMUserRecord *user_record = NULL;
2259 NMConference *conf = NULL;
2260 NMMessage *message;
2261 NMUser *user;
2262 const char *dn = NULL;
2263 char *plain;
2264 gboolean done = TRUE, created_conf = FALSE;
2265 NMERR_T rc = NM_OK;
2266 const gchar *name = purple_message_get_recipient(msg);
2268 if (gc == NULL || name == NULL || purple_message_is_empty(msg))
2269 return 0;
2271 user = purple_connection_get_protocol_data(gc);
2272 if (user == NULL)
2273 return 0;
2275 /* Create a new message */
2276 plain = purple_unescape_html(purple_message_get_contents(msg));
2277 message = nm_create_message(plain);
2278 g_free(plain);
2280 /* Need to get the DN for the buddy so we can look up the convo */
2281 dn = nm_lookup_dn(user, name);
2283 /* Do we already know about the sender? */
2284 user_record = nm_find_user_record(user, dn);
2285 if (user_record) {
2287 /* Do we already have an instantiated conference? */
2288 conf = nm_find_conversation(user, dn);
2289 if (conf == NULL) {
2291 /* If not, create a blank conference */
2292 conf = nm_create_conference(NULL);
2293 created_conf = TRUE;
2295 nm_conference_add_participant(conf, user_record);
2298 nm_message_set_conference(message, conf);
2300 /* Make sure conference is instantiated */
2301 if (!nm_conference_is_instantiated(conf)) {
2303 /* It is not, so send the createconf. We will
2304 * have to finish sending the message when we
2305 * get the response with the new conference guid.
2307 rc = nm_send_create_conference(user, conf, _createconf_resp_send_msg, message);
2308 _check_for_disconnect(user, rc);
2310 done = FALSE;
2313 } else {
2315 /* If we don't have details for the user, then we don't have
2316 * a conference yet. So create one and send the getdetails
2317 * to the server. We will have to finish sending the message
2318 * when we get the response from the server.
2320 conf = nm_create_conference(NULL);
2321 created_conf = TRUE;
2323 nm_message_set_conference(message, conf);
2325 rc = nm_send_get_details(user, name, _get_details_resp_send_msg, message);
2326 _check_for_disconnect(user, rc);
2328 done = FALSE;
2331 if (done) {
2333 /* Did we find everything we needed? */
2334 rc = nm_send_message(user, message, _send_message_resp_cb);
2335 _check_for_disconnect(user, rc);
2337 nm_release_message(message);
2340 if (created_conf && conf)
2341 nm_release_conference(conf);
2343 return 1;
2346 static unsigned int
2347 novell_send_typing(PurpleConnection * gc, const char *name, PurpleIMTypingState state)
2349 NMConference *conf = NULL;
2350 NMUser *user;
2351 const char *dn = NULL;
2352 NMERR_T rc = NM_OK;
2354 if (gc == NULL || name == NULL)
2355 return 0;
2357 user = purple_connection_get_protocol_data(gc);
2358 if (user == NULL)
2359 return 0;
2361 /* Need to get the DN for the buddy so we can look up the convo */
2362 dn = nm_lookup_dn(user, name);
2363 if (dn) {
2365 /* Now find the conference in our list */
2366 conf = nm_find_conversation(user, dn);
2367 if (conf) {
2369 rc = nm_send_typing(user, conf,
2370 ((state == PURPLE_IM_TYPING) ? TRUE : FALSE), NULL);
2371 _check_for_disconnect(user, rc);
2377 return 0;
2380 static void
2381 novell_convo_closed(PurpleConnection * gc, const char *who)
2383 NMUser *user;
2384 NMConference *conf;
2385 const char *dn;
2386 NMERR_T rc = NM_OK;
2388 if (gc == NULL || who == NULL)
2389 return;
2391 user = purple_connection_get_protocol_data(gc);
2392 if (user && (dn = nm_lookup_dn(user, who))) {
2393 conf = nm_find_conversation(user, dn);
2394 if (conf) {
2395 rc = nm_send_leave_conference(user, conf, NULL, NULL);
2396 _check_for_disconnect(user, rc);
2401 static void
2402 novell_chat_leave(PurpleConnection * gc, int id)
2404 NMConference *conference;
2405 NMUser *user;
2406 PurpleChatConversation *chat;
2407 GSList *cnode;
2408 NMERR_T rc = NM_OK;
2410 if (gc == NULL)
2411 return;
2413 user = purple_connection_get_protocol_data(gc);
2414 if (user == NULL)
2415 return;
2417 for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
2418 conference = cnode->data;
2419 if (conference && (chat = nm_conference_get_data(conference))) {
2420 if (purple_chat_conversation_get_id(chat) == id) {
2421 rc = nm_send_leave_conference(user, conference, NULL, NULL);
2422 _check_for_disconnect(user, rc);
2423 break;
2428 purple_serv_got_chat_left(gc, id);
2431 static void
2432 novell_chat_invite(PurpleConnection *gc, int id,
2433 const char *message, const char *who)
2435 NMConference *conference;
2436 NMUser *user;
2437 PurpleChatConversation *chat;
2438 GSList *cnode;
2439 NMERR_T rc = NM_OK;
2440 NMUserRecord *user_record = NULL;
2442 if (gc == NULL)
2443 return;
2445 user = purple_connection_get_protocol_data(gc);
2446 if (user == NULL)
2447 return;
2449 user_record = nm_find_user_record(user, who);
2450 if (user_record == NULL) {
2451 rc = nm_send_get_details(user, who, _get_details_resp_send_invite, GINT_TO_POINTER(id));
2452 _check_for_disconnect(user, rc);
2453 return;
2456 for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
2457 conference = cnode->data;
2458 if (conference && (chat = nm_conference_get_data(conference))) {
2459 if (purple_chat_conversation_get_id(chat) == id) {
2460 rc = nm_send_conference_invite(user, conference, user_record,
2461 message, _sendinvite_resp_cb, NULL);
2462 _check_for_disconnect(user, rc);
2463 break;
2469 static int
2470 novell_chat_send(PurpleConnection * gc, int id, PurpleMessage *msg)
2472 NMConference *conference;
2473 PurpleChatConversation *chat;
2474 GSList *cnode;
2475 NMMessage *message;
2476 NMUser *user;
2477 NMERR_T rc = NM_OK;
2478 const char *name;
2479 char *str, *plain;
2481 if (gc == NULL || purple_message_is_empty(msg))
2482 return -1;
2484 user = purple_connection_get_protocol_data(gc);
2485 if (user == NULL)
2486 return -1;
2488 plain = purple_unescape_html(purple_message_get_contents(msg));
2489 message = nm_create_message(plain);
2490 g_free(plain);
2492 for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
2493 conference = cnode->data;
2494 if (conference && (chat = nm_conference_get_data(conference))) {
2495 if (purple_chat_conversation_get_id(chat) == id) {
2497 nm_message_set_conference(message, conference);
2499 /* check to see if the conference is instatiated yet */
2500 if (!nm_conference_is_instantiated(conference)) {
2501 nm_message_add_ref(message);
2502 nm_send_create_conference(user, conference, _createconf_resp_send_msg, message);
2503 } else {
2504 rc = nm_send_message(user, message, _send_message_resp_cb);
2507 nm_release_message(message);
2509 if (!_check_for_disconnect(user, rc)) {
2511 /* Use the account alias if it is set */
2512 name = purple_account_get_private_alias(user->client_data);
2513 if (name == NULL || *name == '\0') {
2515 /* If there is no account alias, try full name */
2516 name = nm_user_record_get_full_name(user->user_record);
2517 if (name == NULL || *name == '\0') {
2519 /* Fall back to the username that we are signed in with */
2520 name = purple_account_get_username(user->client_data);
2524 purple_serv_got_chat_in(gc, id, name,
2525 purple_message_get_flags(msg),
2526 purple_message_get_contents(msg), time(NULL));
2527 return 0;
2528 } else
2529 return -1;
2536 /* The conference was not found, must be closed */
2537 chat = purple_conversations_find_chat(gc, id);
2538 if (chat) {
2539 str = g_strdup(_("This conference has been closed."
2540 " No more messages can be sent."));
2541 purple_conversation_write_system_message(PURPLE_CONVERSATION(chat), str, 0);
2542 g_free(str);
2545 if (message)
2546 nm_release_message(message);
2548 return -1;
2551 static void
2552 novell_add_buddy(PurpleConnection * gc, PurpleBuddy *buddy, PurpleGroup * group, const char *message)
2554 NMFolder *folder = NULL;
2555 NMContact *contact;
2556 NMUser *user;
2557 NMERR_T rc = NM_OK;
2558 const char *alias, *gname, *bname;
2560 if (gc == NULL || buddy == NULL || group == NULL)
2561 return;
2563 user = (NMUser *) purple_connection_get_protocol_data(gc);
2564 if (user == NULL)
2565 return;
2567 /* If we haven't synched the contact list yet, ignore
2568 * the add_buddy calls. Server side list is the master.
2570 if (!user->clist_synched)
2571 return;
2573 /* Don't re-add a buddy that is already on our contact list */
2574 if (nm_find_user_record(user, purple_buddy_get_name(buddy)) != NULL)
2575 return;
2577 contact = nm_create_contact();
2578 nm_contact_set_dn(contact, purple_buddy_get_name(buddy));
2580 /* Remove the PurpleBuddy (we will add it back after adding it
2581 * to the server side list). Save the alias if there is one.
2583 alias = purple_buddy_get_alias(buddy);
2584 bname = purple_buddy_get_name(buddy);
2585 if (alias && !purple_strequal(alias, bname))
2586 nm_contact_set_display_name(contact, alias);
2588 purple_blist_remove_buddy(buddy);
2589 buddy = NULL;
2591 gname = purple_group_get_name(group);
2592 if (purple_strequal(gname, NM_ROOT_FOLDER_NAME)) {
2593 gname = "";
2596 folder = nm_find_folder(user, gname);
2597 if (folder) {
2599 /* We have everything that we need, so send the createcontact */
2600 rc = nm_send_create_contact(user, folder, contact,
2601 _create_contact_resp_cb, contact);
2603 } else {
2605 /* Need to create the folder before we can add the contact */
2606 rc = nm_send_create_folder(user, gname,
2607 _create_folder_resp_add_contact, contact);
2610 _check_for_disconnect(user, rc);
2614 static void
2615 novell_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
2617 NMContact *contact;
2618 NMFolder *folder;
2619 NMUser *user;
2620 const char *dn, *gname;
2621 NMERR_T rc = NM_OK;
2623 if (gc == NULL || buddy == NULL || group == NULL)
2624 return;
2626 user = purple_connection_get_protocol_data(gc);
2627 if (user && (dn = nm_lookup_dn(user, purple_buddy_get_name(buddy)))) {
2628 gname = purple_group_get_name(group);
2629 if (purple_strequal(gname, NM_ROOT_FOLDER_NAME)) {
2630 gname = "";
2632 folder = nm_find_folder(user, gname);
2633 if (folder) {
2634 contact = nm_folder_find_contact(folder, dn);
2635 if (contact) {
2637 /* Remove the buddy from the contact */
2638 nm_contact_set_data(contact, NULL);
2640 /* Tell the server to remove the contact */
2641 rc = nm_send_remove_contact(user, folder, contact,
2642 _remove_contact_resp_cb, NULL);
2643 _check_for_disconnect(user, rc);
2649 static void
2650 novell_remove_group(PurpleConnection * gc, PurpleGroup *group)
2652 NMUser *user;
2653 NMERR_T rc = NM_OK;
2655 if (gc == NULL || group == NULL)
2656 return;
2658 user = purple_connection_get_protocol_data(gc);
2659 if (user) {
2660 NMFolder *folder = nm_find_folder(user, purple_group_get_name(group));
2662 if (folder) {
2663 rc = nm_send_remove_folder(user, folder,
2664 _remove_folder_resp_cb, NULL);
2665 _check_for_disconnect(user, rc);
2670 static void
2671 novell_alias_buddy(PurpleConnection * gc, const char *name, const char *alias)
2673 NMContact *contact;
2674 NMUser *user;
2675 GList *contacts = NULL;
2676 GList *cnode = NULL;
2677 const char *dn = NULL, *fname = NULL;
2678 NMERR_T rc = NM_OK;
2680 if (gc == NULL || name == NULL || alias == NULL)
2681 return;
2683 user = purple_connection_get_protocol_data(gc);
2684 if (user && (dn = nm_lookup_dn(user, name))) {
2686 /* Alias all of instances of the contact */
2687 contacts = nm_find_contacts(user, dn);
2688 for (cnode = contacts; cnode != NULL; cnode = cnode->next) {
2689 contact = (NMContact *) cnode->data;
2690 if (contact) {
2691 PurpleGroup *group = NULL;
2692 PurpleBuddy *buddy;
2693 NMFolder *folder;
2695 /* Alias the Purple buddy? */
2696 folder = nm_find_folder_by_id(user,
2697 nm_contact_get_parent_id(contact));
2698 if (folder) {
2699 fname = nm_folder_get_name(folder);
2700 if (*fname == '\0') {
2701 fname = NM_ROOT_FOLDER_NAME;
2703 group = purple_blist_find_group(fname);
2706 if (group) {
2707 const char *balias;
2708 buddy = purple_blist_find_buddy_in_group(user->client_data,
2709 name, group);
2710 balias = buddy ? purple_buddy_get_local_alias(buddy) : NULL;
2711 if (balias && !purple_strequal(balias, alias))
2712 purple_buddy_set_local_alias(buddy, alias);
2715 /* Tell the server to alias the contact */
2716 rc = nm_send_rename_contact(user, contact, alias,
2717 _rename_contact_resp_cb, NULL);
2718 _check_for_disconnect(user, rc);
2721 if (contacts)
2722 g_list_free(contacts);
2726 static void
2727 novell_group_buddy(PurpleConnection * gc,
2728 const char *name, const char *old_group_name,
2729 const char *new_group_name)
2731 NMFolder *old_folder;
2732 NMFolder *new_folder;
2733 NMContact *contact;
2734 NMUser *user;
2735 const char *dn;
2736 NMERR_T rc = NM_OK;
2738 if (gc == NULL || name == NULL ||
2739 old_group_name == NULL || new_group_name == NULL)
2740 return;
2742 user = purple_connection_get_protocol_data(gc);
2743 if (user && (dn = nm_lookup_dn(user, name))) {
2745 /* Find the old folder */
2746 if (purple_strequal(old_group_name, NM_ROOT_FOLDER_NAME)) {
2747 old_folder = nm_get_root_folder(user);
2748 if (nm_folder_find_contact(old_folder, dn) == NULL)
2749 old_folder = nm_find_folder(user, old_group_name);
2750 } else {
2751 old_folder = nm_find_folder(user, old_group_name);
2754 if (old_folder && (contact = nm_folder_find_contact(old_folder, dn))) {
2756 /* Find the new folder */
2757 new_folder = nm_find_folder(user, new_group_name);
2758 if (new_folder == NULL) {
2759 if (purple_strequal(new_group_name, NM_ROOT_FOLDER_NAME))
2760 new_folder = nm_get_root_folder(user);
2763 if (new_folder) {
2765 /* Tell the server to move the contact to the new folder */
2766 rc = nm_send_move_contact(user, contact, new_folder,
2767 _move_contact_resp_cb, NULL);
2769 } else {
2771 nm_contact_add_ref(contact);
2773 /* Remove the old contact first */
2774 nm_send_remove_contact(user, old_folder, contact,
2775 _remove_contact_resp_cb, NULL);
2777 /* New folder does not exist yet, so create it */
2778 rc = nm_send_create_folder(user, new_group_name,
2779 _create_folder_resp_move_contact,
2780 contact);
2783 _check_for_disconnect(user, rc);
2788 static void
2789 novell_rename_group(PurpleConnection * gc, const char *old_name,
2790 PurpleGroup *group, GList *moved_buddies)
2792 NMERR_T rc = NM_OK;
2793 NMFolder *folder;
2794 NMUser *user;
2796 if (gc == NULL || old_name == NULL || group == NULL || moved_buddies == NULL) {
2797 return;
2800 user = purple_connection_get_protocol_data(gc);
2801 if (user) {
2802 const char *gname = purple_group_get_name(group);
2803 /* Does new folder exist already? */
2804 if (nm_find_folder(user, gname)) {
2805 /* purple_group_set_name() adds the buddies
2806 * to the new group and removes the old group...
2807 * so there is nothing more to do here.
2809 return;
2812 if (purple_strequal(old_name, NM_ROOT_FOLDER_NAME)) {
2813 /* Can't rename the root folder ... need to revisit this */
2814 return;
2817 folder = nm_find_folder(user, old_name);
2818 if (folder) {
2819 rc = nm_send_rename_folder(user, folder, gname,
2820 _rename_folder_resp_cb, NULL);
2821 _check_for_disconnect(user, rc);
2826 static const char *
2827 novell_list_icon(PurpleAccount * account, PurpleBuddy * buddy)
2829 return "novell";
2832 static void
2833 novell_tooltip_text(PurpleBuddy * buddy, PurpleNotifyUserInfo * user_info, gboolean full)
2835 NMUserRecord *user_record = NULL;
2836 PurpleConnection *gc;
2837 NMUser *user;
2838 int status = 0;
2839 const char *status_str = NULL;
2840 const char *text = NULL;
2842 if (buddy == NULL)
2843 return;
2845 gc = purple_account_get_connection(purple_buddy_get_account(buddy));
2846 if (gc == NULL || (user = purple_connection_get_protocol_data(gc)) == NULL)
2847 return;
2849 if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
2850 user_record = nm_find_user_record(user, purple_buddy_get_name(buddy));
2851 if (user_record) {
2852 status = nm_user_record_get_status(user_record);
2853 text = nm_user_record_get_status_text(user_record);
2854 /* No custom text, so default it ... */
2855 switch (status) {
2856 case NM_STATUS_AVAILABLE:
2857 status_str = _("Available");
2858 break;
2859 case NM_STATUS_AWAY:
2860 status_str = _("Away");
2861 break;
2862 case NM_STATUS_BUSY:
2863 status_str = _("Busy");
2864 break;
2865 case NM_STATUS_AWAY_IDLE:
2866 status_str = _("Idle");
2867 break;
2868 case NM_STATUS_OFFLINE:
2869 status_str = _("Offline");
2870 break;
2871 default:
2872 status_str = _("Unknown");
2873 break;
2876 purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), status_str);
2878 if (text) {
2879 /* TODO: Check whether it's correct to call add_pair_html,
2880 or if we should be using add_pair_plaintext */
2881 purple_notify_user_info_add_pair_html(user_info, _("Message"), text);
2887 static void
2888 novell_set_idle(PurpleConnection * gc, int time)
2890 NMUser *user;
2891 NMERR_T rc = NM_OK;
2892 const char *id = NULL;
2893 PurpleStatus *status = NULL;
2895 if (gc == NULL)
2896 return;
2898 user = purple_connection_get_protocol_data(gc);
2899 if (user == NULL)
2900 return;
2902 status = purple_account_get_active_status(purple_connection_get_account(gc));
2903 id = purple_status_get_id(status);
2905 /* Only go idle if active status is available */
2906 if (purple_strequal(id, NOVELL_STATUS_TYPE_AVAILABLE)) {
2907 if (time > 0) {
2908 rc = nm_send_set_status(user, NM_STATUS_AWAY_IDLE, NULL, NULL, NULL, NULL);
2909 } else {
2910 rc = nm_send_set_status(user, NM_STATUS_AVAILABLE, NULL, NULL, NULL, NULL);
2914 _check_for_disconnect(user, rc);
2917 static void
2918 novell_get_info(PurpleConnection * gc, const char *name)
2920 NMUserRecord *user_record;
2921 NMUser *user;
2922 NMERR_T rc;
2924 if (gc == NULL || name == NULL)
2925 return;
2927 user = purple_connection_get_protocol_data(gc);
2928 if (user) {
2930 user_record = nm_find_user_record(user, name);
2931 if (user_record) {
2932 _show_info(gc, user_record, g_strdup(name));
2934 } else {
2935 rc = nm_send_get_details(user, name,
2936 _get_details_resp_show_info, g_strdup(name));
2938 _check_for_disconnect(user, rc);
2945 static char *
2946 novell_status_text(PurpleBuddy * buddy)
2948 const char *text = NULL;
2949 const char *dn = NULL;
2950 PurpleAccount *account;
2952 account = buddy ? purple_buddy_get_account(buddy) : NULL;
2953 if (buddy && account) {
2954 PurpleConnection *gc = purple_account_get_connection(account);
2956 if (gc) {
2957 NMUser *user = purple_connection_get_protocol_data(gc);
2959 if (user) {
2960 dn = nm_lookup_dn(user, purple_buddy_get_name(buddy));
2961 if (dn) {
2962 NMUserRecord *user_record = nm_find_user_record(user, dn);
2964 if (user_record) {
2965 text = nm_user_record_get_status_text(user_record);
2966 if (text)
2967 return g_strdup(text);
2974 return NULL;
2977 static GList *
2978 novell_status_types(PurpleAccount *account)
2980 GList *status_types = NULL;
2981 PurpleStatusType *type;
2983 g_return_val_if_fail(account != NULL, NULL);
2985 type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, NOVELL_STATUS_TYPE_AVAILABLE,
2986 NULL, TRUE, TRUE, FALSE,
2987 "message", _("Message"), purple_value_new(G_TYPE_STRING),
2988 NULL);
2989 status_types = g_list_append(status_types, type);
2991 type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, NOVELL_STATUS_TYPE_AWAY,
2992 NULL, TRUE, TRUE, FALSE,
2993 "message", _("Message"), purple_value_new(G_TYPE_STRING),
2994 NULL);
2995 status_types = g_list_append(status_types, type);
2997 type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE, NOVELL_STATUS_TYPE_BUSY,
2998 _("Busy"), TRUE, TRUE, FALSE,
2999 "message", _("Message"), purple_value_new(G_TYPE_STRING),
3000 NULL);
3001 status_types = g_list_append(status_types, type);
3003 type = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE, NOVELL_STATUS_TYPE_APPEAR_OFFLINE,
3004 NULL, TRUE, TRUE, FALSE);
3005 status_types = g_list_append(status_types, type);
3007 type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE);
3008 status_types = g_list_append(status_types, type);
3010 return status_types;
3013 static void
3014 novell_set_status(PurpleAccount *account, PurpleStatus *status)
3016 PurpleConnection *gc;
3017 gboolean connected;
3018 PurplePresence *presence;
3019 PurpleStatusType *type;
3020 PurpleStatusPrimitive primitive;
3021 NMUser *user;
3022 NMSTATUS_T novellstatus = NM_STATUS_AVAILABLE;
3023 NMERR_T rc = NM_OK;
3024 const char *msg = NULL;
3025 char *text = NULL;
3027 connected = purple_account_is_connected(account);
3028 presence = purple_status_get_presence(status);
3029 type = purple_status_get_status_type(status);
3030 primitive = purple_status_type_get_primitive(type);
3033 * We don't have any independent statuses, so we don't need to
3034 * do anything when a status is deactivated (because another
3035 * status is about to be activated).
3037 if (!purple_status_is_active(status))
3038 return;
3040 if (!connected)
3041 return;
3043 gc = purple_account_get_connection(account);
3044 user = purple_connection_get_protocol_data(gc);
3045 if (user == NULL)
3046 return;
3048 if (primitive == PURPLE_STATUS_AVAILABLE) {
3049 novellstatus = NM_STATUS_AVAILABLE;
3050 } else if (primitive == PURPLE_STATUS_AWAY) {
3051 novellstatus = NM_STATUS_AWAY;
3052 } else if (primitive == PURPLE_STATUS_UNAVAILABLE) {
3053 novellstatus = NM_STATUS_BUSY;
3054 } else if (primitive == PURPLE_STATUS_INVISIBLE) {
3055 novellstatus = NM_STATUS_OFFLINE;
3056 } else if (purple_presence_is_idle(presence)) {
3057 novellstatus = NM_STATUS_AWAY_IDLE;
3058 } else {
3059 novellstatus = NM_STATUS_AVAILABLE;
3062 if (primitive == PURPLE_STATUS_AWAY || primitive == PURPLE_STATUS_AVAILABLE ||
3063 primitive == PURPLE_STATUS_UNAVAILABLE) {
3064 msg = purple_status_get_attr_string(status, "message");
3065 text = g_strdup(msg);
3067 if (primitive == PURPLE_STATUS_AVAILABLE)
3068 msg = NULL; /* no auto replies for online status */
3070 /* Don't want newlines in status text */
3071 purple_util_chrreplace(text, '\n', ' ');
3074 rc = nm_send_set_status(user, novellstatus, text, msg, NULL, NULL);
3075 _check_for_disconnect(user, rc);
3077 g_free(text);
3080 static void
3081 novell_add_permit(PurpleConnection *gc, const char *who)
3083 NMUser *user;
3084 NMERR_T rc = NM_OK;
3085 const char *name = who;
3087 if (gc == NULL || who == NULL)
3088 return;
3090 user = purple_connection_get_protocol_data(gc);
3091 if (user == NULL)
3092 return;
3094 /* Remove first -- we will add it back in when we get
3095 * the okay from the server
3097 purple_account_privacy_permit_remove(purple_connection_get_account(gc), who, TRUE);
3099 if (nm_user_is_privacy_locked(user)) {
3100 _show_privacy_locked_error(gc, user);
3101 _sync_privacy_lists(user);
3102 return;
3105 /* Work around for problem with un-typed, dotted contexts */
3106 if (strchr(who, '.')) {
3107 const char *dn = nm_lookup_dn(user, who);
3108 if (dn == NULL) {
3109 rc = nm_send_get_details(user, who, _get_details_send_privacy_create,
3110 (gpointer)TRUE);
3111 _check_for_disconnect(user, rc);
3112 return;
3113 } else {
3114 name = dn;
3118 rc = nm_send_create_privacy_item(user, name, TRUE,
3119 _create_privacy_item_permit_resp_cb,
3120 g_strdup(who));
3121 _check_for_disconnect(user, rc);
3124 static void
3125 novell_add_deny(PurpleConnection *gc, const char *who)
3127 NMUser *user;
3128 NMERR_T rc = NM_OK;
3129 const char *name = who;
3131 if (gc == NULL || who == NULL)
3132 return;
3134 user = purple_connection_get_protocol_data(gc);
3135 if (user == NULL)
3136 return;
3138 /* Remove first -- we will add it back in when we get
3139 * the okay from the server
3141 purple_account_privacy_deny_remove(purple_connection_get_account(gc), who, TRUE);
3143 if (nm_user_is_privacy_locked(user)) {
3144 _show_privacy_locked_error(gc, user);
3145 _sync_privacy_lists(user);
3146 return;
3149 /* Work around for problem with un-typed, dotted contexts */
3150 if (strchr(who, '.')) {
3151 const char *dn = nm_lookup_dn(user, who);
3152 if (dn == NULL) {
3153 rc = nm_send_get_details(user, who, _get_details_send_privacy_create,
3154 (gpointer)FALSE);
3155 _check_for_disconnect(user, rc);
3156 return;
3157 } else {
3158 name = dn;
3162 rc = nm_send_create_privacy_item(user, name, FALSE,
3163 _create_privacy_item_deny_resp_cb,
3164 g_strdup(who));
3165 _check_for_disconnect(user, rc);
3168 static void
3169 novell_rem_permit(PurpleConnection *gc, const char *who)
3171 NMUser *user;
3172 NMERR_T rc = NM_OK;
3173 const char *dn = NULL;
3175 if (gc == NULL || who == NULL)
3176 return;
3178 user = purple_connection_get_protocol_data(gc);
3179 if (user == NULL)
3180 return;
3182 if (nm_user_is_privacy_locked(user)) {
3183 _show_privacy_locked_error(gc, user);
3184 _sync_privacy_lists(user);
3185 return;
3188 dn = nm_lookup_dn(user, who);
3189 if (dn == NULL)
3190 dn = who;
3192 rc = nm_send_remove_privacy_item(user, dn, TRUE,
3193 _remove_privacy_item_resp_cb,
3194 g_strdup(who));
3195 _check_for_disconnect(user, rc);
3198 static void
3199 novell_rem_deny(PurpleConnection *gc, const char *who)
3201 NMUser *user;
3202 NMERR_T rc = NM_OK;
3203 const char *dn = NULL;
3205 if (gc == NULL || who == NULL)
3206 return;
3208 user = purple_connection_get_protocol_data(gc);
3209 if (user == NULL)
3210 return;
3212 if (nm_user_is_privacy_locked(user)) {
3213 _show_privacy_locked_error(gc, user);
3214 _sync_privacy_lists(user);
3215 return;
3218 dn = nm_lookup_dn(user, who);
3219 if (dn == NULL)
3220 dn = who;
3222 rc = nm_send_remove_privacy_item(user, dn, FALSE,
3223 _remove_privacy_item_resp_cb,
3224 g_strdup(who));
3225 _check_for_disconnect(user, rc);
3228 static void
3229 novell_set_permit_deny(PurpleConnection *gc)
3231 NMERR_T rc = NM_OK;
3232 const char *dn, *name = NULL;
3233 NMUserRecord *user_record = NULL;
3234 GSList *node = NULL, *copy = NULL;
3235 NMUser *user;
3236 int i, j, num_contacts, num_folders;
3237 NMContact *contact;
3238 NMFolder *folder = NULL;
3239 PurpleAccount *account;
3241 if (gc == NULL)
3242 return;
3244 account = purple_connection_get_account(gc);
3246 user = purple_connection_get_protocol_data(gc);
3247 if (user == NULL)
3248 return;
3250 if (user->privacy_synched == FALSE) {
3251 _sync_privacy_lists(user);
3252 user->privacy_synched = TRUE;
3253 return;
3256 if (nm_user_is_privacy_locked(user)) {
3257 _show_privacy_locked_error(gc, user);
3258 _sync_privacy_lists(user);
3259 return;
3262 switch (purple_account_get_privacy_type(account)) {
3264 case PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL:
3265 rc = nm_send_set_privacy_default(user, FALSE,
3266 _set_privacy_default_resp_cb, NULL);
3267 _check_for_disconnect(user, rc);
3269 /* clear server side deny list */
3270 if (rc == NM_OK) {
3271 copy = g_slist_copy(user->deny_list);
3272 for (node = copy; node && node->data; node = node->next) {
3273 rc = nm_send_remove_privacy_item(user, (const char *)node->data,
3274 FALSE, NULL, NULL);
3275 if (_check_for_disconnect(user, rc))
3276 break;
3278 g_slist_free(copy);
3279 g_slist_free(user->deny_list);
3280 user->deny_list = NULL;
3282 break;
3284 case PURPLE_ACCOUNT_PRIVACY_DENY_ALL:
3285 rc = nm_send_set_privacy_default(user, TRUE,
3286 _set_privacy_default_resp_cb, NULL);
3287 _check_for_disconnect(user, rc);
3289 /* clear server side allow list */
3290 if (rc == NM_OK) {
3291 copy = g_slist_copy(user->allow_list);
3292 for (node = copy; node && node->data; node = node->next) {
3293 rc = nm_send_remove_privacy_item(user, (const char *)node->data,
3294 TRUE, NULL, NULL);
3295 if (_check_for_disconnect(user, rc))
3296 break;
3298 g_slist_free(copy);
3299 g_slist_free(user->allow_list);
3300 user->allow_list = NULL;
3302 break;
3304 case PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS:
3306 rc = nm_send_set_privacy_default(user, TRUE,
3307 _set_privacy_default_resp_cb, NULL);
3308 _check_for_disconnect(user, rc);
3310 /* sync allow lists */
3311 if (rc == NM_OK) {
3313 for (node = user->allow_list; node; node = node->next) {
3314 user_record = nm_find_user_record(user, (char *)node->data);
3315 if (user_record) {
3316 name = nm_user_record_get_display_id(user_record);
3318 if (!g_slist_find_custom(purple_account_privacy_get_permitted(account),
3319 name, (GCompareFunc)purple_utf8_strcasecmp)) {
3320 purple_account_privacy_permit_add(account, name , TRUE);
3325 for (node = purple_account_privacy_get_permitted(account); node; node = node->next) {
3326 dn = nm_lookup_dn(user, (char *)node->data);
3327 if (dn) {
3329 if (!g_slist_find_custom(user->allow_list,
3330 dn, (GCompareFunc)purple_utf8_strcasecmp)) {
3331 rc = nm_send_create_privacy_item(user, dn, TRUE,
3332 _create_privacy_item_deny_resp_cb,
3333 g_strdup(dn));
3334 _check_for_disconnect(user, rc);
3336 } else {
3337 purple_account_privacy_permit_remove(account, (char *)node->data, TRUE);
3341 break;
3343 case PURPLE_ACCOUNT_PRIVACY_DENY_USERS:
3345 /* set to default allow */
3346 rc = nm_send_set_privacy_default(user, FALSE,
3347 _set_privacy_default_resp_cb, NULL);
3348 _check_for_disconnect(user, rc);
3350 /* sync deny lists */
3351 if (rc == NM_OK) {
3353 for (node = user->deny_list; node; node = node->next) {
3354 user_record = nm_find_user_record(user, (char *)node->data);
3355 if (user_record) {
3356 name = nm_user_record_get_display_id(user_record);
3358 if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
3359 name, (GCompareFunc)purple_utf8_strcasecmp)) {
3360 purple_account_privacy_deny_add(account, name , TRUE);
3365 for (node = purple_account_privacy_get_denied(account); node; node = node->next) {
3367 name = NULL;
3368 dn = nm_lookup_dn(user, (char *)node->data);
3369 if (dn) {
3370 user_record = nm_find_user_record(user, dn);
3371 name = nm_user_record_get_display_id(user_record);
3373 if (!g_slist_find_custom(user->deny_list,
3374 dn, (GCompareFunc)purple_utf8_strcasecmp)) {
3375 rc = nm_send_create_privacy_item(user, dn, FALSE,
3376 _create_privacy_item_deny_resp_cb,
3377 g_strdup(name));
3378 _check_for_disconnect(user, rc);
3380 } else {
3381 purple_account_privacy_deny_remove(account, (char *)node->data, TRUE);
3386 break;
3388 case PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST:
3390 /* remove users from allow list that are not in buddy list */
3391 copy = g_slist_copy(user->allow_list);
3392 for (node = copy; node && node->data; node = node->next) {
3393 if (!nm_find_contacts(user, node->data)) {
3394 rc = nm_send_remove_privacy_item(user, (const char *)node->data,
3395 TRUE, NULL, NULL);
3396 if (_check_for_disconnect(user, rc))
3397 return;
3400 g_slist_free(copy);
3402 /* add all buddies to allow list */
3403 num_contacts = nm_folder_get_contact_count(user->root_folder);
3404 for (i = 0; i < num_contacts; i++) {
3405 contact = nm_folder_get_contact(user->root_folder, i);
3406 dn = nm_contact_get_dn(contact);
3407 if (dn && !g_slist_find_custom(user->allow_list,
3408 dn, (GCompareFunc)purple_utf8_strcasecmp))
3410 rc = nm_send_create_privacy_item(user, dn, TRUE,
3411 _create_privacy_item_deny_resp_cb,
3412 g_strdup(dn));
3413 if (_check_for_disconnect(user, rc))
3414 return;
3419 num_folders = nm_folder_get_subfolder_count(user->root_folder);
3420 for (i = 0; i < num_folders; i++) {
3421 folder = nm_folder_get_subfolder(user->root_folder, i);
3422 num_contacts = nm_folder_get_contact_count(folder);
3423 for (j = 0; j < num_contacts; j++) {
3424 contact = nm_folder_get_contact(folder, j);
3425 dn = nm_contact_get_dn(contact);
3426 if (dn && !g_slist_find_custom(user->allow_list,
3427 dn, (GCompareFunc)purple_utf8_strcasecmp))
3429 rc = nm_send_create_privacy_item(user, dn, TRUE,
3430 _create_privacy_item_deny_resp_cb,
3431 g_strdup(dn));
3432 if (_check_for_disconnect(user, rc))
3433 return;
3438 /* set to default deny */
3439 rc = nm_send_set_privacy_default(user, TRUE,
3440 _set_privacy_default_resp_cb, NULL);
3441 if (_check_for_disconnect(user, rc))
3442 break;
3444 break;
3448 static GList *
3449 novell_blist_node_menu(PurpleBlistNode *node)
3451 GList *list = NULL;
3452 PurpleActionMenu *act;
3454 if(PURPLE_IS_BUDDY(node)) {
3455 act = purple_action_menu_new(_("Initiate _Chat"),
3456 PURPLE_CALLBACK(_initiate_conference_cb),
3457 NULL, NULL);
3458 list = g_list_append(list, act);
3461 return list;
3464 static void
3465 novell_keepalive(PurpleConnection *gc)
3467 NMUser *user;
3468 NMERR_T rc = NM_OK;
3470 if (gc == NULL)
3471 return;
3473 user = purple_connection_get_protocol_data(gc);
3474 if (user == NULL)
3475 return;
3477 rc = nm_send_keepalive(user, NULL, NULL);
3478 _check_for_disconnect(user, rc);
3481 static gssize
3482 novell_get_max_message_size(PurpleConversation *conv)
3484 /* XXX: got from pidgin-otr - verify and document it */
3485 return 1792;
3488 static void
3489 novell_protocol_init(PurpleProtocol *protocol)
3491 PurpleAccountOption *option;
3493 protocol->id = "prpl-novell";
3494 protocol->name = "GroupWise";
3496 option = purple_account_option_string_new(_("Server address"), "server", NULL);
3497 protocol->account_options =
3498 g_list_append(protocol->account_options, option);
3500 option = purple_account_option_int_new(_("Server port"), "port", DEFAULT_PORT);
3501 protocol->account_options =
3502 g_list_append(protocol->account_options, option);
3505 static void
3506 novell_protocol_class_init(PurpleProtocolClass *klass)
3508 klass->login = novell_login;
3509 klass->close = novell_close;
3510 klass->status_types = novell_status_types;
3511 klass->list_icon = novell_list_icon;
3514 static void
3515 novell_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
3517 client_iface->status_text = novell_status_text;
3518 client_iface->tooltip_text = novell_tooltip_text;
3519 client_iface->blist_node_menu = novell_blist_node_menu;
3520 client_iface->convo_closed = novell_convo_closed;
3521 client_iface->normalize = purple_normalize_nocase;
3522 client_iface->get_max_message_size = novell_get_max_message_size;
3525 static void
3526 novell_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
3528 server_iface->get_info = novell_get_info;
3529 server_iface->set_status = novell_set_status;
3530 server_iface->set_idle = novell_set_idle;
3531 server_iface->add_buddy = novell_add_buddy;
3532 server_iface->remove_buddy = novell_remove_buddy;
3533 server_iface->keepalive = novell_keepalive;
3534 server_iface->alias_buddy = novell_alias_buddy;
3535 server_iface->group_buddy = novell_group_buddy;
3536 server_iface->rename_group = novell_rename_group;
3537 server_iface->remove_group = novell_remove_group;
3540 static void
3541 novell_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
3543 im_iface->send = novell_send_im;
3544 im_iface->send_typing = novell_send_typing;
3547 static void
3548 novell_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface)
3550 chat_iface->invite = novell_chat_invite;
3551 chat_iface->leave = novell_chat_leave;
3552 chat_iface->send = novell_chat_send;
3555 static void
3556 novell_protocol_privacy_iface_init(PurpleProtocolPrivacyInterface *privacy_iface)
3558 privacy_iface->add_permit = novell_add_permit;
3559 privacy_iface->add_deny = novell_add_deny;
3560 privacy_iface->rem_permit = novell_rem_permit;
3561 privacy_iface->rem_deny = novell_rem_deny;
3562 privacy_iface->set_permit_deny = novell_set_permit_deny;
3565 PURPLE_DEFINE_TYPE_EXTENDED(
3566 NovellProtocol, novell_protocol, PURPLE_TYPE_PROTOCOL, 0,
3568 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT,
3569 novell_protocol_client_iface_init)
3571 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER,
3572 novell_protocol_server_iface_init)
3574 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM,
3575 novell_protocol_im_iface_init)
3577 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT,
3578 novell_protocol_chat_iface_init)
3580 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY,
3581 novell_protocol_privacy_iface_init)
3584 static PurplePluginInfo *
3585 plugin_query(GError **error)
3587 return purple_plugin_info_new(
3588 "id", "prpl-novell",
3589 "name", "Novell GroupWise Protocol",
3590 "version", DISPLAY_VERSION,
3591 "category", N_("Protocol"),
3592 "summary", N_("Novell GroupWise Messenger Protocol Plugin"),
3593 "description", N_("Novell GroupWise Messenger Protocol Plugin"),
3594 "website", PURPLE_WEBSITE,
3595 "abi-version", PURPLE_ABI_VERSION,
3596 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL |
3597 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD,
3598 NULL
3602 static gboolean
3603 plugin_load(PurplePlugin *plugin, GError **error)
3605 novell_protocol_register_type(plugin);
3607 my_protocol = purple_protocols_add(NOVELL_TYPE_PROTOCOL, error);
3608 if (!my_protocol)
3609 return FALSE;
3611 return TRUE;
3614 static gboolean
3615 plugin_unload(PurplePlugin *plugin, GError **error)
3617 if (!purple_protocols_remove(my_protocol, error))
3618 return FALSE;
3620 return TRUE;
3623 PURPLE_PLUGIN_INIT(novell, plugin_query, plugin_load, plugin_unload);