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