1a8deaf44e3b6d105a28d3a518bea795926199e7
[sip-router] / modules_k / presence / presentity.c
1 /*
2  * $Id$
3  *
4  * presence module - presence server implementation
5  *
6  * Copyright (C) 2006 Voice Sistem S.R.L.
7  *
8  * This file is part of openser, a free SIP server.
9  *
10  * openser is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version
14  *
15  * openser is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License 
21  * along with this program; if not, write to the Free Software 
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  *
24  * History:
25  * --------
26  *  2006-08-15  initial version (anca)
27  */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
33
34 #include "../../db/db.h"
35 #include "../../dprint.h"
36 #include "../../mem/shm_mem.h"
37 #include "../../str.h"
38 #include "../alias_db/alias_db.h"
39 #include "../../data_lump_rpl.h"
40 #include "presentity.h"
41 #include "presence.h" 
42 #include "notify.h"
43 #include "publish.h"
44 #include "hash.h"
45 #include "utils_func.h"
46
47 extern int use_db;
48 extern char* presentity_table;
49 extern db_con_t* pa_db;
50 extern db_func_t pa_dbf;
51
52 static str pu_200_rpl  = str_init("OK");
53 static str pu_412_rpl  = str_init("Conditional request failed");
54
55 char* generate_ETag(int publ_count)
56 {
57         char* etag;
58         int size = 0;
59         etag = (char*)pkg_malloc(128*sizeof(char));
60         if(etag ==NULL)
61         {
62                 LOG(L_ERR, "PRESENCE:generate_ETag:Error while allocating memory \n");
63                 return NULL ;
64         }
65         memset(etag, 0, 60*sizeof(char));
66         size = sprintf (etag, "%c.%d.%d.%d.%d",prefix, startup_time, pid, counter, publ_count);
67         if( size <0 )
68         {
69                 LOG(L_ERR, "PRESENCE: generate_ETag: ERROR unsuccessfull sprintf\n ");
70                 pkg_free(etag);
71                 return NULL;
72         }
73         if(size> 128)
74         {
75                 LOG(L_ERR, "PRESENCE: generate_ETag: ERROR buffer size overflown\n");
76                 pkg_free(etag);
77                 return NULL;
78         }
79
80         etag[size] = '\0';
81         DBG("PRESENCE: generate_ETag: etag= %s / %d\n ",etag, size);
82         return etag;
83 }
84
85 int publ_send200ok(struct sip_msg *msg, int lexpire, str etag)
86 {
87         char buf[128];
88         int buf_len= 128, size;
89         str hdr_append= {0, 0}, hdr_append2= {0, 0} ;
90
91         DBG("PRESENCE:publ_send200ok: send 200OK reply\n");     
92         DBG("PRESENCE:publ_send200ok: etag= %s - len= %d\n", etag.s, etag.len);
93         
94         /* ??? should we use static allocated buffer */
95         
96         hdr_append.s = buf;
97         hdr_append.s[0]='\0';
98         hdr_append.len = sprintf(hdr_append.s, "Expires: %d\r\n",lexpire -
99                         expires_offset);
100         if(hdr_append.len < 0)
101         {
102                 LOG(L_ERR, "PRESENCE:publ_send200ok: ERROR unsuccessful sprintf\n");
103                 goto error;
104         }
105         if(hdr_append.len > buf_len)
106         {
107                 LOG(L_ERR, "PRESENCE:publ_send200ok: ERROR buffer size overflown\n");
108                 goto error;
109         }
110         hdr_append.s[hdr_append.len]= '\0';
111                 
112         if (add_lump_rpl( msg, hdr_append.s, hdr_append.len, LUMP_RPL_HDR)==0 )
113         {
114                 LOG(L_ERR,"PRESENCE: publ_send200ok:ERROR unable to add lump_rl\n");
115                 goto error;
116         }
117
118         size= sizeof(char)*(20+etag.len) ;
119         hdr_append2.s = (char *)pkg_malloc(size);
120         if(hdr_append2.s == NULL)
121         {
122                 LOG(L_ERR,"PRESENCE:publ_send200ok:ERROR no more memory\n");
123                 goto error;
124         }
125         hdr_append2.s[0]='\0';
126         hdr_append2.len = sprintf(hdr_append2.s, "SIP-ETag: %s\r\n", etag.s);
127         if(hdr_append2.len < 0)
128         {
129                 LOG(L_ERR, "PRESENCE:publ_send200ok:ERROR unsuccessful sprintf\n ");
130                 goto error;
131         }
132         if(hdr_append2.len > size)
133         {
134                 LOG(L_ERR, "PRESENCE:publ_send200ok: ERROR buffer size overflown\n");
135                 goto error;
136         }
137
138         hdr_append2.s[hdr_append2.len]= '\0';
139         if (add_lump_rpl(msg, hdr_append2.s, hdr_append2.len, LUMP_RPL_HDR)==0 )
140         {
141                 LOG(L_ERR,"PRESENCE:publ_send200ok: unable to add lump_rl\n");
142                 goto error;
143         }
144
145         if( slb.reply( msg, 200, &pu_200_rpl)== -1)
146         {
147                 LOG(L_ERR,"PRESENCE: publ_send200ok: ERORR while sending reply\n");
148                 goto error;
149         }
150
151         pkg_free(hdr_append2.s);
152         return 0;
153
154 error:
155
156         if(hdr_append2.s)
157                 pkg_free(hdr_append2.s);
158
159         return 0;
160
161 }       
162 presentity_t* new_presentity( str* domain,str* user,int expires, 
163                 pres_ev_t* event, str* etag, str* sender)
164 {
165         presentity_t *presentity;
166         int size, init_len;
167         
168         /* allocating memory for presentity */
169         size = sizeof(presentity_t)+ (domain->len+ user->len+ etag->len + 50)
170                 * sizeof(char);
171         if(sender)
172                 size+= sizeof(str)+ sender->len* sizeof(char);
173         
174         init_len= size;
175
176         presentity = (presentity_t*)pkg_malloc(size);
177         if(presentity == NULL)
178         {
179                 LOG(L_ERR, "PRESENCE:new_presentity: No memory left: size=%d\n", size);
180                 return NULL;
181         }
182         memset(presentity, 0, size);
183         size= sizeof(presentity_t);
184
185         presentity->domain.s = (char*)presentity+ size;
186         strncpy(presentity->domain.s, domain->s, domain->len);
187         presentity->domain.len = domain->len;
188         size+= domain->len;     
189         
190         presentity->user.s = (char*)presentity+size;
191         strncpy(presentity->user.s, user->s, user->len);
192         presentity->user.len = user->len;
193         size+= user->len;
194
195         presentity->etag.s = (char*)presentity+ size;
196         memcpy(presentity->etag.s, etag->s, etag->len);
197         presentity->etag.s[etag->len]= '\0';
198         presentity->etag.len = etag->len;
199
200         size+= etag->len+1;
201         
202         if(sender)
203         {
204                 presentity->sender= (str*)((char*)presentity+ size);
205                 size+= sizeof(str);
206                 presentity->sender->s= (char*)presentity + size;
207                 memcpy(presentity->sender->s, sender->s, sender->len);
208                 presentity->sender->len= sender->len;
209                 size+= sender->len;
210         }
211
212         if(size> init_len)
213         {
214                 LOG(L_ERR, "PRESENCE: new_presentity: ERROR"
215                         " buffer size overflow init_len= %d, size= %d\n", init_len, size);
216                 pkg_free(presentity);
217                 return NULL;
218         }
219         DBG("PRESENCE: new_presentity:init_len= %d size= %d\n", init_len, size);
220         presentity->event= event;
221         presentity->expires = expires;
222         presentity->received_time= (int)time(NULL);
223         return presentity;
224     
225 }
226
227 int update_presentity(struct sip_msg* msg, presentity_t* presentity, str* body,
228                 int new_t)
229 {
230         db_key_t query_cols[11], update_keys[7], result_cols[5];
231         db_op_t  query_ops[11];
232         db_val_t query_vals[11], update_vals[7];
233         db_res_t *result= NULL;
234         int n_query_cols = 0;
235         int n_update_cols = 0;
236         char* dot= NULL;
237         str etag= {0, 0};
238         str cur_etag= {0, 0};
239         str* rules_doc= NULL;
240
241         if(presentity->event->req_auth)
242         {
243                 /* get rules_document */
244                 if(presentity->event->get_rules_doc(&presentity->user,
245                                         &presentity->domain, &rules_doc))
246                 {
247                         LOG(L_ERR, "PRESENCE:update_presentity: ERROR getting rules doc\n");
248                         goto error;
249                 }
250         }
251
252         query_cols[n_query_cols] = "domain";
253         query_ops[n_query_cols] = OP_EQ;
254         query_vals[n_query_cols].type = DB_STR;
255         query_vals[n_query_cols].nul = 0;
256         query_vals[n_query_cols].val.str_val = presentity->domain;
257         n_query_cols++;
258         
259         query_cols[n_query_cols] = "username";
260         query_ops[n_query_cols] = OP_EQ;
261         query_vals[n_query_cols].type = DB_STR;
262         query_vals[n_query_cols].nul = 0;
263         query_vals[n_query_cols].val.str_val = presentity->user;
264         n_query_cols++;
265
266         query_cols[n_query_cols] = "event";
267         query_ops[n_query_cols] = OP_EQ;
268         query_vals[n_query_cols].type = DB_STR;
269         query_vals[n_query_cols].nul = 0;
270         query_vals[n_query_cols].val.str_val = presentity->event->name;
271         n_query_cols++;
272
273         query_cols[n_query_cols] = "etag";
274         query_ops[n_query_cols] = OP_EQ;
275         query_vals[n_query_cols].type = DB_STR;
276         query_vals[n_query_cols].nul = 0;
277         query_vals[n_query_cols].val.str_val = presentity->etag;
278         n_query_cols++;
279
280         result_cols[0]= "expires"; 
281
282         if(new_t) 
283         {
284                 /* insert new record in hash_table */
285                 
286                 if(insert_phtable(&msg->first_line.u.request.uri, 
287                                         presentity->event->evp->parsed)< 0)
288                 {
289                         LOG(L_ERR, "PRESENCE:update_presentity: ERROR inserting record"
290                                         " in hash table\n");
291                         goto error;
292                 }
293                 
294                 /* insert new record into database */   
295                 query_cols[n_query_cols] = "expires";
296                 query_vals[n_query_cols].type = DB_INT;
297                 query_vals[n_query_cols].nul = 0;
298                 query_vals[n_query_cols].val.int_val = presentity->expires+
299                                 (int)time(NULL);
300                 n_query_cols++;
301
302                 query_cols[n_query_cols] = "body";
303                 query_vals[n_query_cols].type = DB_BLOB;
304                 query_vals[n_query_cols].nul = 0;
305                 query_vals[n_query_cols].val.str_val = *body;
306                 n_query_cols++;
307                 
308                 query_cols[n_query_cols] = "received_time";
309                 query_vals[n_query_cols].type = DB_INT;
310                 query_vals[n_query_cols].nul = 0;
311                 query_vals[n_query_cols].val.int_val = presentity->received_time;
312                 n_query_cols++;
313
314                 if (pa_dbf.use_table(pa_db, presentity_table) < 0) 
315                 {
316                         LOG(L_ERR, "PRESENCE:update_presentity: Error in use_table\n");
317                         goto error;
318                 }
319
320                 DBG( "PRESENCE:update_presentity: inserting %d cols into"
321                                 " table\n",
322                                 n_query_cols);
323                                 
324                 if (pa_dbf.insert(pa_db, query_cols, query_vals, n_query_cols) < 0) 
325                 {
326                         LOG(L_ERR, "PRESENCE:update_presentity: Error while"
327                                         " inserting new presentity\n");
328                         goto error;
329                 }
330                 if( publ_send200ok(msg, presentity->expires, presentity->etag)< 0)
331                 {
332                         LOG(L_ERR, "PRESENCE:update_presentity: ERROR while sending 200OK\n");
333                         return -1;
334                 }
335                 goto send_notify;
336         }
337         else
338         {
339                 if (pa_dbf.use_table(pa_db, presentity_table) < 0) 
340                 {
341                         LOG(L_ERR, "PRESENCE:update_presentity: Error in use_table\n");
342                         goto error;
343                 }
344
345                 DBG("PRESENCE:update_presentity: querying presentity  \n");
346                 if (pa_dbf.query (pa_db, query_cols, query_ops, query_vals,
347                          result_cols, n_query_cols, 1, 0, &result) < 0) 
348                 {
349                         LOG(L_ERR, "PRESENCE:update_presentity: Error while querying"
350                                         " presentity\n");
351                         goto error;
352                 }
353                 if(result== NULL)
354                         goto error;
355
356                 if (result->n > 0)
357                 {
358                         pa_dbf.free_result(pa_db, result);
359                         result= NULL;
360
361                         if(presentity->expires == 0) 
362                         {
363                                 if( publ_send200ok(msg, presentity->expires, presentity->etag)< 0)
364                                 {
365                                         LOG(L_ERR, "PRESENCE:update_presentity: ERROR while sending 200OK\n");
366                                         return -1;
367                                 }
368                                 if( publ_notify( presentity, body, &presentity->etag, rules_doc)< 0 )
369                                 {
370                                         LOG(L_ERR,"PRESENCE:update_presentity: ERROR while sending notify\n");
371                                         return -1;
372                                 }
373                                 if (pa_dbf.use_table(pa_db, presentity_table) < 0) 
374                                 {
375                                         LOG(L_ERR, "PRESENCE:update_presentity: Error in use_table\n");
376                                         goto error;
377                                 }
378                                 DBG("PRESENCE:update_presentity: expires =0 -> deleting"
379                                         " from database\n");
380                                 if(pa_dbf.delete(pa_db, query_cols, 0 ,query_vals,n_query_cols)< 0 )
381                                 {
382                                         LOG(L_ERR,"PRESENCE:update_presentity:ERROR deleting"
383                                                 " expired record from presentity database table\n");
384                                         goto error;
385                                 }
386                                 DBG("PRESENCE:update_presentity:delete from db %.*s\n",
387                                         presentity->user.len,presentity->user.s );
388
389                                 /* delete from hash table */
390         
391                                 if(delete_phtable(&msg->first_line.u.request.uri, 
392                                                 presentity->event->evp->parsed)< 0)
393                                 {
394                                         LOG(L_ERR, "PRESENCE:update_presentity: ERROR deleting record"
395                                                 " from hash table\n");
396                                         goto error;
397                                 }
398                                 return 1;
399                         }
400
401                         n_update_cols= 0;
402                         if(presentity->event->etag_not_new== 0)
403                         {       
404                                 /* generate another etag */
405                                 unsigned int publ_nr;
406                                 str str_publ_nr= {0, 0};
407
408                                 dot= presentity->etag.s+ presentity->etag.len;
409                                 while(*dot!= '.' && str_publ_nr.len< presentity->etag.len)
410                                 {
411                                         str_publ_nr.len++;
412                                         dot--;
413                                 }
414                                 if(str_publ_nr.len== presentity->etag.len)
415                                 {
416                                         LOG(L_ERR, "PRESENCE:update_presentity: ERROR wrong etag\n");
417                                         return -1;                      
418                                 }       
419                                 str_publ_nr.s= dot+1;
420                                 str_publ_nr.len--;
421         
422                                 if( str2int(&str_publ_nr, &publ_nr)< 0)
423                                 {
424                                         LOG(L_ERR, "PRESENCE: update_presentity: ERROR while converting string to int\n");
425                                         goto error;
426                                 }
427                                 etag.s = generate_ETag(publ_nr+1);
428                                 if(etag.s == NULL)
429                                 {
430                                         LOG(L_ERR,"PRESENCE:update_presentity: ERROR while generating etag\n");
431                                         return -1;
432                                 }
433                                 etag.len=(strlen(etag.s));
434                                 DBG("PRESENCE:update_presentity: new etag  = %.*s \n", etag.len,
435                                         etag.s);
436                                 
437                                 cur_etag= etag;
438
439                                 update_keys[n_update_cols] = "etag";
440                                 update_vals[n_update_cols].type = DB_STR;
441                                 update_vals[n_update_cols].nul = 0;
442                                 update_vals[n_update_cols].val.str_val = etag;
443                                 n_update_cols++;
444
445                         }
446                         else
447                                 cur_etag= presentity->etag;
448                         
449                         update_keys[n_update_cols] = "expires";
450                         update_vals[n_update_cols].type = DB_INT;
451                         update_vals[n_update_cols].nul = 0;
452                         update_vals[n_update_cols].val.int_val = presentity->expires + (int)time(NULL);
453                         n_update_cols++;
454
455                         update_keys[n_update_cols] = "received_time";
456                         update_vals[n_update_cols].type = DB_INT;
457                         update_vals[n_update_cols].nul = 0;
458                         update_vals[n_update_cols].val.int_val = presentity->received_time;
459                         n_update_cols++;
460
461                         if(body && body->s)
462                         {
463                                 update_keys[n_update_cols] = "body";
464                                 update_vals[n_update_cols].type = DB_BLOB;
465                                 update_vals[n_update_cols].nul = 0;
466                                 update_vals[n_update_cols].val.str_val.s = body->s;
467                                 update_vals[n_update_cols].val.str_val.len=body->len;
468                                 n_update_cols++;
469                         }
470
471                         if( pa_dbf.update( pa_db,query_cols, query_ops, query_vals,
472                         update_keys,update_vals, n_query_cols, n_update_cols )<0) 
473                         {
474                                 LOG( L_ERR , "PRESENCE:update_presentity: ERROR while"
475                                                 " updating presence information\n");
476                                 goto error;
477                         }
478                         
479                         /* send 200OK */
480                         if( publ_send200ok(msg, presentity->expires, cur_etag)< 0)
481                         {
482                                 LOG(L_ERR, "PRESENCE:update_presentity: ERROR while sending 200OK\n");
483                                 if(etag.s)
484                                         pkg_free(etag.s);
485                                 return -1;
486                         }
487                         if(etag.s)
488                                 pkg_free(etag.s);
489                         etag.s= NULL;
490                         
491                         if(!body)
492                                 return 0;
493                 
494                         goto send_notify;
495                 }  
496                 else  /* if there isn't no registration with those 3 values */
497                 {
498                         pa_dbf.free_result(pa_db, result);
499                         result= NULL;
500                         LOG(L_DBG, "PRESENCE:update_presentity: No E_Tag match\n");
501                         if (slb.reply(msg, 412, &pu_412_rpl) == -1)
502                         {
503                                 LOG(L_ERR, "PRESENCE:PRESENCE:update_presentity: ERROR while sending"
504                                         "reply\n");
505                                 goto error;
506                         }
507                 }
508         }
509
510 send_notify:
511                         
512         /* send notify with presence information */
513         if (publ_notify(presentity, body, NULL, rules_doc)<0)
514         {
515                 LOG(L_ERR,"PRESENCE:update_presentity: ERROR while sending notify\n");
516                 return -1;
517         }
518
519         if(rules_doc)
520         {
521                 if(rules_doc->s)
522                         pkg_free(rules_doc->s);
523                 pkg_free(rules_doc);
524         }
525
526         return 0;
527
528 error:
529         LOG(L_ERR, "PRESENCE:update_presentity: ERROR occured\n");
530         if(result)
531                 pa_dbf.free_result(pa_db, result);
532         if(etag.s)
533                 pkg_free(etag.s);
534         if(rules_doc)
535         {
536                 if(rules_doc->s)
537                         pkg_free(rules_doc->s);
538                 pkg_free(rules_doc);
539         }
540         return -1;
541
542 }
543
544 int pres_htable_restore(void)
545 {
546         /* query all records from presentity table and insert records 
547          * in presentity table */
548         db_key_t result_cols[5];
549         db_res_t *result= NULL;
550         db_row_t *row= NULL ;   
551         db_val_t *row_vals;
552         int  i;
553         str user, domain, ev_str, uri;
554         int n_result_cols= 0;
555         int user_col, domain_col, event_col, expires_col;
556         int event;
557         event_t e;
558
559         result_cols[user_col= n_result_cols++]= "username";
560         result_cols[domain_col= n_result_cols++]= "domain";
561         result_cols[event_col= n_result_cols++]= "event";
562         result_cols[expires_col= n_result_cols++]= "expires";
563
564         if (pa_dbf.use_table(pa_db, presentity_table) < 0) 
565         {
566                 LOG(L_ERR, "PRESENCE:update_presentity: Error in use_table\n");
567                 goto error;
568         }
569
570         if (pa_dbf.query (pa_db, 0, 0, 0,result_cols,0, n_result_cols,
571                                 "username", &result) < 0)
572         {
573                 LOG(L_ERR, "PRESENCE:pres_htable_restore: Error while querying"
574                                 " presentity\n");
575                 goto error;
576         }
577         if(result== NULL)
578                 goto error;
579
580         if(result->n<= 0)
581         {
582                 pa_dbf.free_result(pa_db, result);
583                 return 0;
584         }
585                 
586         for(i= 0; i< result->n; i++)
587         {
588                 row = &result->rows[i];
589                 row_vals = ROW_VALUES(row);
590
591                 if(row_vals[expires_col].val.int_val< (int)time(NULL))
592                         continue;
593
594                 user.s= (char*)row_vals[user_col].val.string_val;
595                 user.len= strlen(user.s);
596                 domain.s= (char*)row_vals[domain_col].val.string_val;
597                 domain.len= strlen(domain.s);
598                 ev_str.s= (char*)row_vals[event_col].val.string_val;
599                 ev_str.len= strlen(ev_str.s);
600         
601                 if(event_parser(ev_str.s, ev_str.len, &e)< 0)
602                 {
603                         LOG(L_ERR, "PRESENCE:pres_htable_restore: ERROR parsing event\n");
604                         goto error;
605                 }
606                 event= e.parsed;
607
608                 if(uandd_to_uri(user, domain, &uri)< 0)
609                 {
610                         LOG(L_ERR,"PRESENCE:pres_htable_restore:ERROR constructing uri\n");
611                         goto error;
612                 }
613                 /* insert in hash_table*/
614                 if(insert_phtable(&uri, event)< 0)
615                 {
616                         LOG(L_ERR, "PRESENCE:pres_htable_restore: ERROR "
617                                 "inserting record in presentity hash table");
618                         pkg_free(uri.s);
619                         goto error;
620                 }
621                 pkg_free(uri.s);
622         }
623         pa_dbf.free_result(pa_db, result);
624
625         return 0;
626
627 error:
628         if(result)
629                 pa_dbf.free_result(pa_db, result);
630         return -1;      
631 }