1 /* $Id: search.c,v 1.37 2003/06/27 17:30:51 jajcus Exp $ */
2 
3 /*
4  *  (C) Copyright 2002-2013 Jacek Konieczny [jajcus(a)jajcus,net]
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 Version 2 as
8  *  published by the Free Software Foundation.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 #include "ggtrans.h"
21 #include <libgadu.h>
22 #include "search.h"
23 #include "requests.h"
24 #include "sessions.h"
25 #include "stream.h"
26 #include "iq.h"
27 #include "jid.h"
28 #include "encoding.h"
29 #include "gg_versions.h"
30 #include "forms.h"
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 #include "debug.h"
35 
36 
37 const char *search_instructions;
38 
39 void jabber_iq_set_search_byform(Stream *s,const char *from,const char *to,
40 				const char *id,xmlnode q);
41 
search_form(xmlnode parent)42 xmlnode search_form(xmlnode parent){
43 xmlnode form,field;
44 
45 	form=form_new(parent,_("GG public directory search"),_("Enter your search filter"));
46 
47 	form_add_field(form,"text-single","uin",_("GG number"),NULL,0);
48 	form_add_field(form,"text-single","nick",_("Nick"),NULL,0);
49 	form_add_field(form,"text-single","firstname",_("First name"),NULL,0);
50 	form_add_field(form,"text-single","lastname",_("Last name"),NULL,0);
51 	form_add_field(form,"text-single","birthyear",_("Birth year"),NULL,0);
52 	form_add_field(form,"text-single","city",_("City"),NULL,0);
53 	field=form_add_field(form,"list-single","gender",_("Sex"),"_any_",0);
54 	form_add_option(field,_("Any"),"_any_");
55 	form_add_option(field,_("Female"),GG_PUBDIR50_GENDER_FEMALE);
56 	form_add_option(field,_("Male"),GG_PUBDIR50_GENDER_MALE);
57 	form_add_field(form,"boolean","active",_("Active only"),"0",0);
58 	form_add_field(form,"text-single","familyname",_("Family name"),NULL,0);
59 	form_add_field(form,"text-single","familycity",_("Family city"),NULL,0);
60 	return form;
61 }
62 
63 #define GG_SEARCH_FRIENDS_MASK	0x0080
64 
search_byform_done(struct request_s * r,gg_pubdir50_t results)65 int search_byform_done(struct request_s *r, gg_pubdir50_t results){
66 int i;
67 xmlnode q,form,item;
68 char *jid;
69 const char *val;
70 
71 	q=xmlnode_new_tag("query");
72 	xmlnode_put_attrib(q,"xmlns","jabber:iq:search");
73 	form=form_new_result(_("GG public directory search results"));
74 
75 	form_add_result_field(form,"jid",_("JID"),"jid-single");
76 	form_add_result_field(form,"nick",_("Nick"),"text-single");
77 	form_add_result_field(form,"status",_("Status"),"text-single");
78 	form_add_result_field(form,"firstname",_("First name"),"text-single");
79 	form_add_result_field(form,"lastname",_("Last name"),"text-single");
80 	form_add_result_field(form,"city",_("City"),"text-single");
81 	form_add_result_field(form,"gender",_("Sex"),"text-single");
82 	form_add_result_field(form,"birthyear","Birth year","text-single");
83 	form_add_result_field(form,"familyname",_("Family name"),"text-single");
84 	form_add_result_field(form,"familycity",_("Family city"),"text-single");
85 
86 	for(i=0;i<gg_pubdir50_count(results);i++){
87 		item=form_add_result_item(form);
88 		val=gg_pubdir50_get(results, i, GG_PUBDIR50_UIN);
89 		if (val==NULL) continue;
90 		jid=jid_build(atoi(val));
91 		form_add_result_value(item,"jid",jid);
92 		g_free(jid);
93 		val=gg_pubdir50_get(results, i, GG_PUBDIR50_NICKNAME);
94 		form_add_result_value(item,"nick",to_utf8(val));
95 		val=gg_pubdir50_get(results, i, GG_PUBDIR50_STATUS);
96 		switch((val)?(atoi(val) & ~GG_SEARCH_FRIENDS_MASK):-1){
97 			case GG_STATUS_AVAIL:
98 				form_add_result_value(item,"status",_("Available"));
99 				break;
100 			case GG_STATUS_BUSY:
101 				form_add_result_value(item,"status",_("Away"));
102 				break;
103 			case GG_STATUS_FFC:
104 				form_add_result_value(item,"status",_("Free for chat"));
105 				break;
106 			case GG_STATUS_DND:
107 				form_add_result_value(item,"status",_("Do not disturb"));
108 				break;
109 			default:
110 				form_add_result_value(item,"status",_("Unavailable"));
111 				break;
112 		}
113 		val=gg_pubdir50_get(results, i, GG_PUBDIR50_FIRSTNAME);
114 		form_add_result_value(item,"firstname",to_utf8(val));
115 		val=gg_pubdir50_get(results, i, GG_PUBDIR50_LASTNAME);
116 		form_add_result_value(item,"lastname",to_utf8(val));
117 		val=gg_pubdir50_get(results, i, GG_PUBDIR50_CITY);
118 		form_add_result_value(item,"city",to_utf8(val));
119 		val=gg_pubdir50_get(results, i, GG_PUBDIR50_GENDER);
120 		if (val && !strcmp(val,GG_PUBDIR50_GENDER_FEMALE))
121 			form_add_result_value(item,"gender",_("female"));
122 		else if (val && !strcmp(val,GG_PUBDIR50_GENDER_MALE))
123 			form_add_result_value(item,"gender",_("male"));
124 		else form_add_result_value(item,"gender","");
125 		val=gg_pubdir50_get(results, i, GG_PUBDIR50_BIRTHYEAR);
126 		form_add_result_value(item,"birthyear",to_utf8(val));
127 		val=gg_pubdir50_get(results, i, GG_PUBDIR50_FAMILYNAME);
128 		form_add_result_value(item,"familyname",to_utf8(val));
129 		val=gg_pubdir50_get(results, i, GG_PUBDIR50_FAMILYCITY);
130 		form_add_result_value(item,"familycity",to_utf8(val));
131 	}
132 	xmlnode_insert_tag_node(q,form);
133 	jabber_iq_send_result(r->stream,r->from,r->to,r->id,q);
134 	xmlnode_free(q);
135 	xmlnode_free(form);
136 	return 0;
137 }
138 
search_done(struct request_s * r,gg_pubdir50_t results)139 int search_done(struct request_s *r, gg_pubdir50_t results){
140 xmlnode q,item,n;
141 char *jid,*name;
142 int i;
143 const char *uin, *first_name, *last_name, *nickname, *born, *city, *gender, *active;
144 
145 	n=xmlnode_get_tag(r->query,"x?xmlns=jabber:x:data");
146 	if (n){
147 		return search_byform_done(r,results);
148 	}
149 	q=xmlnode_new_tag("query");
150 	xmlnode_put_attrib(q,"xmlns","jabber:iq:search");
151 	for(i=0;i<gg_pubdir50_count(results);i++){
152 		item=xmlnode_insert_tag(q,"item");
153 		uin=gg_pubdir50_get(results, i, GG_PUBDIR50_UIN);
154 		if (uin==NULL) continue;
155 		jid=jid_build(atoi(uin));
156 		xmlnode_put_attrib(item,"jid",jid);
157 		g_free(jid);
158 		first_name=gg_pubdir50_get(results, i, GG_PUBDIR50_FIRSTNAME);
159 		last_name=gg_pubdir50_get(results, i, GG_PUBDIR50_LASTNAME);
160 		name=g_strdup_printf("%s %s",first_name,last_name);
161 		n=xmlnode_insert_tag(item,"name");
162 		xmlnode_insert_cdata(n,to_utf8(name),-1);
163 		g_free(name);
164 		n=xmlnode_insert_tag(item,"first");
165 		xmlnode_insert_cdata(n,to_utf8(first_name),-1);
166 		n=xmlnode_insert_tag(item,"last");
167 		xmlnode_insert_cdata(n,to_utf8(last_name),-1);
168 		nickname=gg_pubdir50_get(results, i, GG_PUBDIR50_NICKNAME);
169 		n=xmlnode_insert_tag(item,"nick");
170 		xmlnode_insert_cdata(n,to_utf8(nickname),-1);
171 		born=gg_pubdir50_get(results, i, GG_PUBDIR50_BIRTHYEAR);
172 		if (born){
173 			n=xmlnode_insert_tag(item,"born");
174 			xmlnode_insert_cdata(n,born,-1);
175 		}
176 		gender=gg_pubdir50_get(results, i, GG_PUBDIR50_GENDER);
177 		if (gender && strcmp(gender, GG_PUBDIR50_GENDER_FEMALE)==0)
178 			xmlnode_insert_cdata(xmlnode_insert_tag(item,"gender"),"f",-1);
179 		else if (gender && strcmp(gender, GG_PUBDIR50_GENDER_MALE)==0)
180 			xmlnode_insert_cdata(xmlnode_insert_tag(item,"gender"),"m",-1);
181 		city=gg_pubdir50_get(results, i, GG_PUBDIR50_CITY);
182 		n=xmlnode_insert_tag(item,"city");
183 		xmlnode_insert_cdata(n,to_utf8(city),-1);
184 		active=gg_pubdir50_get(results, i, GG_PUBDIR50_ACTIVE);
185 		if (active && strcmp(active, GG_PUBDIR50_ACTIVE_TRUE)==0)
186 			xmlnode_insert_cdata(xmlnode_insert_tag(item,"active"),"yes",-1);
187 	}
188 	jabber_iq_send_result(r->stream,r->from,r->to,r->id,q);
189 	xmlnode_free(q);
190 	return 0;
191 }
192 
jabber_iq_get_search(Stream * s,const char * from,const char * to,const char * id,xmlnode q)193 void jabber_iq_get_search(Stream *s,const char *from,const char *to,const char *id,xmlnode q){
194 xmlnode iq,n;
195 Session *sess;
196 
197 	sess=session_get_by_jid(from,NULL,0);
198 	if (!sess || !sess->connected){
199 		jabber_iq_send_error(s,from,to,id,407,_("Not logged in"));
200 		return;
201 	}
202 
203 	iq=xmlnode_new_tag("query");
204 	xmlnode_put_attrib(iq,"xmlns","jabber:iq:search");
205 	n=xmlnode_insert_tag(iq,"instructions");
206 	xmlnode_insert_cdata(n,search_instructions,-1);
207 	xmlnode_insert_tag(iq,"active");
208 	xmlnode_insert_tag(iq,"first");
209 	xmlnode_insert_tag(iq,"last");
210 	xmlnode_insert_tag(iq,"nick");
211 	xmlnode_insert_tag(iq,"city");
212 	xmlnode_insert_tag(iq,"gender");
213 	xmlnode_insert_tag(iq,"born");
214 	xmlnode_insert_tag(iq,"phone");
215 	xmlnode_insert_tag(iq,"username");
216 	search_form(iq);
217 
218 	jabber_iq_send_result(s,from,to,id,iq);
219 	xmlnode_free(iq);
220 }
221 
222 #define FIELD_TO_PUBDIR(fieldname,symbol) \
223 	val=NULL; \
224 	field=xmlnode_get_tag(form,"field?var=" fieldname); \
225 	if (field!=NULL){ \
226 		value=xmlnode_get_tag(field,"value"); \
227 		if (value!=NULL) val=xmlnode_get_data(value); \
228 	} \
229 	if (val!=NULL && val[0]) \
230 		gg_pubdir50_add(search, symbol, from_utf8(val));
231 
jabber_iq_set_search_byform(Stream * s,const char * from,const char * to,const char * id,xmlnode q)232 void jabber_iq_set_search_byform(Stream *s,const char *from,const char *to,
233 		const char *id,xmlnode q){
234 xmlnode form,field,value;
235 char *val;
236 gg_pubdir50_t search;
237 Session *sess;
238 
239 	form=xmlnode_get_tag(q,"x?xmlns=jabber:x:data");
240 
241 	sess=session_get_by_jid(from,NULL,0);
242 	if (!sess || !sess->connected){
243 		jabber_iq_send_error(s,from,to,id,407,_("Not logged in"));
244 		return;
245 	}
246 
247 	search=gg_pubdir50_new(GG_PUBDIR50_SEARCH);
248 	FIELD_TO_PUBDIR("uin",GG_PUBDIR50_UIN);
249 	FIELD_TO_PUBDIR("firstname",GG_PUBDIR50_FIRSTNAME);
250 	FIELD_TO_PUBDIR("lastname",GG_PUBDIR50_LASTNAME);
251 	FIELD_TO_PUBDIR("nick",GG_PUBDIR50_NICKNAME);
252 	FIELD_TO_PUBDIR("city",GG_PUBDIR50_CITY);
253 
254 	field=xmlnode_get_tag(form,"field?var=gender");
255 	if (field!=NULL){
256 		value=xmlnode_get_tag(field,"value");
257 		if (value!=NULL) val=xmlnode_get_data(value);
258 	}
259 	if (val!=NULL && val[0] && ( !strcmp(val,GG_PUBDIR50_GENDER_FEMALE)
260 					|| !strcmp(val,GG_PUBDIR50_GENDER_FEMALE)) )
261 		gg_pubdir50_add(search, GG_PUBDIR50_GENDER, val);
262 
263 	val=NULL;
264 	field=xmlnode_get_tag(form,"field?var=active");
265 	if (field!=NULL){
266 		value=xmlnode_get_tag(field,"value");
267 		if (value!=NULL) val=xmlnode_get_data(value);
268 	}
269 	if (val!=NULL && strcmp(val,"1")==0 )
270 		gg_pubdir50_add(search, GG_PUBDIR50_ACTIVE,GG_PUBDIR50_ACTIVE_TRUE);
271 
272 	FIELD_TO_PUBDIR("birthyear",GG_PUBDIR50_BIRTHYEAR);
273 	FIELD_TO_PUBDIR("familyname",GG_PUBDIR50_FAMILYNAME);
274 	FIELD_TO_PUBDIR("familycity",GG_PUBDIR50_FAMILYCITY);
275 
276 	gg_pubdir50_add(search, GG_PUBDIR50_START, "0");
277 
278 	add_request(RT_SEARCH,from,to,id,q,search,s);
279 	gg_pubdir50_free(search);
280 }
281 
jabber_iq_set_search(Stream * s,const char * from,const char * to,const char * id,xmlnode q)282 void jabber_iq_set_search(Stream *s,const char *from,const char *to,const char *id,xmlnode q){
283 gg_pubdir50_t sr;
284 xmlnode n;
285 Session *sess;
286 char *data;
287 
288 	n=xmlnode_get_tag(q,"x?xmlns=jabber:x:data");
289 	if (n){
290 		return jabber_iq_set_search_byform(s,from,to,id,q);
291 	}
292 	sess=session_get_by_jid(from,NULL,0);
293 	if (!sess || !sess->connected){
294 		jabber_iq_send_error(s,from,to,id,407,_("Not logged in"));
295 		return;
296 	}
297 
298 	q=xmlnode_dup(q);
299 	sr=gg_pubdir50_new(GG_PUBDIR50_SEARCH);
300 	n=xmlnode_get_tag(q,"active");
301 	if (n){
302 		data=xmlnode_get_data(n);
303 		if (data!=NULL && (data[0]=='y' || data[0]=='Y' || data[0]=='t' || data[0]=='T'))
304 			gg_pubdir50_add(sr, GG_PUBDIR50_ACTIVE, GG_PUBDIR50_ACTIVE_TRUE);
305 	}
306 	n=xmlnode_get_tag(q,"nick");
307 	if (n){
308 		data=xmlnode_get_data(n);
309 		if (data)
310 			gg_pubdir50_add(sr, GG_PUBDIR50_NICKNAME, from_utf8(data));
311 	}
312 	n=xmlnode_get_tag(q,"first");
313 	if (n){
314 		data=xmlnode_get_data(n);
315 		if (data)
316 			gg_pubdir50_add(sr, GG_PUBDIR50_FIRSTNAME, from_utf8(data));
317 	}
318 	n=xmlnode_get_tag(q,"last");
319 	if (n){
320 		data=xmlnode_get_data(n);
321 		if (data)
322 			gg_pubdir50_add(sr, GG_PUBDIR50_LASTNAME, from_utf8(data));
323 	}
324 	n=xmlnode_get_tag(q,"city");
325 	if (n){
326 		data=xmlnode_get_data(n);
327 		if (data)
328 			gg_pubdir50_add(sr, GG_PUBDIR50_CITY, from_utf8(data));
329 	}
330 	n=xmlnode_get_tag(q,"gender");
331 	if (n){
332 		data=xmlnode_get_data(n);
333 		if (data!=NULL){
334 			if (data[0]=='k' || data[0]=='f' || data[0]=='K' || data[0]=='F')
335 				gg_pubdir50_add(sr, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_FEMALE);
336 			else if (data[0]=='m' || data[0]=='M')
337 				gg_pubdir50_add(sr, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_MALE);
338 		}
339 	}
340 	n=xmlnode_get_tag(q,"born");
341 	if (n){
342 		data=xmlnode_get_data(n);
343 		if (data)
344 			gg_pubdir50_add(sr, GG_PUBDIR50_BIRTHYEAR, data);
345 	}
346 	n=xmlnode_get_tag(q,"username");
347 	if (n){
348 		data=xmlnode_get_data(n);
349 		if (data)
350 			gg_pubdir50_add(sr, GG_PUBDIR50_UIN, data);
351 	}
352 
353 	add_request(RT_SEARCH,from,to,id,q,(void*)sr,s);
354 	gg_pubdir50_free(sr);
355 }
356 
jabber_iq_get_user_vcard(Stream * s,const char * from,const char * to,const char * id,xmlnode q)357 void jabber_iq_get_user_vcard(Stream *s,const char *from,const char * to,const char *id,xmlnode q){
358 int i=0;
359 char *uin;
360 gg_pubdir50_t sr;
361 
362 	sr=gg_pubdir50_new(GG_PUBDIR50_SEARCH);
363 
364 	while(to[i]!='@')
365 		i++;
366 
367 	uin=g_strndup(to, i);
368 
369 	gg_pubdir50_add(sr, GG_PUBDIR50_UIN, uin);
370 
371 	add_request(RT_VCARD,from,to,id,q,(void*)sr,s);
372 
373 	gg_pubdir50_free(sr);
374 	g_free(uin);
375 }
376 
vcard_done(struct request_s * r,gg_pubdir50_t results)377 int vcard_done(struct request_s *r, gg_pubdir50_t results){
378 xmlnode vc,n,n1;
379 char *jid,*name=NULL,*str;
380 GList *it;
381 Contact *c;
382 User *u;
383 const char *uin, *first_name, *last_name, *nickname, *born, *city;
384 
385 
386 	if (gg_pubdir50_count(results)<1){
387 		jabber_iq_send_error(r->stream,r->from,r->to,r->id,404,_("Not Found"));
388 		return 1;
389 	}
390 
391 	uin=gg_pubdir50_get(results, 0, GG_PUBDIR50_UIN);
392 	first_name=gg_pubdir50_get(results, 0, GG_PUBDIR50_FIRSTNAME);
393 	last_name=gg_pubdir50_get(results, 0, GG_PUBDIR50_LASTNAME);
394 	nickname=gg_pubdir50_get(results, 0, GG_PUBDIR50_NICKNAME);
395 	born=gg_pubdir50_get(results, 0, GG_PUBDIR50_BIRTHYEAR);
396 	city=gg_pubdir50_get(results, 0, GG_PUBDIR50_CITY);
397 
398 	vc=xmlnode_new_tag("vCard");
399 	xmlnode_put_attrib(vc,"xmlns","vcard-temp");
400 	xmlnode_put_attrib(vc,"version","2.0");
401 
402 	n=xmlnode_insert_tag(vc,"FN");
403 	if (first_name && last_name){
404 		name=g_strdup_printf("%s %s",first_name,
405 							last_name);
406 		xmlnode_insert_cdata(n,to_utf8(name),-1);
407 	}
408 	else if (first_name){
409 		xmlnode_insert_cdata(n,to_utf8(first_name),-1);
410 	}
411 	else if (last_name){
412 		xmlnode_insert_cdata(n,to_utf8(last_name),-1);
413 	}
414 	g_free(name);
415 
416 	n1=xmlnode_insert_tag(vc,"N");
417 	n=xmlnode_insert_tag(n1,"GIVEN");
418 	if (first_name){
419 		xmlnode_insert_cdata(n,to_utf8(first_name),-1);
420 	}
421 	n=xmlnode_insert_tag(n1,"FAMILY");
422 	if (last_name){
423 		xmlnode_insert_cdata(n,to_utf8(last_name),-1);
424 	}
425 
426 	n=xmlnode_insert_tag(vc,"NICKNAME");
427 	if (nickname){
428 		xmlnode_insert_cdata(n,to_utf8(nickname),-1);
429 	}
430 
431 	if (born){
432 		n=xmlnode_insert_tag(vc,"BDAY");
433 		xmlnode_insert_cdata(n,born,-1);
434 	}
435 
436 	n1=xmlnode_insert_tag(vc,"ADR");
437 	xmlnode_insert_tag(n1,"HOME");
438 	n=xmlnode_insert_tag(n1,"LOCALITY");
439 	xmlnode_insert_cdata(n,to_utf8(city),-1);
440 
441 	if (uin){
442 		jid=jid_build(atoi(uin));
443 		n=xmlnode_insert_tag(vc,"JABBERID");
444 		xmlnode_insert_cdata(n,jid,-1);
445 		g_free(jid);
446 	}
447 
448 	c=NULL;
449 	u=user_get_by_jid(r->from);
450 	if (u){
451 		for(it=g_list_first(u->contacts);it;it=it->next){
452 			c=(Contact *)it->data;
453 			if (c->uin==atoi(uin)) break;
454 		}
455 		if (it==NULL) c=NULL;
456 	}
457 
458 	n=xmlnode_insert_tag(vc,"DESC");
459 	xmlnode_insert_cdata(n,_("GG user\n"),-1);
460 	if (c!=NULL){
461 		struct in_addr a;
462 		a.s_addr=c->ip;
463 		str=g_strdup_printf(_("Client version: %s (prot.0x%02X)\n"),
464 					((c->version & 0xff) < GG_VERSION_ELEMENTS) && gg_version[c->version & 0xff]
465 						? gg_version[c->version & 0xff]
466 						: "?.?.?",
467 					c->version & 0xff);
468 		xmlnode_insert_cdata(n,str,-1);
469 		g_free(str);
470 		str=g_strdup_printf(_("User IP: %s\n"),inet_ntoa(a));
471 		xmlnode_insert_cdata(n,str,-1);
472 		g_free(str);
473 		str=g_strdup_printf(_("User port: %u\n"),(unsigned)c->port);
474 		xmlnode_insert_cdata(n,str,-1);
475 		g_free(str);
476 	}
477 
478 	jabber_iq_send_result(r->stream,r->from,r->to,r->id,vc);
479 	xmlnode_free(vc);
480 	return 0;
481 }
482 
483