dlg_state.c

Go to the documentation of this file.
00001 
00055 #include <time.h>
00056 
00057 #include "dlg_state.h"
00058 
00059 #include "../../mem/shm_mem.h"
00060 #include "../sl/sl_funcs.h"
00061 
00062 #include "sip.h"
00063 #include "release_call.h"
00064 #include "ims_pm.h"
00065 
00066 int p_dialogs_hash_size;                    
00067 p_dialog_hash_slot *p_dialogs=0;            
00069 extern int pcscf_dialogs_expiration_time;   
00070 extern int pcscf_dialogs_enable_release;    
00071 time_t d_time_now;                          
00073 extern str pcscf_record_route_mo;           
00074 extern str pcscf_record_route_mo_uri;       
00075 extern str pcscf_record_route_mt;           
00076 extern str pcscf_record_route_mt_uri;       
00078 extern str pcscf_name_str;                  
00080 extern struct tm_binds tmb;
00081 extern int pcscf_min_se;
00082 
00083 
00084 int (*sl_reply)(struct sip_msg* _msg, char* _str1, char* _str2); 
00085 int supports_extension(struct sip_msg *m, str *extension);
00086 int requires_extension(struct sip_msg *m, str *extension);
00087 
00088 #define strtotime(src,dest) \
00089 {\
00090     int i;\
00091     (dest)=0;\
00092     for(i=0;i<(src).len;i++)\
00093         if ((src).s[i]>='0' && (src).s[i]<='9')\
00094             (dest) = (dest)*10 + (src).s[i] -'0';\
00095 }
00096 
00097 
00098 #define FParam_INT(val) { \
00099      .v = { \
00100         .i = val \
00101      },\
00102     .type = FPARAM_INT, \
00103     .orig = "int_value", \
00104 };
00105 
00106 #define FParam_STRING(val) { \
00107     .v = { \
00108         .str = STR_STATIC_INIT(val) \
00109     },\
00110     .type = FPARAM_STR, \
00111     .orig = val, \
00112 };
00113 
00114 
00120 inline unsigned int get_p_dialog_hash(str call_id)
00121 {
00122     if (call_id.len==0) return 0;
00123 #define h_inc h+=v^(v>>3)
00124    char* p;
00125    register unsigned v;
00126    register unsigned h;
00127 
00128    h=0;
00129    for (p=call_id.s; p<=(call_id.s+call_id.len-4); p+=4){
00130        v=(*p<<24)+(p[1]<<16)+(p[2]<<8)+p[3];
00131        h_inc;
00132    }
00133    v=0;
00134    for (;p<(call_id.s+call_id.len); p++) {
00135        v<<=8;
00136        v+=*p;
00137    }
00138    h_inc;
00139 
00140    h=((h)+(h>>11))+((h>>13)+(h>>23));
00141    return (h)%p_dialogs_hash_size;
00142 #undef h_inc 
00143 }
00144 
00150 int p_dialogs_init(int hash_size)
00151 {
00152     int i;
00153     
00154     p_dialogs_hash_size = hash_size;
00155     p_dialogs = shm_malloc(sizeof(p_dialog_hash_slot)*p_dialogs_hash_size);
00156 
00157     if (!p_dialogs) return 0;
00158 
00159     memset(p_dialogs,0,sizeof(p_dialog_hash_slot)*p_dialogs_hash_size);
00160     
00161     for(i=0;i<p_dialogs_hash_size;i++){
00162         p_dialogs[i].lock = lock_alloc();
00163         if (!p_dialogs[i].lock){
00164             LOG(L_ERR,"ERR:"M_NAME":d_hash_table_init(): Error creating lock\n");
00165             return 0;
00166         }
00167         p_dialogs[i].lock = lock_init(p_dialogs[i].lock);
00168     }
00169             
00170     return 1;
00171 }
00172 
00176 void p_dialogs_destroy()
00177 {
00178     int i;
00179     p_dialog *d,*nd;
00180     for(i=0;i<p_dialogs_hash_size;i++){
00181         d_lock(i);
00182             d = p_dialogs[i].head;
00183             while(d){
00184                 nd = d->next;
00185                 free_p_dialog(d);
00186                 d = nd;
00187             }
00188         d_unlock(i);
00189         lock_dealloc(p_dialogs[i].lock);
00190     }
00191     shm_free(p_dialogs);
00192 }
00193 
00198 inline void d_lock(unsigned int hash)
00199 {
00200 //  LOG(L_CRIT,"GET %d\n",hash);
00201     lock_get(p_dialogs[(hash)].lock);
00202 //  LOG(L_CRIT,"GOT %d\n",hash);    
00203 }
00204 
00209  inline void d_unlock(unsigned int hash)
00210 {
00211     lock_release(p_dialogs[(hash)].lock);
00212 //  LOG(L_CRIT,"RELEASED %d\n",hash);   
00213 }
00214 
00219 inline int d_act_time()
00220 {
00221     d_time_now=time(0);
00222     return d_time_now;
00223 }
00224 
00225 extern int* pcscf_dialog_count;
00226 extern int pcscf_max_dialog_count;
00227 extern gen_lock_t* pcscf_dialog_count_lock;
00228 
00232 inline void p_dialog_count_lock()
00233 {
00234     lock_get(pcscf_dialog_count_lock);
00235 }
00236 
00240 inline void p_dialog_count_unlock()
00241 {
00242         lock_release(pcscf_dialog_count_lock);
00243 }
00244 
00245 
00250 inline int p_dialog_count_increment ()
00251 {
00252     if (pcscf_max_dialog_count<0) return 1;
00253     p_dialog_count_lock();  
00254     if (*pcscf_dialog_count<pcscf_max_dialog_count){
00255         (*pcscf_dialog_count)++;
00256         p_dialog_count_unlock();
00257         return 1;
00258     } else {
00259         p_dialog_count_unlock();
00260         return 0;
00261     }
00262     LOG(L_DBG,"DBG:"M_NAME":p_dialog_count_increment(): P-CSCF Dialog counter value is %d\n", *pcscf_dialog_count);
00263 }
00264 
00268 inline void p_dialog_count_decrement()
00269 {
00270     if (pcscf_max_dialog_count<0) return ;
00271     p_dialog_count_lock();
00272     (*pcscf_dialog_count)--;
00273     p_dialog_count_unlock();
00274     LOG(L_DBG,"DBG:"M_NAME":p_dialog_count_decrement(): P-CSCF Dialog counter value is %d\n", *pcscf_dialog_count);    
00275 }
00276 
00277 
00287 p_dialog* new_p_dialog(str call_id,str host,int port, int transport)
00288 {
00289     p_dialog *d;
00290     
00291     if (!p_dialog_count_increment()) return 0;
00292     d = shm_malloc(sizeof(p_dialog));
00293     if (!d) {
00294         LOG(L_ERR,"ERR:"M_NAME":new_p_dialog(): Unable to alloc %d bytes\n",
00295             sizeof(p_dialog));
00296         goto error;
00297     }
00298     memset(d,0,sizeof(p_dialog));
00299     
00300     d->hash = get_p_dialog_hash(call_id);       
00301     STR_SHM_DUP(d->call_id,call_id,"shm");
00302     STR_SHM_DUP(d->host,host,"shm");    
00303     d->port = port;
00304     d->transport = transport;
00305                 
00306     return d;
00307 error:
00308 out_of_memory:
00309     if (d){
00310         if (d->call_id.s) shm_free(d->call_id.s);
00311         if (d->host.s) shm_free(d->host.s);
00312         shm_free(d);        
00313     }
00314     p_dialog_count_decrement();
00315     return 0;
00316 }
00317 
00328 p_dialog* add_p_dialog(str call_id,str host,int port, int transport)
00329 {
00330     p_dialog *d;
00331     
00332     d = new_p_dialog(call_id,host,port,transport);
00333     if (!d) return 0;       
00334     
00335     d_lock(d->hash);
00336         d->next = 0;
00337         d->prev = p_dialogs[d->hash].tail;
00338         if (d->prev) d->prev->next = d;
00339         p_dialogs[d->hash].tail = d;
00340         if (!p_dialogs[d->hash].head) p_dialogs[d->hash].head = d;
00341 
00342         return d;
00343 }
00344 
00355 int is_p_dialog(str call_id,str host,int port, int transport,enum p_dialog_direction *dir)
00356 {
00357     p_dialog *d=0;
00358     unsigned int hash = get_p_dialog_hash(call_id);
00359 
00360     d_lock(hash);
00361         d = p_dialogs[hash].head;
00362         while(d){
00363             if ((!dir || d->direction == *dir) &&
00364                 d->port == port &&
00365 /*              d->transport == transport &&*/
00366 /* commented because of strange behaviour */
00367                 d->host.len == host.len &&
00368                 d->call_id.len == call_id.len &&
00369                 strncasecmp(d->host.s,host.s,host.len)==0 &&
00370                 strncasecmp(d->call_id.s,call_id.s,call_id.len)==0) {
00371                     d_unlock(hash);
00372                     return 1;
00373                 }
00374             d = d->next;
00375         }
00376     d_unlock(hash);
00377     return 0;
00378 }
00379 
00387 int is_p_dialog_dir(str call_id,enum p_dialog_direction dir)
00388 {
00389     p_dialog *d=0;
00390     unsigned int hash = get_p_dialog_hash(call_id);
00391         d_lock(hash);
00392         d = p_dialogs[hash].head;
00393         while(d){
00394             if (d->direction==dir && d->call_id.len == call_id.len &&
00395                         strncasecmp(d->call_id.s,call_id.s,call_id.len)==0) {
00396                 d_unlock(hash);
00397                 return 1;
00398             }
00399             d=d->next;
00400         }
00401         d_unlock(hash);
00402         return 0;
00403 }
00404 
00415 p_dialog* get_p_dialog(str call_id,str host,int port, int transport,enum p_dialog_direction *dir)
00416 {
00417     p_dialog *d=0;
00418     unsigned int hash = get_p_dialog_hash(call_id);
00419 
00420     d_lock(hash);
00421         d = p_dialogs[hash].head;
00422         while(d){
00423             if ((!dir || d->direction == *dir) &&
00424                 d->port == port &&
00425 /*              d->transport == transport &&*/
00426 /* commented because of strange behaviour */
00427                 d->host.len == host.len &&
00428                 d->call_id.len == call_id.len &&
00429                 strncasecmp(d->host.s,host.s,host.len)==0 &&
00430                 strncasecmp(d->call_id.s,call_id.s,call_id.len)==0) {
00431                     return d;
00432                 }
00433             d = d->next;
00434         }
00435     d_unlock(hash);
00436     return 0;
00437 }
00438 
00445 p_dialog* get_p_dialog_dir(str call_id,enum p_dialog_direction dir)
00446 {
00447     p_dialog *d=0;
00448     unsigned int hash = get_p_dialog_hash(call_id);
00449 
00450     d_lock(hash);
00451         d = p_dialogs[hash].head;
00452         while(d){
00453             if (d->direction == dir &&
00454                 d->call_id.len == call_id.len &&
00455                 strncasecmp(d->call_id.s,call_id.s,call_id.len)==0) {
00456                     return d;
00457                 }
00458             d = d->next;
00459         }
00460     d_unlock(hash);
00461     return 0;
00462 }
00463 
00464 
00472 p_dialog* get_p_dialog_dir_nolock(str call_id,enum p_dialog_direction dir)
00473 {
00474     p_dialog *d=0;
00475     unsigned int hash = get_p_dialog_hash(call_id);
00476 
00477     d = p_dialogs[hash].head;
00478     while(d){
00479         if (d->direction == dir &&
00480             d->call_id.len == call_id.len &&
00481             strncasecmp(d->call_id.s,call_id.s,call_id.len)==0) {
00482                 return d;
00483             }
00484         d = d->next;
00485     }
00486     return 0;
00487 }
00488 
00489 str reason_terminate_p_dialog_s={"Session terminated in the P-CSCF",32};
00496 int terminate_p_dialog(p_dialog *d)
00497 {
00498     if (!pcscf_dialogs_enable_release) return 0;
00499     switch (d->method){
00500         case DLG_METHOD_INVITE:
00501             if (release_call_p(d,503,reason_terminate_p_dialog_s)==-1){             
00502                 del_p_dialog(d);
00503             }
00504             return 1;
00505             break;
00506         case DLG_METHOD_SUBSCRIBE:
00507             LOG(L_ERR,"ERR:"M_NAME":terminate_p_dialog(): Not needed for SUBSCRIBE dialogs - silent drop on expiration.\n");
00508             return 0;
00509             break;
00510         default:
00511             LOG(L_ERR,"ERR:"M_NAME":terminate_p_dialog(): Not implemented yet for method[%d]!\n",d->method);
00512             return 0;
00513     }
00514 }
00515 
00521 void del_p_dialog(p_dialog *d)
00522 {
00523     if (d->prev) d->prev->next = d->next;
00524     else p_dialogs[d->hash].head = d->next;
00525     if (d->next) d->next->prev = d->prev;
00526     else p_dialogs[d->hash].tail = d->prev;
00527     free_p_dialog(d);
00528 }
00529 
00534 void free_p_dialog(p_dialog *d)
00535 {
00536     int i;
00537     if (!d) return;
00538     if (d->call_id.s) shm_free(d->call_id.s);
00539     if (d->host.s) shm_free(d->host.s); 
00540     if (d->method_str.s) shm_free(d->method_str.s); 
00541     if (d->routes){
00542         for(i=0;i<d->routes_cnt;i++)
00543             shm_free(d->routes[i].s);
00544         shm_free(d->routes);
00545     }
00546     if (d->dialog_s) tmb.free_dlg(d->dialog_s);
00547     if (d->dialog_c) tmb.free_dlg(d->dialog_c);
00548     if (d->refresher.s) shm_free(d->refresher.s);
00549     shm_free(d);
00550     p_dialog_count_decrement(); 
00551 }
00552 
00557 void print_p_dialogs(int log_level)
00558 {
00559     p_dialog *d;
00560     int i,j;
00561     if (debug<log_level) return; /* to avoid useless calls when nothing will be printed */
00562     d_act_time();
00563     LOG(log_level,"INF:"M_NAME":----------  P-CSCF Dialog List begin --------------\n");
00564     for(i=0;i<p_dialogs_hash_size;i++){
00565         d_lock(i);
00566             d = p_dialogs[i].head;
00567             while(d){
00568                 LOG(log_level,"INF:"M_NAME":[%4d] Dir:["ANSI_MAGENTA"%d"ANSI_GREEN
00569                     "]\tCall-ID:<"ANSI_BLUE"%.*s"ANSI_GREEN
00570                     ">\tAOR:"ANSI_RED"%d://%.*s:%d"ANSI_GREEN"\n"
00571                     ,i,             
00572                     d->direction,
00573                     d->call_id.len,d->call_id.s,
00574                     d->transport,d->host.len,d->host.s,d->port);
00575                 LOG(log_level,"INF:"M_NAME":\t\tMethod:["ANSI_MAGENTA"%d"ANSI_GREEN
00576                     "] State:["ANSI_MAGENTA"%d"ANSI_GREEN
00577                     "] Exp:["ANSI_MAGENTA"%4d"ANSI_GREEN"]\n",
00578                     d->method,d->state,
00579                     (int)(d->expires - d_time_now));    
00580                 for(j=0;j<d->routes_cnt;j++)
00581                     LOG(log_level,"INF:"M_NAME":\t\t RR: <"ANSI_YELLOW"%.*s"ANSI_GREEN">\n",            
00582                         d->routes[j].len,d->routes[j].s);                   
00583                 d = d->next;
00584             }       
00585         d_unlock(i);
00586     }
00587     LOG(log_level,"INF:"M_NAME":----------  P-CSCF Dialog List end   --------------\n");    
00588 }
00589 
00595 static inline enum p_dialog_direction get_dialog_direction(char *direction)
00596 {
00597     if (!direction) {
00598         LOG(L_CRIT,"ERR:"M_NAME":get_dialog_direction(): Unknown direction NULL");
00599         return DLG_MOBILE_UNKNOWN;
00600     }
00601     switch(direction[0]){
00602         case 'o':
00603         case 'O':
00604         case '0':
00605             return DLG_MOBILE_ORIGINATING;
00606         case 't':
00607         case 'T':
00608         case '1':
00609             return DLG_MOBILE_TERMINATING;
00610         default:
00611             LOG(L_CRIT,"ERR:"M_NAME":get_dialog_direction(): Unknown direction %s",direction);
00612             return DLG_MOBILE_UNKNOWN;
00613     }
00614 }
00615 
00625 static inline int find_dialog_contact(struct sip_msg *msg,enum p_dialog_direction dir,str *host,int *port,int *transport)
00626 {
00627     switch(dir){
00628         case DLG_MOBILE_ORIGINATING:
00629             if (!cscf_get_originating_contact(msg,host,port,transport))
00630                 return 0;
00631             if (*port==0) *port = 5060;
00632             return 1;
00633         case DLG_MOBILE_TERMINATING:
00634             if (!cscf_get_terminating_contact(msg,host,port,transport))
00635                 return 0;
00636             if (*port==0) *port = 5060;
00637             return 1;
00638         default:
00639             LOG(L_CRIT,"ERR:"M_NAME":find_dialog_contact(): Unknown direction %d",dir);
00640             return 0;
00641     }
00642     return 1;
00643 }
00644 
00652 int P_is_in_dialog(struct sip_msg* msg, char* str1, char* str2)
00653 {
00654     str call_id;
00655     str host;
00656     int port,transport;
00657     enum p_dialog_direction dir;
00658 
00659     dir = get_dialog_direction(str1);
00660     
00661     if (!find_dialog_contact(msg,dir,&host,&port,&transport)){
00662         LOG(L_ERR,"ERR:"M_NAME":P_is_in_dialog(%s): Error retrieving %s contact\n",str1,str1);
00663         return CSCF_RETURN_BREAK;
00664     }       
00665 
00666     //print_p_dialog(L_ERR);
00667     call_id = cscf_get_call_id(msg,0);
00668     if (!call_id.len)
00669         return CSCF_RETURN_FALSE;
00670             
00671     if (is_p_dialog(call_id,host,port,transport,0)) {
00672         return CSCF_RETURN_TRUE;
00673     }
00674     else 
00675         return CSCF_RETURN_FALSE;
00676 }
00677 
00678 str s_OTHER={"<OTHER>",7};
00679 str s_INVITE={"INVITE",6};
00680 str s_SUBSCRIBE={"SUBSCRIBE",9};
00686 static enum p_dialog_method get_dialog_method(str method)
00687 {
00688     if (method.len == s_INVITE.len &&
00689         strncasecmp(method.s,s_INVITE.s,s_INVITE.len)==0) return DLG_METHOD_INVITE;
00690     if (method.len == s_SUBSCRIBE.len &&
00691         strncasecmp(method.s,s_SUBSCRIBE.s,s_SUBSCRIBE.len)==0) return DLG_METHOD_SUBSCRIBE;
00692     return DLG_METHOD_OTHER;
00693 }
00694 
00695 #ifdef WITH_IMS_PM
00696 
00701 static str get_dialog_method_str(enum p_dialog_method method)
00702 {
00703     switch(method){
00704         case DLG_METHOD_INVITE:
00705             return s_INVITE;
00706         case DLG_METHOD_SUBSCRIBE:
00707             return s_SUBSCRIBE;
00708         default:
00709             return s_OTHER;
00710     }   
00711 }
00712 #endif
00713 
00714 static fparam_t fp_422 = FParam_INT(422);
00715 static fparam_t fp_se_small = FParam_STRING("Session Interval Too Small");
00716 
00724 int P_422_session_expires(struct sip_msg* msg, char* str1, char* str2)
00725 {
00726     str hdr = {pkg_malloc(32), 0};
00727     
00728     if (!hdr.s) {
00729         LOG(L_ERR, "ERR:"M_NAME":P_422_session_expires(): no memory for hdr\n");
00730         goto error;
00731     }
00732 
00733     hdr.len = snprintf(hdr.s, 31, "Min-SE: %d\r\n", pcscf_min_se);
00734 
00735     if (!cscf_add_header_rpl(msg, &hdr)) {
00736         LOG(L_ERR, "ERR:"M_NAME":P_422_session_expires(): Can't add header\n");
00737         goto error;
00738     }
00739     
00740     return sl_reply(msg, (char *)&fp_422, (char *)&fp_se_small);
00741 
00742 error:
00743     if (hdr.s) pkg_free(hdr.s);
00744     return CSCF_RETURN_FALSE;
00745 }
00746 
00747 
00748 static str s_refresher = {"refresher=", 10};
00749 static str str_ext_timer = {"timer", 5};
00750 static str str_min_se = {"Min-SE:",7};
00751 static str str_se = {"Session-Expires:",16}; 
00752 static str str_require = {"Require:",8}; 
00753 
00761 int P_check_session_expires(struct sip_msg* msg, char* str1, char* str2)
00762 {
00763     time_t t_time;
00764     time_t min_se_time = 0;
00765     str ses_exp = {0,0};
00766     str min_se = {0,0};
00767     str new_min_se = {0,0};
00768     str new_ses_exp = {0,0};
00769     struct hdr_field *h_se, *h_min_se;
00770     str refresher;
00771 
00772     ses_exp = cscf_get_session_expires_body(msg, &h_se);
00773     t_time = cscf_get_session_expires(ses_exp, &refresher);
00774     
00775     if (!t_time || t_time >= pcscf_min_se)
00776         return CSCF_RETURN_TRUE;
00777     if (!supports_extension(msg, &str_ext_timer)) //does not suports timer extension
00778     {
00779         //add Min-SE header with its minimum interval
00780         min_se = cscf_get_min_se(msg, &h_min_se);
00781         if (min_se.len) {
00782             strtotime(min_se, min_se_time);
00783             if (min_se_time < pcscf_min_se)
00784                 cscf_del_header(msg, h_min_se);
00785             else
00786                 return CSCF_RETURN_TRUE;
00787         }
00788         new_min_se.len = 11/*int value*/ + str_min_se.len+3;
00789         new_min_se.s = pkg_malloc(new_min_se.len+1);
00790         if (!new_min_se.s) {
00791             LOG(L_ERR,"ERR:"M_NAME":P_check_session_expires: Error allocating %d bytes\n",new_min_se.len);
00792             goto error;
00793         }
00794         new_min_se.len = snprintf(new_min_se.s, new_min_se.len, "%.*s %d\r\n",str_min_se.len, str_min_se.s, pcscf_min_se);
00795         min_se_time = pcscf_min_se;
00796         cscf_add_header(msg, &new_min_se, HDR_OTHER_T);
00797         if (t_time < pcscf_min_se) {
00798             cscf_del_header(msg, h_se);
00799             new_ses_exp.len = 11 + str_se.len+3;
00800             new_ses_exp.s = pkg_malloc(new_ses_exp.len+1);
00801             if (!new_ses_exp.s) {
00802                 LOG(L_ERR,"ERR:"M_NAME":P_check_session_expires: Error allocating %d bytes\n",new_ses_exp.len);
00803                 goto error;
00804             }
00805             new_ses_exp.len = snprintf(new_ses_exp.s, new_ses_exp.len, "%.*s %d\r\n",str_se.len, str_se.s, pcscf_min_se);
00806             t_time = pcscf_min_se;
00807             cscf_add_header(msg, &new_ses_exp, HDR_OTHER_T);
00808         }
00809         return CSCF_RETURN_TRUE;
00810     }
00811 error:
00812     if (new_min_se.s) pkg_free(new_min_se.s);
00813     if (new_ses_exp.s) pkg_free(new_ses_exp.s);
00814     return CSCF_RETURN_FALSE;
00815 }       
00816 
00817 
00825 int P_save_dialog(struct sip_msg* msg, char* str1, char* str2)
00826 {
00827     str call_id;
00828     p_dialog *d;
00829     str host;
00830     time_t t_time;
00831     str ses_exp = {0,0};
00832     str refresher = {0,0};
00833     int port,transport;
00834     char buf1[256],buf2[256];
00835     str tag,ruri,uri,x;
00836     struct hdr_field *h;
00837     unsigned int hash;
00838     enum p_dialog_direction dir;
00839     
00840     dir = get_dialog_direction(str1);
00841     
00842     if (!find_dialog_contact(msg,dir,&host,&port,&transport)){
00843         LOG(L_ERR,"ERR:"M_NAME":P_is_in_dialog(): Error retrieving %s contact\n",str1);
00844         return CSCF_RETURN_BREAK;
00845     }       
00846         
00847     call_id = cscf_get_call_id(msg,0);
00848     if (!call_id.len)
00849         return CSCF_RETURN_FALSE;
00850 
00851     LOG(L_DBG,"DBG:"M_NAME":P_save_dialog(%s): Call-ID <%.*s>\n",str1,call_id.len,call_id.s);
00852 
00853     if (is_p_dialog(call_id,host,port,transport,&dir)){
00854         LOG(L_ERR,"ERR:"M_NAME":P_save_dialog: dialog already exists!\n");  
00855         return CSCF_RETURN_TRUE;
00856     }
00857     
00858     d = add_p_dialog(call_id,host,port,transport);
00859     if (!d) return CSCF_RETURN_FALSE;
00860 
00861     d->method = get_dialog_method(msg->first_line.u.request.method);
00862     STR_SHM_DUP(d->method_str,msg->first_line.u.request.method,"shm");
00863     d->first_cseq = cscf_get_cseq(msg,0);
00864     d->last_cseq = d->first_cseq;
00865     d->state = DLG_STATE_INITIAL;
00866 
00867     d->uac_supp_timer = supports_extension(msg, &str_ext_timer);
00868 
00869     ses_exp = cscf_get_session_expires_body(msg, &h);
00870     t_time = cscf_get_session_expires(ses_exp, &refresher);
00871     if (!t_time){
00872         d->expires = d_act_time() + 60;
00873         d->lr_session_expires = 0;
00874     }else {
00875         d->expires = d_act_time() + t_time;
00876         d->lr_session_expires = t_time;
00877         if (refresher.len)
00878             STR_SHM_DUP(d->refresher, refresher, "DIALOG_REFRESHER");
00879     }
00880     
00881     d->direction=dir;
00882                 
00883     cscf_get_from_tag(msg,&tag);
00884     cscf_get_from_uri(msg,&x);
00885     uri.len = snprintf(buf1,256,"<%.*s>",x.len,x.s);
00886     uri.s = buf1;   
00887     cscf_get_to_uri(msg,&x);
00888     ruri.len = snprintf(buf2,256,"<%.*s>",x.len,x.s);
00889     ruri.s = buf2;
00890         
00891     tmb.new_dlg_uac(&call_id,
00892                     &tag,
00893                     d->first_cseq,
00894                     &uri,
00895                     &ruri,
00896                     &d->dialog_c);
00897                     
00898     tmb.new_dlg_uas(msg,99,&d->dialog_s);
00899         
00900     d_unlock(d->hash);
00901     print_p_dialogs(L_INFO);
00902     
00903     return CSCF_RETURN_TRUE;
00904 out_of_memory:
00905     if (d){
00906         hash = d->hash;
00907         del_p_dialog(d);
00908         d_unlock(hash);
00909     }
00910     return CSCF_RETURN_FALSE;
00911 }
00912 
00919 void save_dialog_routes(struct sip_msg* msg, char* str1,p_dialog *d)
00920 {
00921     int i;
00922     rr_t *rr,*ri;   
00923     struct hdr_field *hdr;
00924     if (d->routes){
00925         for(i=0;i<d->routes_cnt;i++)
00926             shm_free(d->routes[i].s);
00927         shm_free(d->routes);
00928         d->routes = 0;
00929     }
00930     d->routes_cnt = 0;
00931     for(hdr=cscf_get_next_record_route(msg,0);hdr;hdr=cscf_get_next_record_route(msg,hdr)){
00932         rr = (rr_t*)hdr->parsed;
00933         for(ri=rr;ri;ri=ri->next)
00934             d->routes_cnt++;
00935     }
00936     d->routes = shm_malloc(sizeof(str)*d->routes_cnt);
00937     if (!d->routes){
00938         LOG(L_ERR,"ERR:"M_NAME":save_dialog_routes(): Unable to alloc %d bytes\n",
00939             sizeof(str)*d->routes_cnt);
00940         d->routes_cnt = 0;
00941         return;     
00942     }
00943     if (!str1) return;
00944     if (str1[0]=='o'||str1[0]=='0'||str1[0]=='O'){
00945         /* originating - reverse order */
00946         i = d->routes_cnt-1;
00947         for(hdr=cscf_get_next_record_route(msg,0);hdr;hdr=cscf_get_next_record_route(msg,hdr)){
00948             rr = (rr_t*)hdr->parsed;
00949             for(ri=rr;ri;ri=ri->next){
00950                 STR_SHM_DUP(d->routes[i],ri->nameaddr.uri,"shm");
00951                 i--;
00952             }
00953         }
00954     }else{
00955         /* terminating - normal order */
00956         i = 0;
00957         for(hdr=cscf_get_next_record_route(msg,0);hdr;hdr=cscf_get_next_record_route(msg,hdr)){
00958             rr = (rr_t*)hdr->parsed;
00959             for(ri=rr;ri;ri=ri->next){
00960                 STR_SHM_DUP(d->routes[i],ri->nameaddr.uri,"shm");
00961                 i++;
00962             }
00963         }
00964     }       
00965 out_of_memory:
00966     return; 
00967 }
00973 int update_dialog_on_reply(struct sip_msg *msg, p_dialog *d)
00974 {
00975     struct hdr_field *h_req;
00976     struct hdr_field *h=0;
00977     int res=0;
00978     time_t t_time=0;
00979     str ses_exp = {0,0};
00980     str refresher = {0,0};
00981     str new_ses_exp = {0,0};
00982     str new_ext = {0,0};
00983     int expires = 0;
00984 
00985     ses_exp = cscf_get_session_expires_body(msg, &h);
00986     t_time = cscf_get_session_expires(ses_exp, &refresher);
00987     if (!t_time) //i.e not session-expires header in response
00988     {
00989         if (!d->uac_supp_timer || !d->lr_session_expires)
00990         {
00991             expires = cscf_get_expires_hdr(msg);
00992             if (expires >= 0)
00993             {
00994                 d->expires = d_act_time()+expires;  
00995             }
00996             else
00997             {
00998                 d->expires = d_act_time()+pcscf_dialogs_expiration_time;    
00999             }
01000         }
01001         else// uac supports timer, but no session-expires header found in response
01002         {
01003             d->expires = d_act_time()+d->lr_session_expires;
01004             
01005             new_ses_exp.len = 11/*int value*/ + str_se.len+s_refresher.len+8;
01006             new_ses_exp.s = pkg_malloc(new_ses_exp.len+1);
01007             if (!new_ses_exp.s) {
01008                 LOG(L_ERR,"ERR:"M_NAME":update_dialog_on_reply: Error allocating %d bytes\n",new_ses_exp.len);
01009                 goto error;
01010             }
01011             new_ses_exp.len = snprintf(new_ses_exp.s, new_ses_exp.len, "%.*s %d; %.*suac\r\n",str_se.len, str_se.s, (int)d->lr_session_expires ,s_refresher.len, s_refresher.s);
01012             cscf_add_header(msg, &new_ses_exp, HDR_OTHER_T);
01013             if (!requires_extension(msg, &str_ext_timer)) //must have require timer extenstion
01014             {
01015                 /* walk through all Require headers to find first require header*/
01016                 res = parse_headers(msg, HDR_EOH_F, 0);
01017                 if (res == -1) {
01018                     ERR("Error while parsing headers (%d)\n", res);
01019                     return 0; /* what to return here ? */
01020                 }
01021                 
01022                 h_req = msg->require;
01023                 while (h_req) {
01024                     if (h_req->type == HDR_REQUIRE_T) {
01025                         if (h_req->body.s[new_ext.len-1]=='\n')
01026                         {
01027                             new_ext.len = str_require.len + 1/* */+h_req->body.len + 7;/*, timer*/
01028                             new_ext.s = pkg_malloc(new_ext.len);
01029                             if (!new_ext.s) {
01030                                 LOG(L_ERR,"ERR:"M_NAME":update_dialog_on_reply: Error allocating %d bytes\n",new_ext.len);
01031                                 goto error;
01032                             }           
01033                             new_ext.len = snprintf(new_ext.s, str_require.len, "%.*s %.*s, timer\r\n", str_require.len, str_require.s, h_req->body.len-2, h_req->body.s);
01034                         }
01035                         else
01036                         {
01037                             new_ext.len = str_require.len + 1/*space*/ + h_req->body.len + 9;/*, timer\r\n*/
01038                             new_ext.s = pkg_malloc(new_ext.len);
01039                             if (!new_ext.s) {
01040                                 LOG(L_ERR,"ERR:"M_NAME":update_dialog_on_reply: Error allocating %d bytes\n",new_ext.len);
01041                                 goto error;
01042                             }           
01043                             new_ext.len = snprintf(new_ext.s, str_require.len, "%.*s %.*s, timer\r\n", str_require.len, str_require.s, h_req->body.len, h_req->body.s);
01044                         }
01045                         cscf_del_header(msg, h_req);
01046                         cscf_add_header(msg, &new_ext, HDR_REQUIRE_T);
01047                         break;
01048                     }
01049                     h_req = h_req->next;
01050                 }
01051             }
01052         }
01053     }
01054     else{
01055         d->expires = d_act_time() + t_time;
01056         d->lr_session_expires = t_time;
01057     }   
01058     return 1;
01059 error:
01060     if (new_ses_exp.s) pkg_free(new_ses_exp.s);
01061     if (new_ext.s) pkg_free(new_ext.s);
01062     return 0;   
01063 }
01064 
01080 int P_update_dialog(struct sip_msg* msg, char* str1, char* str2)
01081 {
01082     str call_id;
01083     p_dialog *d;
01084     int response;
01085     int cseq;
01086     struct hdr_field *h=0;
01087     struct sip_msg *req=0;
01088     str host;
01089     int port,transport;
01090     int expires;
01091     str totag;
01092     time_t t_time=0;
01093     str ses_exp = {0,0};
01094     str refresher = {0,0};
01095     enum p_dialog_direction dir;
01096     
01097     dir = get_dialog_direction(str1);
01098     
01099     if (msg->first_line.type==SIP_REPLY) req = cscf_get_request_from_reply(msg);
01100     else req = msg;
01101     if (!find_dialog_contact(req,dir,&host,&port,&transport)){
01102         LOG(L_ERR,"ERR:"M_NAME":P_update_dialog(%s): Error retrieving %s contact\n",str1,str1);
01103         return CSCF_RETURN_BREAK;
01104     }       
01105         
01106     call_id = cscf_get_call_id(msg,0);
01107     if (!call_id.len)
01108         return CSCF_RETURN_FALSE;
01109 
01110     LOG(L_DBG,"DBG:"M_NAME":P_update_dialog(%s): Call-ID <%.*s>\n",str1,call_id.len,call_id.s);
01111 
01112     d = get_p_dialog(call_id,host,port,transport,&dir);
01113     if (!d)
01114         d = get_p_dialog(call_id,host,port,transport,0);
01115     if (!d){
01116         if (msg->first_line.type==SIP_REQUEST &&
01117             msg->first_line.u.request.method.len == 3 && 
01118             strncasecmp(msg->first_line.u.request.method.s,"ACK",3)){
01119             /* to skip the ACK after a 4xx when the dialog was dropped already*/
01120             return CSCF_RETURN_TRUE;
01121         }else{
01122             LOG(L_CRIT,"ERR:"M_NAME":P_update_dialog: dialog does not exists!\n");
01123             return CSCF_RETURN_FALSE;
01124         }   
01125     }
01126 
01127 
01128     if (msg->first_line.type==SIP_REQUEST){
01129         /* Request */
01130         LOG(L_DBG,"DBG:"M_NAME":P_update_dialog(%s): Method <%.*s> \n",str1,
01131             msg->first_line.u.request.method.len,msg->first_line.u.request.method.s);
01132         cseq = cscf_get_cseq(msg,&h);
01133         if (cseq>d->last_cseq) d->last_cseq = cseq;
01134         if (get_dialog_method(msg->first_line.u.request.method) == DLG_METHOD_INVITE)
01135         {
01136             d->uac_supp_timer = supports_extension(msg, &str_ext_timer);
01137     
01138             ses_exp = cscf_get_session_expires_body(msg, &h);
01139             t_time = cscf_get_session_expires(ses_exp, &refresher);
01140             if (!t_time){
01141                 d->expires = d_act_time()+pcscf_dialogs_expiration_time;
01142                 d->lr_session_expires = 0;
01143             } else {
01144                 d->expires = d_act_time() + t_time;
01145                 d->lr_session_expires = t_time;
01146                 if (refresher.len)
01147                     STR_SHM_DUP(d->refresher, refresher, "DIALOG_REFRESHER");
01148             }
01149         }
01150         else if (d->method == DLG_METHOD_SUBSCRIBE && 
01151             msg->first_line.u.request.method.len == 6 && 
01152             strncasecmp(msg->first_line.u.request.method.s,"NOTIFY",6)==0)
01153         {
01154             // Subscription-State header is mandatory for NOTIFY. See RFC 3265, Section 7.2
01155             expires =