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