PageRenderTime 119ms CodeModel.GetById 10ms app.highlight 90ms RepoModel.GetById 1ms app.codeStats 1ms

/cl.c

https://code.google.com/
C | 1348 lines | 1203 code | 85 blank | 60 comment | 222 complexity | 5518c38f7917712dad7a4cb89c505f4c MD5 | raw file
   1/*  This file is part of mrim-prpl.
   2 *
   3 *  mrim-prpl is free software: you can redistribute it and/or modify
   4 *  it under the terms of the GNU General Public License as published by
   5 *  the Free Software Foundation, either version 2 of the License, or
   6 *  (at your option) any later version.
   7 *
   8 *  mrim-prpl is distributed in the hope that it will be useful,
   9 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 *  GNU General Public License for more details.
  12 *
  13 *  You should have received a copy of the GNU General Public License
  14 *  along with mrim-prpl.  If not, see <http://www.gnu.org/licenses/>.
  15 */
  16
  17#include "mrim.h"
  18#include "package.h"
  19#include "statuses.h"
  20#include "cl.h"
  21#include "util.h"
  22#include "message.h"
  23
  24/* Contact list */
  25
  26void mrim_cl_skip(MrimPackage *pack, gchar *mask) {
  27	while (*mask) {
  28		switch (*mask) {
  29			case 's':
  30				g_free(mrim_package_read_LPSA(pack));
  31				break;
  32			case 'z':
  33			case 'u':
  34				mrim_package_read_UL(pack);
  35				break;
  36		}
  37		mask++;
  38	}
  39}
  40
  41static MrimBuddy *mrim_cl_load_buddy(MrimData *mrim, MrimPackage *pack, gchar *mask) {
  42	MrimBuddy *mb	= g_new0(MrimBuddy, 1);
  43	mb->mrim		= mrim;
  44	mb->flags		= mrim_package_read_UL(pack);
  45	mb->group_id		= mrim_package_read_UL(pack);
  46	mb->email		= mrim_package_read_LPSA(pack);
  47	mb->alias		= mrim_package_read_LPSW(pack);
  48	mb->s_flags		= mrim_package_read_UL(pack);
  49	mb->phones		= g_new0(gchar*, 4);
  50	{
  51		guint32 status_id = mrim_package_read_UL(pack);
  52		{
  53			gchar *phones = mrim_package_read_LPSA(pack);
  54			if (phones) {
  55				gchar **phones_splitted = g_strsplit(phones, ",", 3);
  56				guint i = 0;
  57				while (phones_splitted[i]) {
  58					gchar *phone;
  59					if ((!phones_splitted[i][0]) || (phones_splitted[i][0] == '+')) {
  60						phone = g_strdup(phones_splitted[i]);
  61					} else {
  62						phone = g_strdup_printf("+%s", phones_splitted[i]);
  63					}
  64					mb->phones[i] = phone;
  65					i++;
  66				}
  67				g_strfreev(phones_splitted);
  68			}
  69		}
  70		{
  71			gchar *status_uri = mrim_package_read_LPSA(pack);
  72			gchar *tmp = mrim_package_read_LPSW(pack);
  73			gchar *status_title = purple_markup_escape_text(tmp, -1);
  74			g_free(tmp);
  75			tmp = mrim_package_read_LPSW(pack);
  76			gchar *status_desc = purple_markup_escape_text(tmp, -1);
  77			g_free(tmp);
  78			mb->status = make_mrim_status(status_id, status_uri, status_title, status_desc);
  79		}
  80	}
  81	mb->com_support = mrim_package_read_UL(pack);
  82	mb->user_agent = mrim_package_read_LPSA(pack);
  83	mrim_package_read_UL(pack);
  84	mrim_package_read_UL(pack);
  85	mrim_package_read_UL(pack);
  86	{
  87		gchar *tmp = mrim_package_read_LPSW(pack);
  88		mb->microblog = purple_markup_escape_text(tmp, -1);
  89		g_free(tmp);
  90	}
  91	mrim_cl_skip(pack, mask + 16);
  92	mb->authorized = !(mb->s_flags & CONTACT_INTFLAG_NOT_AUTHORIZED);
  93	return mb;
  94}
  95
  96void mrim_avatar_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message) {
  97	purple_debug_info("mrim-prpl", "[%s]\n", __func__);
  98	PurpleBuddy* buddy = user_data;
  99	if(url_text && len) {
 100		purple_buddy_icons_set_for_user(purple_buddy_get_account(buddy), purple_buddy_get_name(buddy), g_memdup(url_text, len), len, NULL);
 101	}
 102}
 103
 104void mrim_fetch_avatar(PurpleBuddy *buddy) {
 105	g_return_if_fail(buddy != NULL);
 106	g_return_if_fail(buddy->name != NULL);
 107	g_return_if_fail(is_myworld_able(buddy->name) == TRUE);
 108	purple_debug_info("mrim-prpl", "[%s] Fetch avatar for buddy '%s'\n", __func__, buddy->name);
 109	//if (!is_valid_email(buddy->name)) return;
 110	if ((!buddy->icon) && buddy->name) {
 111		gchar** split_1 = g_strsplit(buddy->name,"@",2);
 112		gchar* email_name=split_1[0];
 113		gchar* domain;
 114		gchar** split_2;
 115		if (split_1[1]) {
 116			split_2 = g_strsplit(split_1[1],".ru\0",2);
 117			domain = split_2[0];
 118			if (g_strcmp0(domain, "corp.mail") == 0) {
 119				domain = g_strdup("corp");
 120			}
 121		} else {
 122			g_strfreev(split_1);
 123			return;
 124		}
 125		gchar* url = g_strdup_printf("http://obraz.foto.mail.ru/%s/%s/_mrimavatar", domain, email_name);
 126		g_strfreev(split_2);
 127		g_strfreev(split_1);
 128		purple_util_fetch_url(url, TRUE, NULL, TRUE, mrim_avatar_cb, buddy);
 129		g_free(url);
 130	}
 131}
 132
 133void mrim_cl_load(MrimPackage *pack, MrimData *mrim) {
 134	guint32 group_count = mrim_package_read_UL(pack);
 135	gchar *group_mask = mrim_package_read_LPSA(pack);
 136	gchar *buddy_mask = mrim_package_read_LPSA(pack);
 137	/* GROUPS */
 138	purple_debug_info("mrim-prpl", "[%s] Group count = %i, group mask = '%s', contact mask = '%s'\n", __func__, group_count, group_mask, buddy_mask);
 139	{
 140		guint32 i;
 141		for (i = 0; i < group_count; i++) {
 142			guint32 flags = mrim_package_read_UL(pack);
 143			gchar *name = mrim_package_read_LPSW(pack);
 144			purple_debug_info("mrim-prpl", "[%s] New group: name = '%s', flags = 0x%x\n",  __func__, name, flags);
 145			new_mrim_group(mrim, i, name, flags);
 146			mrim_cl_skip(pack, group_mask + 2);
 147			g_free(name);
 148		}
 149	}
 150	g_free(group_mask);
 151	{
 152		guint32 id = 20;
 153		while (pack->cur < pack->data_size) {
 154			MrimBuddy *mb = mrim_cl_load_buddy(mrim, pack, buddy_mask);
 155			if (!mb)
 156				break;
 157
 158			mb->id = id++;
 159
 160			if (mb->flags & CONTACT_FLAG_REMOVED) {
 161				purple_debug_info("mrim-prpl", "[%s] Buddy '%s' removed\n", __func__, mb->email);
 162				free_mrim_buddy(mb);
 163				continue;
 164			}
 165			/* CHATS */
 166			if (mb->flags & CONTACT_FLAG_MULTICHAT) {
 167				PurpleGroup *group = get_mrim_group(mrim, mb->group_id)->group;
 168				PurpleChat *pc = NULL;
 169				PurpleChat *old_pc = purple_blist_find_chat(mrim->account, mb->email);
 170				if (old_pc) {
 171					pc = old_pc;
 172					purple_debug_info("mrim-prpl", "[%s] update chat: %s \n", __func__, mb->email);
 173				} else {
 174					purple_debug_info("mrim-prpl", "[%s] New chat: %s \n", __func__, mb->email);
 175					GHashTable *defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
 176					g_hash_table_insert(defaults, "room", g_strdup(mb->email));
 177					pc = purple_chat_new(mrim->account, mb->email, defaults);
 178
 179					purple_blist_add_chat(pc, group, NULL);
 180				}
 181				purple_blist_alias_chat(pc, mb->alias);
 182			} else {
 183				/* BUDDIES */
 184				purple_debug_info("mrim-prpl", "[%s] New buddy: email = '%s', nick = '%s', flags = 0x%x, status = '%s', UA = '%s', microblog = '%s'\n",
 185						__func__, mb->email, mb->alias, mb->flags, mb->status->purple_id, mb->user_agent, mb->microblog);
 186				PurpleGroup *group = get_mrim_group(mrim, mb->group_id)->group;
 187				PurpleBuddy *buddy = purple_find_buddy(mrim->account, mb->email);
 188				if (buddy) {
 189					purple_blist_alias_buddy(buddy, mb->alias);
 190				} else {
 191					buddy = purple_buddy_new(mrim->account, mb->email, mb->alias);
 192					purple_blist_add_buddy(buddy, NULL, group, NULL);
 193				}
 194				purple_buddy_set_protocol_data(buddy, mb);
 195				mb->buddy = buddy;
 196				update_buddy_status(buddy);
 197				if (purple_account_get_bool(mrim->gc->account, "fetch_avatars", TRUE)) {
 198					if (!(mb->flags & CONTACT_FLAG_PHONE)) {
 199						mrim_fetch_avatar(buddy);
 200					}
 201				}
 202			}
 203		} /* while */
 204	}
 205	g_free(buddy_mask);
 206	/* Purge all obsolete buddies. */
 207	{
 208		GSList *buddies = purple_find_buddies(mrim->gc->account, NULL);
 209		GSList *first = buddies;
 210		while (buddies) {
 211			PurpleBuddy *buddy = (PurpleBuddy*)buddies->data;
 212			if (buddy) {
 213				if (!(buddy->proto_data)) {
 214					purple_blist_remove_buddy(buddy);
 215				}
 216			}
 217			buddies = g_slist_next(buddies);
 218		}
 219		g_slist_free(first);
 220	}
 221	/* TODO: Purge all obsolete chats. */
 222	purple_blist_show();
 223}
 224
 225/* Groups */
 226
 227void mrim_modify_group_ack(MrimData *mrim, gpointer user_data, MrimPackage *pack) {
 228	guint32 status = mrim_package_read_UL(pack);
 229	purple_debug_info("mrim-prpl", "[%s] Status is %i\n", __func__, status);
 230	g_return_if_fail(status == CONTACT_OPER_SUCCESS);
 231}
 232
 233void mrim_rename_group(PurpleConnection *gc, const char *old_name, PurpleGroup *group, GList *moved_buddies) {
 234	MrimData *mrim = gc->proto_data;
 235	g_return_if_fail(mrim != NULL);
 236	MrimGroup *gr = get_mrim_group_by_name(mrim, group->name);
 237	g_free(gr->name);
 238	gr->name = g_strdup(group->name);
 239	MrimPackage *pack = mrim_package_new(mrim->seq++, MRIM_CS_MODIFY_CONTACT);
 240	mrim_package_add_UL(pack, gr->id);
 241	mrim_package_add_UL(pack, gr->flags);
 242	mrim_package_add_UL(pack, 0);
 243	mrim_package_add_LPSA(pack, NULL);
 244	mrim_package_add_LPSW(pack, gr->name);
 245	mrim_package_add_LPSA(pack, NULL);
 246	mrim_add_ack_cb(mrim, pack->header->seq, mrim_modify_group_ack, NULL);
 247	mrim_package_send(pack, mrim);
 248}
 249
 250void mrim_remove_group(PurpleConnection *gc, PurpleGroup *group) {
 251	MrimData *mrim = gc->proto_data;
 252	g_return_if_fail(mrim != NULL);
 253	MrimGroup *gr = get_mrim_group_by_name(mrim, group->name);
 254	g_return_if_fail(gr != NULL);
 255	MrimPackage *pack = mrim_package_new(mrim->seq++, MRIM_CS_MODIFY_CONTACT);
 256	mrim_package_add_UL(pack, gr->id);
 257	mrim_package_add_UL(pack, gr->flags & CONTACT_FLAG_REMOVED);
 258	mrim_package_add_UL(pack, 0);
 259	mrim_package_add_LPSA(pack, NULL);
 260	mrim_package_add_LPSW(pack, gr->name);
 261	mrim_package_add_LPSA(pack, NULL);
 262	mrim_add_ack_cb(mrim, pack->header->seq, mrim_modify_group_ack, NULL);
 263	mrim_package_send(pack, mrim);
 264}
 265
 266MrimGroup *new_mrim_group(MrimData *mrim, guint32 id, gchar *name, guint32 flags) {
 267	MrimGroup *group = g_new0(MrimGroup, 1);
 268	group->id = id;
 269	group->name = g_strdup(name);
 270	group->flags = flags;
 271	group->group = purple_find_group(name);
 272	if (!group->group) {
 273		group->group = purple_group_new(name);
 274		purple_blist_add_group(group->group, NULL);
 275	}
 276	g_hash_table_insert(mrim->groups, GUINT_TO_POINTER(id), group);
 277	return group;
 278}
 279
 280void free_mrim_group(MrimGroup *group) {
 281	if (group) {
 282		if (group->name) {
 283			g_free(group->name);
 284		}
 285		g_free(group);
 286	}
 287}
 288
 289MrimGroup *get_mrim_group(MrimData *mrim, guint32 id) {
 290	MrimGroup *group =  g_hash_table_lookup(mrim->groups, GUINT_TO_POINTER(id));
 291	g_return_val_if_fail(group != NULL, g_hash_table_lookup(mrim->groups, GUINT_TO_POINTER(0)));
 292	return group;
 293}
 294
 295MrimGroup *get_mrim_group_by_name(MrimData *mrim, gchar *name) {
 296	if (!name) {
 297		purple_debug_info("mrim-prpl", "[%s] name = NULL!\n", __func__);
 298		return NULL;
 299	} else {
 300		purple_debug_info("mrim-prpl", "[%s] name = '%s'\n", __func__, name);
 301	};
 302	GList *g = g_list_first(g_hash_table_get_values(mrim->groups));
 303	MrimGroup *group;
 304	while (g) {
 305		group = g->data;
 306		if (!group) {
 307			purple_debug_info("mrim-prpl", "[%s] g->data FAIL!\n", __func__);
 308		} else if (!group->name) {
 309			purple_debug_info("mrim-prpl", "[%s] NONAME group (id, flags) = (%u,%u)\n", __func__, group->id, group->flags);
 310		} else {
 311			purple_debug_info("mrim-prpl", "[%s] group info: (id, flags) = (%u,%u)\n", __func__, group->id, group->flags);
 312			purple_debug_info("mrim-prpl", "[%s] group->name = '%s'\n", __func__, group->name);
 313			if (g_strcmp0(group->name, name) == 0) {
 314				g_list_free(g);
 315				return group;
 316			};
 317		};
 318		g = g_list_next(g);
 319	};
 320	g_list_free(g);
 321	return NULL;
 322}
 323
 324void mrim_add_group_ack(MrimData *mrim, gpointer user_data, MrimPackage *pack) {
 325	guint32 status = mrim_package_read_UL(pack);
 326	purple_debug_info("mrim-prpl", "[%s] Status = %i\n", __func__, status);
 327	g_return_if_fail(status == CONTACT_OPER_SUCCESS);
 328	guint32 id = mrim_package_read_UL(pack);
 329	AddContactInfo *info = user_data;
 330	new_mrim_group(mrim, id, info->group->name, 0);
 331	if (info->buddy) {
 332		if (info->move) {
 333			mrim_add_buddy(mrim->gc, info->buddy, info->group);
 334		} else {
 335			mrim_move_buddy(mrim->gc, info->buddy->name, NULL, info->group->name);
 336		}
 337	}
 338}
 339
 340void cl_add_group(MrimData *mrim, gchar *name, AddContactInfo *info) {
 341	purple_debug_info("mrim-prpl", "[%s] Add group with name '%s'\n", __func__, name);
 342	guint32 groups_count = g_hash_table_size(mrim->groups);
 343	MrimPackage *pack = mrim_package_new(mrim->seq++, MRIM_CS_ADD_CONTACT);
 344	mrim_package_add_UL(pack, CONTACT_FLAG_GROUP | (groups_count << 24));
 345	mrim_package_add_UL(pack, 0);
 346	mrim_package_add_LPSA(pack, NULL);
 347	mrim_package_add_LPSW(pack, name);
 348	mrim_package_add_LPSA(pack, NULL);
 349	mrim_package_add_UL(pack, 0);
 350	mrim_package_add_UL(pack, 0);
 351	if (!info) {
 352		info = g_new0(AddContactInfo, 1);
 353		info->group = purple_find_group(name);
 354	}
 355	mrim_add_ack_cb(mrim, pack->header->seq, mrim_add_group_ack, info);
 356	mrim_package_send(pack, mrim);
 357}
 358
 359/* Buddies */
 360
 361void mrim_add_contact_ack(MrimData *mrim, gpointer user_data, MrimPackage *pack) {
 362	guint32 status = mrim_package_read_UL(pack);
 363	purple_debug_info("mrim-prpl", "[%s] Status is %i\n", __func__, status);
 364	g_return_if_fail(status == CONTACT_OPER_SUCCESS);
 365	guint32 id = mrim_package_read_UL(pack);
 366	BuddyAddInfo *info = user_data;
 367	PurpleBuddy *buddy = info->buddy;
 368	MrimBuddy *mb = buddy->proto_data;
 369	mb->id = id;
 370}
 371
 372void mrim_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) {
 373	purple_debug_info("mrim-prpl", "[%s]\n", __func__);
 374	g_return_if_fail(buddy != NULL);
 375	g_return_if_fail(group != NULL);
 376	g_return_if_fail(gc != NULL);
 377	g_return_if_fail(gc->state == PURPLE_CONNECTED);
 378	purple_debug_info("mrim-prpl", "[%s] Add buddy '%s' to group '%s'\n", __func__, buddy->name, group->name);
 379	{
 380		const gchar *normalized_name = mrim_normalize(gc->account, (const gchar*)buddy->name);
 381		g_free(buddy->name);
 382		buddy->name = (gchar*)normalized_name;
 383	}
 384	PurpleBuddy *old_buddy = purple_find_buddy(gc->account, buddy->name);
 385	MrimData *mrim = gc->proto_data;
 386	MrimBuddy *mb;
 387	if (old_buddy != NULL  && old_buddy != buddy) {
 388		purple_blist_remove_buddy(buddy);
 389		buddy = old_buddy;
 390		mb = (MrimBuddy*)(buddy->proto_data);
 391		if (mb) {
 392			mb->buddy = buddy;
 393			purple_blist_alias_buddy(buddy, mb->alias);
 394			update_buddy_status(buddy);
 395		}
 396	} else if (is_valid_email(buddy->name) || is_valid_phone(buddy->name)) {
 397		purple_debug_info("mrim-prpl", "[%s] Buddy has a valid email or phone '%s'\n", __func__, buddy->name);
 398		MrimGroup *gr = get_mrim_group_by_name(mrim, group->name);
 399		gint group_id = gr ? gr->id : -1;
 400		if (group_id == -1) {
 401			purple_debug_info("mrim-prpl", "[%s] Group '%s' not exists - creating\n", __func__, group->name);
 402			AddContactInfo *info = g_new(AddContactInfo, 1);
 403			info->buddy = buddy;
 404			info->group = group;
 405			info->move = FALSE;
 406			cl_add_group(mrim, group->name, info);
 407		} else {
 408			mb = g_new0(MrimBuddy, 1);
 409			mb->email = g_strdup(buddy->name);
 410			mb->alias = g_strdup(buddy->alias ? buddy->alias : buddy->name);
 411			buddy->proto_data = mb;
 412			mb->group_id = group_id;
 413			mb->phones = g_new0(gchar*, 4);
 414			if (is_valid_phone(buddy->name)) {
 415				mb->flags |= CONTACT_FLAG_PHONE;
 416				mb->authorized = TRUE;
 417				mb->status = make_mrim_status(STATUS_ONLINE, NULL, NULL, NULL);
 418			} else {
 419				mb->authorized = FALSE;
 420				mb->status = make_mrim_status(STATUS_OFFLINE, NULL, NULL, NULL);
 421			}
 422			purple_debug_info("mrim-prpl", "[%s] Adding buddy with email = '%s' alias = '%s', flags = 0x%x\n", __func__,
 423				mb->email, mb->alias, mb->flags);
 424			MrimPackage *pack = mrim_package_new(mrim->seq++, MRIM_CS_ADD_CONTACT);
 425			mrim_package_add_UL(pack, mb->flags);
 426			mrim_package_add_UL(pack, mb->group_id);
 427			mrim_package_add_LPSA(pack, mb->email);
 428			mrim_package_add_LPSW(pack, mb->alias);
 429			{
 430				gchar *str = g_strjoinv(",", mb->phones);
 431				mrim_package_add_LPSA(pack, str);
 432				g_free(str);
 433			}
 434			mrim_package_add_LPSA(pack, " ");
 435			mrim_package_add_UL(pack, 0);
 436			{
 437				BuddyAddInfo *info = g_new(BuddyAddInfo, 1);
 438				info->buddy = buddy;
 439				mrim_add_ack_cb(mrim, pack->header->seq, mrim_add_contact_ack, info);
 440			}
 441			mrim_package_send(pack, mrim);
 442			if (!(mb->flags & CONTACT_FLAG_PHONE)) {
 443				mrim_fetch_avatar(buddy);
 444			}
 445		}
 446	} else {
 447		purple_debug_info("mrim-prpl", "[%s] '%s' is not valid email or phone number!\n", __func__, buddy->name);
 448		gchar *msg = g_strdup_printf(_("Unable to add the buddy \"%s\" because the username is invalid.  Usernames must be a valid email address(in mail.ru bk.ru list.ru corp.mail.ru inbox.ru domains), valid ICQ UIN in NNNN@uin.icq format or valid phone number (start with + and contain only numbers, spaces and \'-\'."), buddy->name);
 449		purple_notify_error(gc, NULL, _("Unable to Add"), msg);
 450		g_free(msg);
 451		purple_blist_remove_buddy(buddy);
 452	}
 453	purple_blist_show();
 454}
 455
 456void mrim_modify_buddy_ack(MrimData *mrim, gpointer user_data, MrimPackage *pack) {
 457	guint32 status = mrim_package_read_UL(pack);
 458	purple_debug_info("mrim-prpl", "[%s] Status is %i\n", __func__, status);
 459	g_return_if_fail(status == CONTACT_OPER_SUCCESS);
 460}
 461
 462void mrim_modify_buddy(MrimData *mrim, PurpleBuddy *buddy) {
 463	MrimBuddy *mb = buddy->proto_data;
 464	MrimPackage *pack = mrim_package_new(mrim->seq++, MRIM_CS_MODIFY_CONTACT);
 465	mrim_package_add_UL(pack, mb->id);
 466	mrim_package_add_UL(pack, mb->flags);
 467	mrim_package_add_UL(pack, mb->group_id);
 468	mrim_package_add_LPSA(pack, mb->flags & CONTACT_FLAG_PHONE ? "phone" : mb->email);
 469	mrim_package_add_LPSW(pack, mb->alias);
 470	{
 471		gchar *str = g_strjoinv(",", mb->phones);
 472		mrim_package_add_LPSA(pack, str);
 473		g_free(str);
 474	}
 475	mrim_add_ack_cb(mrim, pack->header->seq, mrim_modify_buddy_ack, NULL);
 476	mrim_package_send(pack, mrim);
 477}
 478
 479void mrim_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) {
 480	MrimData *mrim = gc->proto_data;
 481	MrimBuddy *mb = buddy->proto_data;
 482	purple_debug_info("mrim-prpl", "[%s] Removing buddy '%s' from buddy list\n", __func__, buddy->name);
 483	mb->flags |= CONTACT_FLAG_REMOVED;
 484	mrim_modify_buddy(mrim, buddy);
 485}
 486
 487void free_mrim_buddy(MrimBuddy *mb) {
 488	if (mb) {
 489		g_free(mb->email);
 490		g_free(mb->alias);
 491		g_strfreev(mb->phones);
 492		g_free(mb->user_agent);
 493		g_free(mb->microblog);
 494		free_mrim_status(mb->status);
 495		g_free(mb);
 496	}
 497}
 498
 499void mrim_free_buddy(PurpleBuddy *buddy) {
 500	if (buddy->proto_data) {
 501		MrimBuddy *mb = buddy->proto_data;
 502		free_mrim_buddy(mb);
 503	}
 504}
 505
 506void mrim_alias_buddy(PurpleConnection *gc, const char *who, const char *alias) {
 507	PurpleBuddy *buddy = purple_find_buddy(gc->account, (gchar*)who);
 508	g_return_if_fail(buddy != NULL);
 509	MrimData *mrim = gc->proto_data;
 510	MrimBuddy *mb = buddy->proto_data;
 511	g_return_if_fail(mb != NULL);
 512	g_free(mb->alias);
 513	mb->alias = g_strdup(alias);
 514	mrim_modify_buddy(mrim, buddy);
 515}
 516
 517void mrim_move_buddy(PurpleConnection *gc, const char *who, const char *old_group, const char *new_group) {
 518	purple_debug_info("mrim-prpl", "[%s] Moving '%s' to group '%s'\n", __func__, who, new_group);
 519	PurpleBuddy *buddy = purple_find_buddy(gc->account, (gchar*)who);
 520	g_return_if_fail(buddy != NULL);
 521	MrimData *mrim = gc->proto_data;
 522	MrimBuddy *mb = buddy->proto_data;
 523	g_return_if_fail(mb != NULL);
 524	MrimGroup *group_found = get_mrim_group_by_name(mrim, (gchar*)new_group);
 525	gint group_id = group_found ? group_found->id : -1;
 526	if (!group_found) {
 527		purple_debug_info("mrim-prpl", "[%s] Group '%s' not exists - creating\n", __func__, new_group);
 528		AddContactInfo *info = g_new(AddContactInfo, 1);
 529		info->buddy = buddy;
 530		info->group = purple_find_group((gchar*)new_group);
 531		info->move = TRUE;
 532		cl_add_group(mrim, (gchar*)new_group, info);
 533	} else {
 534		mb->group_id = group_id;
 535		mrim_modify_buddy(mrim, buddy);
 536	}
 537	g_free(group_found);
 538}
 539
 540const char *mrim_normalize(const PurpleAccount *account, const char *who) {
 541	return g_ascii_strdown((gchar*)who, -1);
 542}
 543
 544/* User actions */
 545
 546void blist_authorize_menu_item(PurpleBlistNode *node, gpointer userdata) { /* Request auth message */
 547	PurpleBuddy *buddy = (PurpleBuddy*)node;
 548	g_return_if_fail(buddy != NULL);
 549	MrimBuddy *mb = buddy->proto_data;
 550	g_return_if_fail(mb != NULL);
 551	MrimData *mrim = (MrimData*)userdata;
 552	g_return_if_fail(mrim != NULL);
 553	purple_debug_info("mrim", "[%s] Asking authorization of '%s'\n", __func__, mb->email);
 554	mrim_send_authorize(mrim, mb->email, NULL);
 555}
 556
 557#ifdef ENABLE_GTK
 558
 559void update_sms_char_counter(GObject *object, gpointer user_data) {
 560	SmsDialogParams *params = user_data;
 561	gchar *original_text, *new_text;
 562	GtkTextBuffer *buffer = gtk_text_view_get_buffer(params->message_text);
 563	{
 564		GtkTextIter start, end;
 565		gtk_text_buffer_get_start_iter(buffer, &start);
 566		gtk_text_buffer_get_end_iter(buffer, &end);
 567		original_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
 568	}
 569	if (gtk_toggle_button_get_active((GtkToggleButton*)params->translit)) {
 570		new_text = transliterate_text(original_text);
 571	} else {
 572		new_text = g_strdup(original_text);
 573	}
 574	g_free(original_text);
 575	g_free(params->sms_text);
 576	params->sms_text = new_text;
 577	gint count = g_utf8_strlen(new_text, -1);
 578	gchar *buf = g_strdup_printf(_("Symbols: %d"), count);
 579	gtk_label_set_text(params->char_counter, buf);
 580	g_free(buf);
 581}
 582
 583void sms_dialog_response(GtkDialog *dialog, gint response_id, gpointer user_data) {
 584	SmsDialogParams *params = user_data;
 585	switch (response_id) {
 586		case GTK_RESPONSE_ACCEPT:
 587			{
 588				MrimBuddy *mb = params->mb;
 589				MrimData *mrim = params->mrim;
 590				update_sms_char_counter(NULL, params); //?? ??????, ???? ????? ????????? ?????????????? ???????????? ??? ?? ??????? ?????
 591				gchar *text = params->sms_text;
 592				gint phone_index = gtk_combo_box_get_active(params->phone);
 593				if (phone_index > -1) {
 594					gchar *phone = mb->phones[phone_index];
 595					mrim_send_sms(mrim, phone, text);
 596				}
 597				break;
 598			}
 599		case GTK_RESPONSE_REJECT:
 600			break;
 601	}
 602	gtk_widget_destroy((GtkWidget*)dialog);
 603}
 604
 605void sms_dialog_destroy(GtkDialog *dialog, gpointer user_data) {
 606	SmsDialogParams *params = user_data;
 607	g_free(params->sms_text);
 608	g_free(params);
 609}
 610
 611void sms_dialog_edit_phones(GtkButton *button, gpointer user_data) {
 612	SmsDialogParams *params = user_data;
 613	blist_edit_phones_menu_item((PurpleBlistNode*)params->buddy, params->mrim);
 614	gtk_combo_box_remove_text(params->phone, 2);
 615	gtk_combo_box_remove_text(params->phone, 1);
 616	gtk_combo_box_remove_text(params->phone, 0);
 617	gtk_combo_box_append_text(params->phone, params->mb->phones[0]);
 618	gtk_combo_box_append_text(params->phone, params->mb->phones[1]);
 619	gtk_combo_box_append_text(params->phone, params->mb->phones[2]);
 620	gtk_combo_box_set_active(params->phone, 0);
 621}
 622
 623void blist_gtk_sms_menu_item(PurpleBlistNode *node, gpointer userdata) {
 624	PurpleBuddy *buddy = (PurpleBuddy *) node;
 625	MrimData *mrim = userdata;
 626	g_return_if_fail(buddy != NULL);
 627	g_return_if_fail(mrim != NULL);
 628	MrimBuddy *mb = buddy->proto_data;
 629	g_return_if_fail(mb != NULL);
 630
 631	/* ?????? */
 632	GtkWidget *dialog = gtk_dialog_new_with_buttons(_("Send SMS"), NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
 633		GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
 634	gtk_window_set_default_size((GtkWindow*)dialog, 320, 240);
 635	GtkWidget *content_area = gtk_dialog_get_content_area((GtkDialog*)dialog);
 636	GtkWidget *hbox;
 637	//gtk_container_set_border_width(content_area, 8); // ?? ??????? ?????? ?? ????????. ???? ?????-?????? ?????????? - ?????? ????????? ???????
 638	gtk_container_set_border_width((GtkContainer*)dialog, 6);
 639	gtk_box_set_spacing((GtkBox*)content_area, 6);
 640	/* ????????? */
 641	GtkWidget *buddy_name = gtk_label_new(mb->alias);
 642	gtk_box_pack_start((GtkBox*)content_area, buddy_name, FALSE, TRUE, 0);
 643	/* ??????? */
 644	hbox = gtk_hbox_new(FALSE, 6);
 645	gtk_box_pack_start((GtkBox*)content_area, hbox, FALSE, TRUE, 0);
 646	GtkWidget *phone_combo_box = gtk_combo_box_new_text();
 647	gtk_combo_box_append_text((GtkComboBox*)phone_combo_box, mb->phones[0]);
 648	gtk_combo_box_append_text((GtkComboBox*)phone_combo_box, mb->phones[1]);
 649	gtk_combo_box_append_text((GtkComboBox*)phone_combo_box, mb->phones[2]);
 650	gtk_combo_box_set_active((GtkComboBox*)phone_combo_box, 0);
 651	gtk_box_pack_start((GtkBox*)hbox, gtk_label_new(_("Phone:")), FALSE, TRUE, 0);
 652	gtk_box_pack_start((GtkBox*)hbox, phone_combo_box, TRUE, TRUE, 0);
 653	GtkWidget *edit_phones_button = gtk_button_new_from_stock(GTK_STOCK_EDIT);
 654	gtk_box_pack_end((GtkBox*)hbox, edit_phones_button, FALSE, TRUE, 0);
 655	/* ????? ????????? */
 656	GtkWidget *scrolled_wnd = gtk_scrolled_window_new(NULL, NULL);
 657	gtk_scrolled_window_set_policy((GtkScrolledWindow*)scrolled_wnd, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 658	GtkWidget *message_text = gtk_text_view_new();
 659	gtk_container_add((GtkContainer*)scrolled_wnd, message_text);
 660	gtk_box_pack_start((GtkBox*)content_area, scrolled_wnd, TRUE, TRUE, 0);
 661	gtk_text_view_set_wrap_mode((GtkTextView*)message_text, GTK_WRAP_WORD);
 662	/* ?????? ?????????????? ? ??????? ???????? */
 663	hbox = gtk_hbutton_box_new();
 664	gtk_button_box_set_spacing((GtkBox*)hbox, 6);
 665	gtk_button_box_set_layout((GtkButtonBox*)hbox, GTK_BUTTONBOX_EDGE);
 666	GtkWidget *translit = gtk_check_button_new_with_label(_("Translit"));
 667	gtk_container_add((GtkContainer*)hbox, translit);
 668	GtkWidget *char_counter = gtk_label_new("");
 669	gtk_container_add((GtkContainer*)hbox, char_counter);
 670	gtk_box_pack_end((GtkBox*)content_area, hbox, FALSE, TRUE, 0);
 671	/* ???????? ?????? ?????? ???????? */
 672	SmsDialogParams *params = g_new0(SmsDialogParams, 1);
 673	params->buddy = buddy;
 674	params->mrim = mrim;
 675	params->mb = mb;
 676	params->message_text = (GtkTextView*)message_text;
 677	params->translit = (GtkCheckButton*)translit;
 678	params->char_counter = (GtkLabel*)char_counter;
 679	params->phone = (GtkComboBox*)phone_combo_box;
 680	params->sms_text = NULL;
 681	/* ????????? ??????????? ???????? */
 682	g_signal_connect(G_OBJECT(dialog), "destroy", G_CALLBACK(sms_dialog_destroy), params);
 683	{
 684		GtkTextBuffer *buffer = gtk_text_view_get_buffer((GtkTextView*)message_text);
 685		g_signal_connect(G_OBJECT(buffer), "changed", G_CALLBACK(update_sms_char_counter), params);
 686		update_sms_char_counter(G_OBJECT(buffer), params);
 687	}
 688	g_signal_connect(G_OBJECT(translit), "toggled", G_CALLBACK(update_sms_char_counter), params);
 689	g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(sms_dialog_response), params);
 690	g_signal_connect(G_OBJECT(edit_phones_button), "clicked", G_CALLBACK(sms_dialog_edit_phones), params);
 691	/* ?????????? ?????? */
 692	gtk_widget_show_all(dialog);
 693	/* ???? ??????? ?????? ?? ???????????? ?????????????? - ???????? ??? ??????? */
 694	if (g_strcmp0("translit-table", _("translit-table")) == 0) {
 695		gtk_widget_hide(translit);
 696	}
 697	/* ?????? ???????? ????? ???? ????? ????????? */
 698	gtk_widget_grab_focus(message_text);
 699}
 700
 701#endif
 702
 703void blist_send_sms(PurpleConnection *gc, PurpleRequestFields *fields) {
 704	g_return_if_fail(gc);
 705	PurpleRequestField *RadioBoxField = purple_request_fields_get_field(fields, "combobox");
 706	int index  = RadioBoxField->u.choice.value;
 707	GList *list = RadioBoxField->u.choice.labels;
 708	while (index-- && list)
 709		list = list->next;
 710	gchar *message = (gchar*)purple_request_fields_get_string(fields, "message_box");
 711	mrim_send_sms((MrimData*)gc->proto_data, list->data, message);
 712}
 713
 714void  blist_sms_menu_item(PurpleBlistNode *node, gpointer userdata) {
 715	PurpleBuddy *buddy = (PurpleBuddy *) node;
 716	MrimData *mrim = userdata;
 717	g_return_if_fail(buddy != NULL);
 718	g_return_if_fail(mrim != NULL);
 719	MrimBuddy *mb = buddy->proto_data;
 720	g_return_if_fail(mb != NULL);
 721	PurpleRequestFields *fields;
 722	PurpleRequestFieldGroup *group;
 723	PurpleRequestField *field;
 724	fields = purple_request_fields_new();
 725	group = purple_request_field_group_new(NULL);
 726	purple_request_fields_add_group(fields, group);
 727	field = purple_request_field_choice_new("combobox", _("Choose phone number"), 0);
 728	purple_request_field_choice_add(field, mb->phones[0]);
 729	purple_request_field_choice_add(field, mb->phones[1]);
 730	purple_request_field_choice_add(field, mb->phones[2]);
 731	purple_request_field_group_add_field(group, field);
 732	field = purple_request_field_string_new("message_box",_("SMS message text"),"",TRUE);
 733	purple_request_field_group_add_field(group, field);
 734	purple_request_fields(mrim->gc, _("Send SMS"), NULL, _("SMS message should contain not\nmore than 135 symbols in latin\nor 35 in cyrillic."),
 735		fields, _("_Send"), G_CALLBACK(blist_send_sms), _("_Cancel"), NULL, mrim->account, buddy->name, NULL, mrim->gc);
 736}
 737
 738void blist_edit_phones(PurpleBuddy *buddy, PurpleRequestFields *fields) {
 739	g_return_if_fail(buddy);
 740	MrimBuddy *mb = buddy->proto_data;
 741	g_return_if_fail(mb);
 742	PurpleAccount *account = purple_buddy_get_account(buddy);
 743	PurpleConnection *gc = purple_account_get_connection(account);
 744	MrimData *mrim = purple_connection_get_protocol_data(gc);
 745	PurpleRequestFieldGroup *group = fields->groups->data;
 746	mb->phones[0] = g_strdup(purple_request_fields_get_string(fields, "phone1"));
 747	mb->phones[1] = g_strdup(purple_request_fields_get_string(fields, "phone2"));
 748	mb->phones[2] = g_strdup(purple_request_fields_get_string(fields, "phone3"));
 749	guint i = 0;
 750	while (mb->phones[i]) {
 751		if (mb->phones[i][0] && (mb->phones[i][0] != '+')) {
 752			gchar *phone = g_strdup_printf("+%s", mb->phones[i]);
 753			g_free(mb->phones[i]);
 754			mb->phones[i] = phone;
 755		}
 756		i++;
 757	}
 758	mrim_modify_buddy(mrim, buddy);
 759}
 760
 761void blist_edit_phones_menu_item(PurpleBlistNode *node, gpointer userdata) {
 762	PurpleBuddy *buddy = (PurpleBuddy*)node;
 763	MrimData *mrim = userdata;
 764	g_return_if_fail(buddy != NULL);
 765	g_return_if_fail(mrim != NULL);
 766	MrimBuddy *mb = buddy->proto_data;
 767	g_return_if_fail(mb != NULL);
 768	if (!mb->phones) { /* ??? ?? ?????? ???? */
 769		mb->phones = g_new0(char *, 4);
 770	}
 771	PurpleRequestFields *fields;
 772	PurpleRequestFieldGroup *group;
 773	PurpleRequestField *field;
 774	fields = purple_request_fields_new();
 775	group = purple_request_field_group_new(mb->email);
 776	purple_request_fields_add_group(fields, group);
 777	field = purple_request_field_string_new("phone1", _("_Main number"), mb->phones[0], FALSE);
 778	purple_request_field_group_add_field(group, field);
 779	field = purple_request_field_string_new("phone2", _("S_econd number"), mb->phones[1], FALSE);
 780	purple_request_field_group_add_field(group, field);
 781	field = purple_request_field_string_new("phone3", _("_Third number"), mb->phones[2], FALSE);
 782	purple_request_field_group_add_field(group, field);
 783	purple_request_fields(mrim->gc, _("Phone numbers"), _("Phone numbers"), _("Specify numbers as shown: +71234567890"),  fields,
 784			_("_OK"), G_CALLBACK(blist_edit_phones),
 785			_("_Cancel"), NULL,
 786			mrim->account, buddy->name, NULL, buddy);
 787}
 788
 789void mrim_url_menu_action(PurpleBlistNode *node, gpointer userdata) {
 790	PurpleBuddy *buddy = (PurpleBuddy*)node;
 791	PurpleAccount *account = purple_buddy_get_account(buddy);
 792	MrimData *mrim = account->gc->proto_data;
 793	g_return_if_fail(mrim != NULL);
 794	mrim_open_myworld_url(mrim, buddy->name, userdata);
 795}
 796
 797void blist_toggle_visible(PurpleBlistNode *node, gpointer userdata) {
 798	PurpleBuddy *buddy = (PurpleBuddy*)node;
 799	MrimData *mrim = userdata;
 800	g_return_if_fail(buddy != NULL);
 801	g_return_if_fail(mrim != NULL);
 802	MrimBuddy *mb = buddy->proto_data;
 803	g_return_if_fail(mb != NULL);
 804	mb->flags ^= CONTACT_FLAG_VISIBLE;
 805	mrim_modify_buddy(mrim, buddy);
 806}
 807
 808void blist_toggle_invisible(PurpleBlistNode *node, gpointer userdata) {
 809	PurpleBuddy *buddy = (PurpleBuddy*)node;
 810	MrimData *mrim = userdata;
 811	g_return_if_fail(buddy != NULL);
 812	g_return_if_fail(mrim != NULL);
 813	MrimBuddy *mb = buddy->proto_data;
 814	g_return_if_fail(mb != NULL);
 815	mb->flags ^= CONTACT_FLAG_INVISIBLE;
 816	mrim_modify_buddy(mrim, buddy);
 817}
 818
 819GList *mrim_user_actions(PurpleBlistNode *node) {
 820	purple_debug_info("mrim-prpl", "[%s]\n", __func__);
 821	if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) return NULL;
 822	PurpleBuddy *buddy = (PurpleBuddy*)node;
 823	MrimBuddy *mb = buddy->proto_data;
 824	MrimData *mrim = purple_buddy_get_account(buddy)->gc->proto_data;
 825	if (mb) {
 826		PurpleMenuAction *action;
 827		GList *list = NULL;//g_list_append(NULL, NULL);
 828		if (!mb->authorized) {
 829			action = purple_menu_action_new(_("Request authorization"), PURPLE_CALLBACK(blist_authorize_menu_item), mrim, NULL);
 830			list = g_list_append(list, action);
 831		}
 832		if (mb->phones && mb->phones[0]) {
 833#ifdef ENABLE_GTK
 834			if (mrim->use_gtk) {
 835				action = purple_menu_action_new(_("Send an SMS..."), PURPLE_CALLBACK(blist_gtk_sms_menu_item), mrim, NULL);
 836			} else {			
 837#endif
 838				action = purple_menu_action_new(_("Send an SMS..."), PURPLE_CALLBACK(blist_sms_menu_item), mrim, NULL);
 839#ifdef ENABLE_GTK
 840			}
 841#endif
 842			list = g_list_append(list, action);
 843		}
 844		action = purple_menu_action_new(_("Edit phone numbers..."), PURPLE_CALLBACK(blist_edit_phones_menu_item), mrim, NULL);
 845		list = g_list_append(list, action);
 846		if (is_valid_email(mb->email)) {
 847			list = g_list_append(list, NULL);
 848			action = purple_menu_action_new(_("MyWorld@Mail.ru"), PURPLE_CALLBACK(mrim_url_menu_action),
 849				"http://r.mail.ru/cln3587/my.mail.ru/%s/%s", NULL);
 850			list = g_list_append(list, action);
 851			action = purple_menu_action_new(_("Photo@Mail.ru"), PURPLE_CALLBACK(mrim_url_menu_action),
 852				"http://r.mail.ru/cln3565/foto.mail.ru/%s/%s", NULL);
 853			list = g_list_append(list, action);
 854			action = purple_menu_action_new(_("Video@Mail.ru"), PURPLE_CALLBACK(mrim_url_menu_action),
 855				"http://r.mail.ru/cln3567/video.mail.ru/%s/%s", NULL);
 856			list = g_list_append(list, action);
 857			action = purple_menu_action_new(_("Blogs@Mail.ru"), PURPLE_CALLBACK(mrim_url_menu_action),
 858				"http://r.mail.ru/cln3566/blogs.mail.ru/%s/%s", NULL);
 859			list = g_list_append(list, action);
 860			list = g_list_append(list, NULL);
 861		}
 862		{
 863			GList *submenu = NULL;
 864			action = purple_menu_action_new((mb->flags & CONTACT_FLAG_VISIBLE) ? _("Remove from 'Visible to' list") :
 865				_("Add to 'Visible to' list"), PURPLE_CALLBACK(blist_toggle_visible), mrim, NULL);
 866			submenu = g_list_append(submenu, action);
 867			action = purple_menu_action_new((mb->flags & CONTACT_FLAG_INVISIBLE) ? _("Remove from 'Invisible to' list") : 
 868				_("Add to 'Invisible to' list"), PURPLE_CALLBACK(blist_toggle_invisible), mrim, NULL);
 869			submenu = g_list_append(submenu, action);
 870			action = purple_menu_action_new(_("Visibility settings"), NULL, mrim, submenu);
 871			list = g_list_append(list, action);
 872		}
 873		return list;
 874	} else { //??? ???? ?????? ?? ?? ??????
 875		return NULL;
 876	}
 877}
 878
 879/* User info */
 880
 881MrimSearchResult *mrim_parse_search_result(MrimPackage *pack) {
 882	guint32 status = mrim_package_read_UL(pack);
 883	purple_debug_info("mrim-prpl", "[%s] Status is %i\n", __func__, status);
 884	if (status != MRIM_ANKETA_INFO_STATUS_OK) {
 885		switch (status) {
 886			case MRIM_ANKETA_INFO_STATUS_NOUSER:
 887				purple_notify_warning(mrim_plugin, _("Encountered an error while working on user details!"),
 888					_("Encountered an error while working on user details!"), _("User not found."));
 889				break;
 890			case MRIM_ANKETA_INFO_STATUS_DBERR:
 891				purple_notify_warning(mrim_plugin, _("Encountered an error while working on user details!"),
 892					_("Encountered an error while working on user details!"), _("DBERR error. Please try later."));
 893				break;
 894			case MRIM_ANKETA_INFO_STATUS_RATELIMERR:
 895				purple_notify_warning(mrim_plugin, _("Encountered an error while working on user details!"),
 896					_("Encountered an error while working on user details!"), _("MRIM_ANKETA_INFO_STATUS_RATELIMERR"));
 897				break;
 898			default:
 899				purple_notify_warning(mrim_plugin, _("Encountered an error while working on user details!"),
 900					_("Encountered an error while working on user details!"), _("unknown error"));
 901				break;
 902		}
 903		return NULL;
 904	}
 905	MrimSearchResult *result = g_new0(MrimSearchResult, 1);
 906	result->column_count = mrim_package_read_UL(pack);
 907	result->row_count = mrim_package_read_UL(pack);
 908	guint32 date = mrim_package_read_UL(pack);
 909	purple_debug_info("mrim-prpl", "[%s] Column count is %i, row count is %i\n", __func__, result->column_count, result->row_count);
 910	result->columns = g_new0(MrimSearchResultColumn, result->column_count);
 911	result->rows = g_new0(gchar**, result->row_count);
 912	guint i;
 913	for (i = 0; i < result->column_count; i++) {
 914		result->columns[i].title = mrim_package_read_LPSA(pack);
 915		if (g_strcmp0(result->columns[i].title, "Username") == 0) {
 916			result->username_index = i;
 917		} else if (g_strcmp0(result->columns[i].title, "Domain") == 0) {
 918			result->domain_index = i;
 919		}
 920		if ((g_strcmp0(result->columns[i].title, "Username") == 0) || (g_strcmp0(result->columns[i].title, "Domain") == 0) ||
 921			(g_strcmp0(result->columns[i].title, "City_id") == 0) || (g_strcmp0(result->columns[i].title, "Country_id") == 0) ||
 922			(g_strcmp0(result->columns[i].title, "BMonth") == 0) || (g_strcmp0(result->columns[i].title, "BDay") == 0) /*|| 
 923			(g_strcmp0(result->columns[i].title, "mrim_status") == 0)*/) {
 924			result->columns[i].skip = TRUE;
 925		} else {
 926			result->columns[i].skip = FALSE;
 927		}
 928		if ((g_strcmp0(result->columns[i].title, "Nickname") == 0) || (g_strcmp0(result->columns[i].title, "FirstName") == 0) ||
 929			(g_strcmp0(result->columns[i].title, "LastName") == 0) || (g_strcmp0(result->columns[i].title, "Location") == 0) ||
 930			(g_strcmp0(result->columns[i].title, "status_title") == 0) || (g_strcmp0(result->columns[i].title, "status_desc") == 0)) {
 931			result->columns[i].unicode = TRUE;
 932		} else {
 933			result->columns[i].unicode = FALSE;
 934		}
 935	}
 936	
 937	for (guint i = 0; i < result->row_count; i++) {
 938		if (pack->cur >= pack->data_size) break;
 939		result->rows[i] = g_new0(gchar*, result->column_count);
 940		for (guint j = 0; j < result->column_count; j++) {
 941			if (result->columns[j].unicode) {
 942				result->rows[i][j] = mrim_package_read_LPSW(pack);
 943			} else {
 944				result->rows[i][j] = mrim_package_read_LPSA(pack);
 945			}
 946			if ((!result->rows[i][j]) || (!result->rows[i][j][0])) {
 947				g_free(result->rows[i][j]);
 948				result->rows[i][j] = g_strdup(" ");
 949			}
 950			if (g_strcmp0(result->columns[j].title, "Sex") == 0) {
 951				gchar *value = (atoi(result->rows[i][j]) == 1) ? g_strdup(_("Male")) : g_strdup(_("Female"));
 952				g_free(result->rows[i][j]);
 953				result->rows[i][j] = value;
 954			} else if (g_strcmp0(result->columns[j].title, "Zodiac") == 0) {
 955				guint index = atoi(result->rows[i][j]) - 1;
 956				if (index < ARRAY_SIZE(zodiac)) {
 957					g_free(result->rows[i][j]);
 958					result->rows[i][j] = g_strdup(_(zodiac[index]));
 959				}
 960			}
 961		}
 962	};
 963	// Detecting if search results contain birthday column so we can determine age then:
 964	purple_debug_info("mrim-prpl", "[%s] Looking for BDay...\n", __func__);
 965	guint bday_col_index = result->column_count;
 966	for (guint i = 0; i < result->column_count; i++) {
 967		if (g_strcmp0(result->columns[i].title, "Birthday") == 0) {
 968			bday_col_index = i;
 969			break;
 970		};
 971	};
 972	if (bday_col_index < result->column_count) {
 973		guint age_col_index = result->column_count++;
 974		result->columns = g_renew(MrimSearchResultColumn, result->columns, result->column_count);
 975		result->columns[age_col_index].title		= "Age";
 976		result->columns[age_col_index].skip			= FALSE;
 977		result->columns[age_col_index].unicode	= FALSE;
 978		
 979		for (guint row_id = 0; row_id < result->row_count; row_id++) {
 980			gchar **old_row = result->rows[row_id];
 981			if (!old_row) {
 982				break;
 983			} else {
 984				result->rows[row_id] = g_new0(gchar*, result->column_count);
 985				for (guint col_id = 0; col_id < age_col_index; col_id++) {
 986					result->rows[row_id][col_id] = g_strdup(old_row[col_id]);
 987					g_free(old_row[col_id]);
 988				}
 989				gchar *BDay_Str = g_strdup(result->rows[row_id][bday_col_index]);
 990				gchar *buddy_age = g_strdup("0");
 991				if ( g_strcmp0(BDay_Str, " ") == 0 ) {
 992					g_free(buddy_age);
 993					buddy_age = g_strdup(_("Not specified"));
 994				} else {
 995					int bd_year=0, bd_mon=0, bd_day=0;
 996					int ret = sscanf(BDay_Str, "%u-%u-%u", &bd_year, &bd_mon, &bd_day);
 997					purple_debug_info("mrim-prpl", "[%s] Birthday parsed (ret=%i) is %i-%i-%i.\n", __func__, ret, bd_year, bd_mon, bd_day);
 998					
 999#if GLIB_MINOR_VERSION < 26
1000			// Age calculation for GLib <= 2.26
1001					GTimeVal *gTimeReal = g_new0(GTimeVal, 1);
1002					GDate *gCurDate			= g_new0(GDate, 1);
1003					g_get_current_time (gTimeReal);
1004					g_date_set_time_val (gCurDate, gTimeReal);
1005					g_date_subtract_years (gCurDate, bd_year);
1006					g_date_subtract_months (gCurDate, bd_mon % 12);
1007					g_date_subtract_days (gCurDate, bd_day);
1008					g_free(buddy_age);
1009					int full_years	= g_date_get_year(gCurDate);
1010					int full_months = g_date_get_month(gCurDate) % 12;
1011					
1012					g_free(gTimeReal);
1013					g_free(gCurDate);
1014			// End GLib < 2.26
1015#else
1016			// Used for GLib >= 2.26
1017					GDateTime *TimeNow	= g_date_time_new_now_local();
1018					GDateTime *LifeTime;
1019					LifeTime	= g_date_time_add_full(TimeNow, -bd_year, -bd_mon, -bd_day, 0, 0, 0);
1020					g_free(buddy_age);
1021					int full_years	= g_date_time_get_year(LifeTime);
1022					int full_months = g_date_time_get_month(LifeTime) % 12;
1023					g_date_time_unref(TimeNow);
1024					g_date_time_unref(LifeTime);
1025			// End GLib >= 2.26
1026#endif
1027					if (!full_months) {
1028						buddy_age	= g_strdup_printf(_("%i full years"), full_years);
1029					} else {
1030						buddy_age	= g_strdup_printf(_("%i years, %i months"), full_years, full_months);
1031					}
1032				}
1033				result->rows[row_id][age_col_index] = buddy_age;
1034			}
1035		}
1036	};
1037	// Age guessing end. 
1038	purple_debug_info("mrim-prpl", "[%s] Search result parsed OK (%i rows)\n", __func__, i);
1039	return result;
1040}
1041
1042void mrim_get_info_ack(MrimData *mrim, gpointer user_data, MrimPackage *pack) {
1043	gchar *user_name = user_data;
1044	MrimSearchResult *result = mrim_parse_search_result(pack);
1045	if (result) {
1046		guint i;
1047		PurpleNotifyUserInfo *info = purple_notify_user_info_new();
1048		purple_notify_user_info_add_pair(info, _("E-mail"), user_name);
1049		for (i = 0; i < result->column_count; i++) {
1050			if (!result->columns[i].skip) {
1051				purple_notify_user_info_add_pair(info, _(result->columns[i].title), result->rows[0][i]);
1052			}
1053		}
1054		PurpleBuddy *buddy = purple_find_buddy(mrim->account, user_name);
1055		if (buddy) {
1056			MrimBuddy *mb = buddy->proto_data;
1057			if (mb) {
1058				if (mb->user_agent) {
1059					gchar *tmp = mrim_get_ua_alias(mrim, mb->user_agent);
1060					purple_notify_user_info_add_pair(info, _("User agent"), tmp);
1061					g_free(tmp);
1062				}
1063				if (mb->microblog) {
1064					purple_notify_user_info_add_pair(info, _("Microblog"), mb->microblog);
1065				}
1066			}
1067		}
1068		purple_notify_userinfo(mrim->gc, user_name, info, NULL, NULL);
1069	}
1070}
1071
1072void mrim_get_info(PurpleConnection *gc, const char *username) {
1073	purple_debug_info("mrim-prpl", "[%s]\n", __func__);
1074	g_return_if_fail(username);
1075	g_return_if_fail(gc);
1076	MrimData *mrim = gc->proto_data;
1077	g_return_if_fail(mrim != NULL);
1078	purple_debug_info("mrim-prpl", "[%s] Fetching info for user '%s'\n", __func__, username);
1079	if (!is_valid_email((gchar*)username)) {
1080		PurpleNotifyUserInfo *info = purple_notify_user_info_new();
1081		purple_notify_user_info_add_pair(info, _("UserInfo is not available for conferences and phones"), "");
1082		purple_notify_userinfo(gc, username, info, NULL, NULL);
1083	} else {
1084		gchar** split = g_strsplit(username, "@", 2);
1085		gchar *user = split[0];
1086		gchar *domain = split[1];
1087		MrimPackage *pack = mrim_package_new(mrim->seq++, MRIM_CS_WP_REQUEST);
1088		mrim_package_add_UL(pack, MRIM_CS_WP_REQUEST_PARAM_USER);
1089		mrim_package_add_LPSA(pack, user);
1090		mrim_package_add_UL(pack, MRIM_CS_WP_REQUEST_PARAM_DOMAIN);
1091		mrim_package_add_LPSA(pack, domain);
1092		g_strfreev(split);
1093		mrim_add_ack_cb(mrim, pack->header->seq, mrim_get_info_ack, g_strdup(username));
1094		mrim_package_send(pack, mrim);
1095	}
1096}
1097
1098void mrim_searchresults_add_buddy(PurpleConnection *gc, GList *row, void *user_data) {
1099	MrimData *mrim = user_data;
1100	purple_debug_info("mrim-prpl","[%s] %s\n", __func__, mrim->account->username);
1101	if (!purple_find_buddy(mrim->account, g_list_nth_data(row, 0))) {
1102			purple_blist_request_add_buddy(mrim->account,  g_list_nth_data(row, 0), NULL, NULL); // TODO Propose alias automatically.
1103		}
1104}
1105
1106void mrim_search_ack(MrimData *mrim, gpointer user_data, MrimPackage *pack) {
1107	MrimSearchResult *result = mrim_parse_search_result(pack);
1108	if (result) {
1109		purple_debug_info("mrim-prpl", "[%s]\n", __func__);
1110		PurpleNotifySearchResults *results = purple_notify_searchresults_new();
1111		PurpleNotifySearchColumn *column = purple_notify_searchresults_column_new(_("E-mail"));
1112		purple_notify_searchresults_column_add(results, column);
1113		guint32 i;
1114		for (i = 0; i < result->column_count; i++) {
1115			if (!result->columns[i].skip) {
1116				column = purple_notify_searchresults_column_new(g_strdup(_(result->columns[i].title)));
1117				purple_notify_searchresults_column_add(results, column);
1118			}
1119		}
1120		purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_ADD,
1121			mrim_searchresults_add_buddy);
1122		for (i = 0; i < result->row_count; i++) {
1123			if (result->rows[i]) {
1124				guint32 j;
1125				GList *row = g_list_append(NULL, g_strdup_printf("%s@%s",
1126					result->rows[i][result->username_index],
1127						result->rows[i][result->domain_index]));
1128				for (j = 0; j < result->column_count; j++) {
1129					if (!result->columns[j].skip) {
1130						row = g_list_append(row, result->rows[i][j]);
1131					}
1132				}
1133				purple_notify_searchresults_row_add(results, row);
1134			} else {
1135				break;
1136			}
1137		}
1138		purple_notify_searchresults(mrim->gc, NULL, _("Search results"), NULL, results, NULL, NULL);
1139	}
1140}
1141
1142/* Tooltip */
1143
1144void mrim_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *info, gboolean full) {
1145	purple_debug_info("mrim-prpl","[%s]\n",__func__);
1146	g_return_if_fail(buddy);
1147	if (buddy->alias) {
1148		purple_notify_user_info_add_pair(info, _("Name"), buddy->alias);
1149	}
1150	MrimBuddy *mb = buddy->proto_data;
1151	if (mb) {
1152		MrimData *mrim = mb->mrim;
1153		if (mb->flags & CONTACT_FLAG_MULTICHAT) {
1154			purple_notify_user_info_add_pair(info, _("Account"), buddy->account->username);
1155			purple_notify_user_info_add_pair(info, _("Room"), mb->email);
1156			purple_notify_user_info_add_pair(info, _("Alias"), mb->alias);
1157			return;
1158		}
1159		if (mb->status->id != STATUS_OFFLINE) {
1160			//if (mb->listening) {
1161				purple_notify_user_info_add_pair(info, _("Status"), mb->status->title);
1162			//} else {
1163			//	purple_notify_user_info_add_pair(info, _("Status"), mb->status->display_str);
1164			//}
1165		}
1166		if (mb->listening) {
1167			purple_notify_user_info_add_pair(info, _("Listening"), mb->listening);
1168		} else if (mb->status && mb->status->desc) {
1169			purple_notify_user_info_add_pair(info, _("Comment"), mb->status->desc);
1170		}
1171		if (mb->user_agent) {
1172			gchar *tmp = mrim_get_ua_alias(mrim, mb->user_agent);
1173			purple_notify_user_info_add_pair(info, _("User agent"), tmp);
1174			g_free(tmp);
1175		}
1176		if (mb->microblog) {
1177			purple_notify_user_info_add_pair(info, _("Microblog"), mb->microblog);
1178		}
1179	}
1180	purple_debug_info("mrim-prpl","[%s] end.\n",__func__);
1181}
1182
1183/* Authorization */
1184
1185void mrim_authorization_yes(gpointer va_data) {
1186	MrimAuthData *data = va_data;
1187	MrimData *mrim = data->mrim;
1188	purple_debug_info("mrim-prpl","[%s] Authorization request from '%s' acepted\n", __func__, data->from);
1189	MrimPackage *pack = mrim_package_new(data->seq, MRIM_CS_AUTHORIZE);
1190	mrim_package_add_LPSA(pack, data->from);
1191	mrim_package_send(pack, mrim);
1192	PurpleBuddy *buddy = purple_find_buddy(mrim->account, data->from);
1193	if (buddy && buddy->proto_data)	{
1194		MrimBuddy *mb = buddy->proto_data;
1195		if (!mb->authorized) {
1196			mrim_send_authorize(mrim, data->from, NULL); /* TODO: Request auth message */
1197		}
1198	}
1199	g_free(data->from);
1200	g_free(data);
1201}
1202
1203void mrim_authorization_no(gpointer va_data) {
1204	purple_debug_info("mrim-prpl", "[%s]\n", __func__);
1205	MrimAuthData *data = va_data;
1206	g_free(data->from);
1207	g_free(data);
1208}
1209
1210void mrim_send_authorize(MrimData *mrim, gchar *email, gchar *message) { /* TODO: Auth message */
1211	purple_debug_info("mrim-prpl", "[%s] Send auhtorization request to '%s' with message '%s'\n", __func__, email, message);
1212	MrimPackage *pack = mrim_package_new(mrim->seq++, MRIM_CS_MESSAGE);
1213	mrim_package_add_UL(pack, MESSAGE_FLAG_AUTHORIZE | MESSAGE_FLAG_NORECV);
1214	mrim_package_add_LPSA(pack, email);
1215	mrim_package_add_base64(pack, "uss", 2, mrim->nick, message ? message : " ");
1216	mrim_package_add_UL(pack, 0);
1217	mrim_package_send(pack, mrim);
1218}
1219
1220/*
1221* CHATS
1222*/
1223
1224// handle MULTICHAT_GET_MEMBERS
1225void mrim_chat_blist(MrimData *mrim, gpointer data, MrimPackage *pack)
1226{
1227	purple_debug_info("mrim-prpl", "[%s] room=<%s>\n", __func__, (gchar*) data);
1228	PurpleConversation *conv =  purple_find_chat(mrim->gc, get_chat_id(data));
1229	PurpleConvChat *chat = PURPLE_CONV_CHAT(conv);
1230
1231	mrim_package_read_UL(pack); // todo: wtf?? 0x62
1232	mrim_package_read_UL(pack); // todo: wtf?? 0x02 == MULTICHAT_MEMBERS
1233	gchar *topic = mrim_package_read_LPSW(pack);
1234	mrim_package_read_UL(pack); // todo: wtf?? 0x4c
1235	// Set Topic
1236	purple_conv_chat_set_topic(chat, NULL, topic);
1237	///
1238	/// Add users
1239	int n = mrim_package_read_UL(pack);
1240	for (int i = 0; i<n ; i++)
1241	{
1242		gchar *username = mrim_package_read_LPSA(pack);
1243		purple_conv_chat_add_user(chat, username, NULL, PURPLE_CBFLAGS_NONE, TRUE);
1244	}
1245}
1246
1247
1248void mrim_chat_join(PurpleConnection *gc, GHashTable *components)
1249{
1250	gchar *room = g_hash_table_lookup(components, "room");
1251	if (! is_valid_chat(room))
1252	{
1253		char *buf = g_strdup_printf(_("%s is not a valid room name"), room);
1254		purple_notify_error(gc, _("Invalid Room Name"), _("Invalid Room Name"), buf);
1255		purple_serv_got_join_chat_failed(gc, components);
1256		g_free(buf);
1257		return;
1258	}
1259
1260	const char *username = gc->account->username;
1261	MrimData *mrim = gc->proto_data;
1262	PurpleChat *pchat = purple_blist_find_chat(gc->account, room);
1263	if (pchat == NULL) // TODO
1264	{
1265		// add chat
1266		purple_debug_info("mrim-prpl", "[%s] New chat: %s \n", __func__, room);
1267		GHashTable *defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
1268		g_hash_table_insert(defaults, "room", g_strdup(room));
1269		PurpleChat *pc = purple_chat_new(mrim->account, room, defaults);
1270
1271		purple_blist_add_chat(pc, get_mrim_group(mrim, 0)->group, NULL);  // add to "?????????" // TODO // TODO move to ack?
1272
1273		MrimPackage *pack = mrim_package_new(mrim->seq, MRIM_CS_ADD_CONTACT);
1274		mrim_package_add_UL(pack, CONTACT_FLAG_MULTICHAT);
1275		mrim_package_add_UL(pack, 0);
1276		mrim_package_add_UL(pack, 0);
1277		mrim_package_add_LPSW(pack, "THIS IS TOPIC"); // TODO
1278		mrim_package_add_UL(pack, 0);
1279		mrim_package_add_UL(pack, 0);
1280		mrim_package_add_UL(pack, 0);
1281
1282		mrim_package_add_UL(pack, 0); // UL 34h 50h 21h
1283		mrim_package_add_UL(pack, 0); // UL 30h 1fh
1284		// UL count
1285		// count ???? LPS email
1286		mrim_package_send(pack, mrim);
1287
1288	}
1289
1290	if (!purple_find_chat(gc, get_chat_id(room))) {
1291		purple_debug_info("mrim-prpl", "[%s] %s is joining chat room %s\n", __func__, username, room);
1292
1293		serv_got_joined_chat(gc, get_chat_id(room), room);
1294		// Get users list
1295		mrim_add_ack_cb(mrim, mrim->seq, mrim_chat_blist, g_strdup(room));
1296
1297		MrimPackage *pack = mrim_package_new(mrim->seq++, MRIM_CS_MESSAGE);
1298		mrim_package_add_UL(pack, MESSAGE_FLAG_MULTICHAT );
1299		mrim_package_add_LPSA(pack, room);
1300		mrim_package_add_UL(pack, 0);
1301		mrim_package_add_UL(pack, 0);
1302		mrim_package_add_UL(pack, 4);
1303		mrim_package_add_UL(pack, MULTICHAT_GET_MEMBERS);
1304		mrim_package_send(pack, mrim);
1305	}
1306}
1307
1308void mrim_reject_chat(PurpleConnection *gc, GHashTable *components)
1309{
1310	const char *room = g_hash_table_lookup(components, "room");
1311	purple_debug_info("mrim-prpl", "[%s] room = %s\n", __func__, room);
1312}
1313
1314char *mrim_get_chat_name(GHashTable *components)
1315{
1316	purple_debug_info("mrim", "[%s]\n", __func__);
1317	const char *str = g_hash_table_lookup(components, "room");
1318	return (char*)str;
1319}
1320
1321
1322void mrim_chat_invite(PurpleConnection *gc, int id, const char *message, const char *who)
1323{
1324	purple_debug_info("mrim-prpl", "[%s]\n", __func__);
1325	MrimData *mrim = gc->proto_data;
1326	PurpleConversation *conv = purple_find_chat(gc, id);
1327	const char *room = conv->name;
1328
1329	purple_debug_info("mrim-prpl", "[%s] %s is invited to join chat room %s\n", __func__, who, room);
1330
1331	MrimPackage *pack = mrim_package_new(mrim->seq++, MRIM_CS_MESSAGE);
1332	mrim_package_add_UL(pack, CONTACT_FLAG_MULTICHAT );
1333	mrim_package_add_LPSA(pack, room);
1334	mrim_package_add_UL(pack, 0); // message
1335	mrim_package_add_UL(pack, 0); // rtf
1336	mrim_package_add_UL(pack, 0); // 0x27 ???
1337	mrim_package_add_UL(pack, MULTICHAT_ADD_MEMBERS);
1338	mrim_package_add_UL(pack, 0); // 0x1f
1339	mrim_package_add_UL(pack, 1); // CLPS?
1340	mrim_package_add_LPSA(pack, who);
1341	mrim_package_send(pack, mrim);
1342}
1343
1344void mrim_chat_leave(PurpleConnection *gc, int id)
1345{
1346	PurpleConversation *conv = purple_find_chat(gc, id);
1347	purple_debug_info("mrim-prpl", "[%s] %s is leaving chat room %s\n", __func__, gc->account->username, conv->name);
1348}