Browse code

add support for 100rel extension (prack)

bpi authored on 20/06/2010 23:48:21
Showing 34 changed files
... ...
@@ -127,8 +127,13 @@ class SIPRegistration : public AmSipDialogEventHandler,
127 127
   void onSipReply(const AmSipReply& reply, int old_dlg_status);
128 128
   void onSipRequest(const AmSipRequest& req) {}
129 129
   void onInvite2xx(const AmSipReply&) {}
130
+#if 0
130 131
   void onNo2xxACK(unsigned int) {}
131 132
   void onNoErrorACK(unsigned int) {}
133
+#else
134
+  void onNoAck(unsigned int) {}
135
+  void onNoPrack(unsigned int) {}
136
+#endif
132 137
 
133 138
   /** is this registration registered? */
134 139
   bool active; 
... ...
@@ -36,6 +36,7 @@
36 36
 #include "log.h"
37 37
 #include "AmConfigReader.h"
38 38
 #include "AmUtils.h"
39
+#include "AmSession.h"
39 40
 
40 41
 #include <fstream>
41 42
 #include <cctype>
... ...
@@ -89,6 +90,8 @@ unsigned int AmConfig::OptionsSessionLimit            = 0;
89 90
 unsigned int AmConfig::OptionsSessionLimitErrCode     = 503;
90 91
 string       AmConfig::OptionsSessionLimitErrReason   = "Server overload";
91 92
 
93
+unsigned char AmConfig::rel100                 = REL100_SUPPORTED;
94
+
92 95
 vector <string> AmConfig::CodecOrder;
93 96
 
94 97
 Dtmf::InbandDetectorType 
... ...
@@ -519,5 +522,20 @@ int AmConfig::readConfiguration()
519 522
     }
520 523
   }
521 524
 
525
+  if (cfg.hasParameter("100rel")) {
526
+    string rel100s = cfg.getParameter("100rel");
527
+    if (rel100s == "disabled" || rel100s == "off") {
528
+      rel100 = REL100_DISABLED;
529
+    } else if (rel100s == "supported") {
530
+      rel100 = REL100_SUPPORTED;
531
+    } else if (rel100s == "require") {
532
+      rel100 = REL100_REQUIRE;
533
+    } else {
534
+      ERROR("unknown setting for '100rel' config option.\n");
535
+      return -1;
536
+    }
537
+  }
538
+  INFO("100rel: %d.\n", AmConfig::rel100);
539
+
522 540
   return 0;
523 541
 }	
... ...
@@ -135,6 +135,8 @@ struct AmConfig
135 135
   static unsigned int OptionsSessionLimitErrCode;
136 136
   static string OptionsSessionLimitErrReason;
137 137
 
138
+  static unsigned char rel100;
139
+
138 140
   /** Time of no RTP after which Session is regarded as dead, 0 for no Timeout */
139 141
   static unsigned int DeadRtpTime;
140 142
 
... ...
@@ -36,6 +36,7 @@
36 36
 #include "AmMediaProcessor.h"
37 37
 #include "AmDtmfDetector.h"
38 38
 #include "AmPlayoutBuffer.h"
39
+#include "AmSipHeaders.h"
39 40
 
40 41
 #ifdef WITH_ZRTP
41 42
 #include "AmZRTP.h"
... ...
@@ -49,6 +50,7 @@
49 50
 #include <assert.h>
50 51
 #include <sys/time.h>
51 52
 
53
+
52 54
 volatile unsigned int AmSession::session_num = 0;
53 55
 AmMutex AmSession::session_num_mut;
54 56
 
... ...
@@ -65,6 +67,7 @@ AmSession::AmSession()
65 67
     m_dtmfDetector(this), m_dtmfEventQueue(&m_dtmfDetector),
66 68
     m_dtmfDetectionEnabled(true),
67 69
     accept_early_session(false),
70
+    reliable_1xx(AmConfig::rel100),
68 71
     processing_status(SESSION_PROCESSING_EVENTS)
69 72
 #ifdef WITH_ZRTP
70 73
   ,  zrtp_session(NULL), zrtp_audio(NULL), enable_zrtp(true)
... ...
@@ -76,6 +79,8 @@ AmSession::AmSession()
76 79
 {
77 80
   use_local_audio[AM_AUDIO_IN] = false;
78 81
   use_local_audio[AM_AUDIO_OUT] = false;
82
+  if (reliable_1xx)
83
+    dlg.rseq = 0; //???
79 84
 }
80 85
 
81 86
 AmSession::~AmSession()
... ...
@@ -668,8 +673,41 @@ void AmSession::onSipRequest(const AmSipRequest& req)
668 673
   CALL_EVENT_H(onSipRequest,req);
669 674
 
670 675
   DBG("onSipRequest: method = %s\n",req.method.c_str());
671
-  if(req.method == "INVITE"){
672
-	
676
+  if(req.method == SIP_METH_INVITE){
677
+
678
+    switch(reliable_1xx) {
679
+      case REL100_SUPPORTED: /* if support is on, enforce if asked by UAC */
680
+        if (key_in_list(getHeader(req.hdrs, SIP_HDR_SUPPORTED), 
681
+              SIP_EXT_100REL) ||
682
+            key_in_list(getHeader(req.hdrs, SIP_HDR_REQUIRE), 
683
+              SIP_EXT_100REL)) {
684
+          reliable_1xx = REL100_REQUIRE;
685
+          DBG(SIP_EXT_100REL " now active.\n");
686
+        }
687
+        break;
688
+      case REL100_REQUIRE: /* if support is required, reject if UAC doesn't */
689
+        if (! (key_in_list(getHeader(req.hdrs,SIP_HDR_SUPPORTED), 
690
+              SIP_EXT_100REL) ||
691
+            key_in_list(getHeader(req.hdrs, SIP_HDR_REQUIRE), 
692
+              SIP_EXT_100REL))) {
693
+          ERROR("'" SIP_EXT_100REL "' extension required, but not advertised"
694
+            " by peer.\n");
695
+          dlg.reply(req, 421, "Extension Required", "", "",
696
+              SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF);
697
+          if (dlg.getStatus() < AmSipDialog::Connected)
698
+            setStopped();
699
+          return;
700
+        }
701
+      default:
702
+        ERROR("BUG: unexpected value `%d' for '" SIP_EXT_100REL "' switch.", 
703
+          reliable_1xx);
704
+#ifndef NDEBUG
705
+        abort();
706
+#endif
707
+      case 0: /* support disabled */
708
+        break;
709
+    }
710
+
673 711
     onInvite(req);
674 712
 
675 713
     if(detached.get() && !getStopped()){
... ...
@@ -702,9 +740,25 @@ void AmSession::onSipRequest(const AmSipRequest& req)
702 740
     } else {
703 741
       dlg.reply(req, 415, "Unsupported Media Type");
704 742
     }
705
-  } 
706
-}
743
+  } else if( req.method == SIP_METH_PRACK ) {
744
+    if (reliable_1xx != REL100_REQUIRE) {
745
+      WARN("unexpected PRACK received while " SIP_EXT_100REL " not active.\n");
746
+      return;
747
+    }
707 748
 
749
+    if ((1<<MAX_RSEQ_BITS)<=req.rseq && req.rseq<=(unsigned)abs(dlg.rseq)) {
750
+      // call interface function
751
+      onPrack(req, LOC_RSEQ_ORDER(req.rseq));
752
+      if (req.rseq == (unsigned)-dlg.rseq) {
753
+        dlg.rseq = -dlg.rseq; // confirmed
754
+        DBG("latest RSeq (%u) confirmed.\n", dlg.rseq);
755
+      }
756
+    } else {
757
+      WARN("no matching RAck value in PRACK (%s).\n", req.hdrs.c_str());
758
+      return;
759
+    }
760
+  }
761
+}
708 762
 
709 763
 void AmSession::onSipReply(const AmSipReply& reply, int old_dlg_status)
710 764
 {
... ...
@@ -755,7 +809,7 @@ void AmSession::onSipReply(const AmSipReply& reply, int old_dlg_status)
755 809
 	break;
756 810
 	
757 811
       case AmSipDialog::Pending:
758
-	
812
+
759 813
 	switch(reply.code){
760 814
 	  // todo: 180 with body (remote rbt)
761 815
 	case 180: { 
... ...
@@ -793,17 +847,61 @@ void AmSession::onSipReply(const AmSipReply& reply, int old_dlg_status)
793 847
 	} break;
794 848
 	default:  break;// continue waiting.
795 849
 	}
796
-      }
797
-    }
798
-  }
850
+
851
+        // FIXME: should this stay under negotiate_onreply???
852
+        if (100 < reply.code && reply.code < 200 && 
853
+            reply.method == SIP_METH_INVITE) {
854
+          switch (reliable_1xx) {
855
+          case REL100_SUPPORTED:
856
+            if (key_in_list(getHeader(reply.hdrs, SIP_HDR_REQUIRE), 
857
+                SIP_EXT_100REL))
858
+              reliable_1xx = REL100_REQUIRE;
859
+            else
860
+              break;
861
+
862
+          case REL100_REQUIRE: {
863
+            if (! reply.rseq) {
864
+              ERROR("no RSeq value (or unsupported 0) in reliable 1xx.\n");
865
+              dlg.cancel();
866
+              setStopped();
867
+              break;
868
+            }
869
+            string cseq_val = int2str(reply.cseq) + " " + reply.method;
870
+            sendPrack(/*rcvd sdp_offr [TODO]*/"",int2str(reply.rseq),cseq_val);
871
+            DBG(SIP_EXT_100REL " now active.\n");
872
+          }
873
+          break;
874
+
875
+          case 0:
876
+            // 100rel support disabled
877
+            break;
878
+          default:
879
+            ERROR("BUG: unexpected value `%d' for " SIP_EXT_100REL " switch.", 
880
+                reliable_1xx);
881
+#ifndef NDEBUG
882
+            abort();
883
+#endif
884
+          } // switch reliable 1xx
885
+        } else if (300<=reply.code && reliable_1xx && 
886
+            reply.method==SIP_METH_PRACK) {
887
+          // if PRACK fails, tear down session
888
+          dlg.cancel();
889
+          setStopped();
890
+        } // if 1xx && INVITE || failed && PRACK
891
+      } // switch dlg status
892
+    } // status < Connected
893
+  } //if negotiate_onreply
799 894
 }
800 895
 
896
+
897
+
801 898
 void AmSession::onInvite2xx(const AmSipReply& reply)
802 899
 {
803 900
   AmSipTransaction* t = dlg.get_uac_trans(reply.cseq);
804 901
   if(t) dlg.send_200_ack(*t);
805 902
 }
806 903
 
904
+#if 0
807 905
 void AmSession::onNo2xxACK(unsigned int cseq)
808 906
 {
809 907
   dlg.bye();
... ...
@@ -814,6 +912,29 @@ void AmSession::onNoErrorACK(unsigned int cseq)
814 912
 {
815 913
   setStopped();
816 914
 }
915
+#else
916
+void AmSession::onNoAck(unsigned int cseq)
917
+{
918
+  if (dlg.getStatus() == AmSipDialog::Connected)
919
+    dlg.bye();
920
+  setStopped();
921
+}
922
+
923
+void AmSession::onNoPrack(const AmSipRequest &req, const AmSipReply &rpl)
924
+{
925
+  INFO("reply <%s> timed out.\n", rpl.print().c_str());
926
+  if (100 < rpl.code && rpl.code < 200 && reliable_1xx == REL100_REQUIRE &&
927
+      (unsigned)dlg.rseq == rpl.rseq && rpl.method == SIP_METH_INVITE) {
928
+    INFO("reliable %d reply timed out; rejecting request.\n", rpl.code);
929
+    dlg.reply(req, 504, "Server Time-out");
930
+    if (dlg.getStatus() < AmSipDialog::Connected)
931
+      setStopped();
932
+  } else {
933
+    WARN("reply timed-out, but not reliable.\n"); // debugging
934
+  }
935
+}
936
+
937
+#endif
817 938
 
818 939
 void AmSession::onAudioEvent(AmAudioEvent* audio_ev)
819 940
 {
... ...
@@ -844,6 +965,12 @@ void AmSession::onBye(const AmSipRequest& req)
844 965
   setStopped();
845 966
 }
846 967
 
968
+void AmSession::onPrack(const AmSipRequest& req, unsigned cnt)
969
+{
970
+  DBG("handling #%u PRACK.\n", cnt);
971
+  dlg.reply(req, 200, "OK");
972
+}
973
+
847 974
 int AmSession::acceptAudio(const string& body,
848 975
 			   const string& hdrs,
849 976
 			   string*       sdp_reply)
... ...
@@ -906,6 +1033,47 @@ void AmSession::onSendReply(const AmSipRequest& req, unsigned int  code,
906 1033
 			    const string& reason, const string& content_type,
907 1034
 			    const string& body, string& hdrs, int flags)
908 1035
 {
1036
+  if (req.method == SIP_METH_INVITE) {
1037
+    if (100 < code && code < 200) {
1038
+      switch (reliable_1xx) {
1039
+        case REL100_SUPPORTED:
1040
+          hdrs += SIP_HDR_COLSP(SIP_HDR_SUPPORTED) SIP_EXT_100REL CRLF;
1041
+          break;
1042
+        case REL100_REQUIRE:
1043
+          // add Require HF
1044
+          hdrs += SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF;
1045
+          // add RSeq HF
1046
+#ifndef NDEBUG
1047
+          if ((abs(dlg.rseq) & ((1 << MAX_RSEQ_BITS) - 1)) == 
1048
+              ((1 << MAX_RSEQ_BITS) - 1)) {
1049
+            ERROR("CRITICAL: RSeq value too high: increase MAX_RSEQ_BITS "
1050
+              "(now %d) and recompile.\n", MAX_RSEQ_BITS);
1051
+            abort();
1052
+          }
1053
+#endif
1054
+          if (dlg.rseq < 0) { // RSeq not yet PRACKed
1055
+            // refuse subsequent 1xx if first isn't yet PRACKed
1056
+            if ((((unsigned)-dlg.rseq) & ((1 << MAX_RSEQ_BITS) - 1)) == 0)
1057
+              throw AmSession::Exception(491, "last reliable 1xx not yet "
1058
+                  "PRACKed");
1059
+            dlg.rseq --;
1060
+          } else if (! dlg.rseq) { // only init rseq if 1xx is used
1061
+            unsigned rseq_1st = (get_random() + 1) << MAX_RSEQ_BITS;
1062
+            rseq_1st &= 0x7fffffff;
1063
+            dlg.rseq = -((signed)rseq_1st);
1064
+          } else {
1065
+            dlg.rseq = -(++dlg.rseq);
1066
+          }
1067
+          // FIXME: code above is not re-entrant; should it actually be???
1068
+          hdrs += SIP_HDR_COLSP(SIP_HDR_RSEQ) + int2str(-dlg.rseq) + CRLF;
1069
+          break;
1070
+      }
1071
+    } else if (code < 300 && reliable_1xx == REL100_REQUIRE) {
1072
+      if (dlg.rseq < 0) // reliable 1xx is pending
1073
+        throw AmSession::Exception(491, "last reliable 1xx not yet PRACKed");
1074
+    }
1075
+  }
1076
+
909 1077
   CALL_EVENT_H(onSendReply,req,code,reason,content_type,body,hdrs,flags);
910 1078
 }
911 1079
 
... ...
@@ -915,9 +1083,47 @@ void AmSession::onRtpTimeout()
915 1083
   setStopped();
916 1084
 }
917 1085
 
918
-void AmSession::sendUpdate() 
1086
+void AmSession::sendUpdate(string &cont_type, string &body, string &hdrs)
1087
+{
1088
+  dlg.update(cont_type, body, hdrs);
1089
+}
1090
+
1091
+void AmSession::sendPrack(const string &sdp_offer, 
1092
+                          const string &rseq_val, 
1093
+                          const string &cseq_val)
1094
+{
1095
+  string hdrs = "RAck: " + rseq_val + " " + cseq_val + "\r\n";
1096
+
1097
+  // TODO: digest an answer based on the sdp_offer
1098
+
1099
+  // TODO: should't cseq&rseq be handled in dialog, entirely?!?!
1100
+  if (dlg.prack(/*cont. type*/"", /*body*/"", hdrs) < 0)
1101
+    ERROR("failed to send PRACK request in session '%s'.\n",sid4dbg().c_str());
1102
+}
1103
+
1104
+string AmSession::sid4dbg()
919 1105
 {
920
-  dlg.update("");
1106
+  string dbg;
1107
+  dbg = dlg.callid + "/" + dlg.local_tag + "/" + dlg.remote_tag + "/" + 
1108
+      int2str(RTPStream()->getLocalPort()) + "/" + 
1109
+      RTPStream()->getRHost() + ":" + int2str(RTPStream()->getRPort());
1110
+  return dbg;
1111
+}
1112
+
1113
+static inline string get_100rel_hdr(unsigned char reliable_1xx)
1114
+{
1115
+  switch(reliable_1xx) {
1116
+    case REL100_SUPPORTED: 
1117
+      return SIP_HDR_COLSP(SIP_HDR_SUPPORTED) SIP_EXT_100REL CRLF;
1118
+    case REL100_REQUIRE: 
1119
+      return SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF;
1120
+    default:
1121
+      ERROR("BUG: unexpected reliability switch value of '%d'.\n",
1122
+          reliable_1xx);
1123
+    case 0:
1124
+      break;
1125
+  }
1126
+  return "";
921 1127
 }
922 1128
 
923 1129
 void AmSession::sendReinvite(bool updateSDP, const string& headers) 
... ...
@@ -926,9 +1132,10 @@ void AmSession::sendReinvite(bool updateSDP, const string& headers)
926 1132
     RTPStream()->setLocalIP(AmConfig::LocalIP);
927 1133
     string sdp_body;
928 1134
     sdp.genResponse(advertisedIP(), RTPStream()->getLocalPort(), sdp_body);
929
-    dlg.reinvite(headers, "application/sdp", sdp_body);
1135
+    dlg.reinvite(headers + get_100rel_hdr(reliable_1xx), "application/sdp",
1136
+        sdp_body);
930 1137
   } else {
931
-    dlg.reinvite(headers, "", "");
1138
+    dlg.reinvite(headers + get_100rel_hdr(reliable_1xx), "", "");
932 1139
   }
933 1140
 }
934 1141
 
... ...
@@ -943,7 +1150,8 @@ int AmSession::sendInvite(const string& headers)
943 1150
   // Generate SDP.
944 1151
   string sdp_body;
945 1152
   sdp.genRequest(advertisedIP(), RTPStream()->getLocalPort(), sdp_body);
946
-  return dlg.invite(headers, "application/sdp", sdp_body);
1153
+  return dlg.invite(headers + get_100rel_hdr(reliable_1xx), "application/sdp",
1154
+      sdp_body);
947 1155
 }
948 1156
 
949 1157
 void AmSession::setOnHold(bool hold)
... ...
@@ -61,6 +61,14 @@ class AmDtmfEvent;
61 61
 #define AM_AUDIO_IN  0
62 62
 #define AM_AUDIO_OUT 1
63 63
 
64
+
65
+/* maximum number of reliable 1xx supported; needed for marking first 1xx
66
+ * (which can not be followed by subsequent ones w/o being PRACKed), as
67
+ * opposed to having another value storing it's value. */
68
+#define MAX_RSEQ_BITS           24
69
+#define LOC_RSEQ_ORDER(_rseq)   (_rseq & ((1 << MAX_RSEQ_BITS) - 1))
70
+
71
+
64 72
 /**
65 73
  * \brief Implements the default behavior of one session
66 74
  * 
... ...
@@ -156,6 +164,12 @@ protected:
156 164
 
157 165
   /** do accept early session? */
158 166
   bool accept_early_session;
167
+  /** enable the reliability of provisional replies? */
168
+  unsigned char reliable_1xx;
169
+#define REL100_DISABLED         0
170
+#define REL100_SUPPORTED        1
171
+#define REL100_REQUIRE          2
172
+#define REL100_MAX              REL100_REQUIRE
159 173
 
160 174
   vector<AmSessionEventHandler*> ev_handlers;
161 175
 
... ...
@@ -329,11 +343,15 @@ public:
329 343
 			 string* sdp_reply);
330 344
 
331 345
   /** send an UPDATE in the session */
332
-  virtual void sendUpdate();
346
+  void sendUpdate(string &cont_type, string &body, string &hdrs);
333 347
   /** send a Re-INVITE (if connected) */
334 348
   virtual void sendReinvite(bool updateSDP = true, const string& headers = "");
335 349
   /** send an INVITE */
336 350
   virtual int sendInvite(const string& headers = "");
351
+  /** send a PRACK request */
352
+  void sendPrack(const string &sdp_offer, 
353
+                 const string &rseq_val, 
354
+                 const string &cseq_val);
337 355
 
338 356
   /** set the session on/off hold */
339 357
   virtual void setOnHold(bool hold);
... ...
@@ -418,6 +436,14 @@ public:
418 436
    */
419 437
   virtual void onCancel(){}
420 438
 
439
+  /**
440
+   * onPrack is called when a PRACK request is received for the session.
441
+   * The sequencing correctness (RAck fits) is already checked.
442
+   * Should be overridden if SDP offer is expected with it.
443
+   * @param cnt order of which 1xx this PRACK is for 
444
+   */
445
+  virtual void onPrack(const AmSipRequest& req, unsigned cnt);
446
+
421 447
   /**
422 448
    * onSessionStart will be called after call setup.
423 449
    *
... ...
@@ -464,14 +490,25 @@ public:
464 490
   virtual void onSipRequest(const AmSipRequest& req);
465 491
   /** Entry point for SIP Replies   */
466 492
   virtual void onSipReply(const AmSipReply& reply, int old_dlg_status);
493
+#if 0
494
+  /** There was a timeout receiving a reply for the given SIP request */
495
+  virtual void onSipReqTimeout(const AmSipRequest &req);
496
+  /** There was a timeout receiving a request (PR-/ACK) for given SIP reply */
497
+  virtual void onSipRplTimeout(const AmSipRequest &req, const AmSipReply &rpl);
498
+#endif
467 499
 
468 500
   /** 2xx reply has been received for an INVITE transaction */
469 501
   virtual void onInvite2xx(const AmSipReply& reply);
470 502
   
503
+#if 0
471 504
   /** missing 2xx-ACK */
472 505
   virtual void onNo2xxACK(unsigned int cseq);
473 506
   /** missing non-2xx-ACK */
474 507
   virtual void onNoErrorACK(unsigned int cseq);
508
+#else
509
+  virtual void onNoAck(unsigned int cseq);
510
+  virtual void onNoPrack(const AmSipRequest &req, const AmSipReply &rpl);
511
+#endif
475 512
 
476 513
   /**
477 514
    * Entry point for Audio events
... ...
@@ -518,6 +555,8 @@ public:
518 555
 
519 556
   // The IP address to put as c= in SDP bodies and to use for Contact:.
520 557
   string advertisedIP();
558
+
559
+  string sid4dbg();
521 560
 };
522 561
 
523 562
 inline AmRtpAudio* AmSession::RTPStream() {
... ...
@@ -48,6 +48,17 @@ bool AmSessionEventHandler::onSipReply(const AmSipReply& reply)
48 48
   return false;
49 49
 }
50 50
 
51
+bool AmSessionEventHandler::onSipReqTimeout(const AmSipRequest &)
52
+{
53
+  return false;
54
+}
55
+
56
+bool AmSessionEventHandler::onSipRplTimeout(const AmSipRequest &, 
57
+    const AmSipReply &)
58
+{
59
+  return false;
60
+}
61
+
51 62
 bool AmSessionEventHandler::onSendRequest(const string& method, 
52 63
 					  const string& content_type,
53 64
 					  const string& body,
... ...
@@ -67,6 +67,8 @@ public:
67 67
 
68 68
   virtual bool onSipRequest(const AmSipRequest&);
69 69
   virtual bool onSipReply(const AmSipReply&);
70
+  virtual bool onSipReqTimeout(const AmSipRequest &);
71
+  virtual bool onSipRplTimeout(const AmSipRequest &, const AmSipReply &);
70 72
 
71 73
   virtual bool onSendRequest(const string& method, 
72 74
 			     const string& content_type,
... ...
@@ -254,6 +254,7 @@ void AmSipDialog::updateStatus(const AmSipReply& reply)
254 254
     break;
255 255
 
256 256
   case Pending:
257
+    // TODO [SBGW]: if negative and PRACK, tear down the call (???)
257 258
   case Disconnected:
258 259
     // only change status of dialog if reply 
259 260
     // to INVITE received
... ...
@@ -299,6 +300,7 @@ void AmSipDialog::uasTimeout(AmSipTimeoutEvent* to_ev)
299 300
   assert(to_ev);
300 301
 
301 302
   switch(to_ev->type){
303
+#if 0
302 304
   case AmSipTimeoutEvent::no2xxACK:
303 305
     DBG("Timeout: missing 2xx-ACK\n");
304 306
     if(hdl) hdl->onNo2xxACK(to_ev->cseq);
... ...
@@ -308,10 +310,16 @@ void AmSipDialog::uasTimeout(AmSipTimeoutEvent* to_ev)
308 310
     DBG("Timeout: missing non-2xx-ACK\n");
309 311
     if(hdl) hdl->onNoErrorACK(to_ev->cseq);
310 312
     break;
313
+#else
314
+  case AmSipTimeoutEvent::noACK:
315
+    DBG("Timeout: missing ACK\n");
316
+    if(hdl) hdl->onNoAck(to_ev->cseq);
317
+    break;
318
+#endif
311 319
 
312 320
   case AmSipTimeoutEvent::noPRACK:
313
-    //TODO
314 321
     DBG("Timeout: missing PRACK\n");
322
+    if(hdl) hdl->onNoPrack(to_ev->req, to_ev->rpl);
315 323
     break;
316 324
 
317 325
   case AmSipTimeoutEvent::_noEv:
... ...
@@ -375,8 +383,8 @@ int AmSipDialog::reply(const AmSipRequest& req,
375 383
       reply.hdrs += SIP_HDR_COLSP(SIP_HDR_SERVER) + AmConfig::Signature + CRLF;
376 384
   }
377 385
 
378
-  if ((req.method!="CANCEL")&&
379
-      !((req.method=="BYE")&&(code<300)))
386
+  if (code < 300 && req.method != "CANCEL" && req.method != "BYE")
387
+    /* if 300<=code<400, explicit contact setting should be done */
380 388
     reply.contact = getContactHdr();
381 389
 
382 390
   reply.content_type = content_type;
... ...
@@ -489,22 +497,19 @@ int AmSipDialog::invite(const string& hdrs,
489 497
   }	
490 498
 }
491 499
 
492
-int AmSipDialog::update(const string& hdrs)
500
+int AmSipDialog::update(const string &cont_type, 
501
+                        const string &body, 
502
+                        const string &hdrs)
493 503
 {
494 504
   switch(status){
495 505
   case Connected:
496
-    return sendRequest("UPDATE", "", "", hdrs);
497
-  case Disconnecting:
498 506
   case Pending:
499
-    DBG("update(): we are not yet connected."
500
-	"(status=%i). do nothing!\n",status);
507
+    return sendRequest(SIP_METH_UPDATE, cont_type, body, hdrs);
501 508
 
502
-    return 0;
503 509
   default:
504
-    DBG("update(): we are not connected "
505
-	"(status=%i). do nothing!\n",status);
506
-    return 0;
510
+    DBG("update(): dialog not connected (status=%i). do nothing!\n",status);
507 511
   }	
512
+  return -1;
508 513
 }
509 514
 
510 515
 int AmSipDialog::refer(const string& refer_to,
... ...
@@ -567,6 +572,25 @@ int AmSipDialog::transfer(const string& target)
567 572
   return 0;
568 573
 }
569 574
 
575
+int AmSipDialog::prack(const string &cont_type, 
576
+                       const string &body, 
577
+                       const string &hdrs)
578
+{
579
+  switch(status) {
580
+    case Pending:
581
+      break;
582
+    case Disconnected:
583
+    case Connected:
584
+    case Disconnecting:
585
+      ERROR("can not send PRACK while dialog is in state '%d'.\n", status);
586
+      return -1;
587
+    default:
588
+      ERROR("BUG: unexpected dialog state '%d'.\n", status);
589
+      return -1;
590
+  }
591
+  return sendRequest(SIP_METH_PRACK, cont_type, body, hdrs);
592
+}
593
+
570 594
 int AmSipDialog::cancel()
571 595
 {
572 596
     for(TransMap::reverse_iterator t = uac_trans.rbegin();
... ...
@@ -96,11 +96,19 @@ class AmSipDialogEventHandler
96 96
   /** Hook called when a local INVITE request has been replied with 2xx */
97 97
   virtual void onInvite2xx(const AmSipReply& reply)=0;
98 98
 
99
+#if 0
99 100
   /** Hook called when a UAS INVITE transaction did not receive a 2xx-ACK */
100 101
   virtual void onNo2xxACK(unsigned int cseq)=0;
101 102
 
102 103
   /** Hook called when a UAS INVITE transaction did not receive a non-2xx-ACK */
103 104
   virtual void onNoErrorACK(unsigned int cseq)=0;
105
+#else
106
+  /** Hook called when a UAS INVITE transaction did not receive the ACK */
107
+  virtual void onNoAck(unsigned int)=0;
108
+
109
+  /** Hook called when a UAS INVITE transaction did not receive the PRACK */
110
+  virtual void onNoPrack(const AmSipRequest &, const AmSipReply &)=0;
111
+#endif
104 112
 
105 113
   virtual ~AmSipDialogEventHandler() {};
106 114
 };
... ...
@@ -152,6 +160,7 @@ class AmSipDialog
152 160
   string outbound_proxy;
153 161
   bool   force_outbound_proxy;
154 162
 
163
+  int rseq;          // RSeq for next request (NOTE: keep it signed!)
155 164
   unsigned int cseq; // Local CSeq for next request
156 165
   bool r_cseq_i;
157 166
   unsigned int r_cseq; // last remote CSeq  
... ...
@@ -193,7 +202,12 @@ class AmSipDialog
193 202
     
194 203
   int bye(const string& hdrs = "");
195 204
   int cancel();
196
-  int update(const string& hdrs);
205
+  int prack(const string &cont_type, 
206
+            const string &body, 
207
+            const string &hdrs);
208
+  int update(const string &cont_type, 
209
+            const string &body, 
210
+            const string &hdrs);
197 211
   int reinvite(const string& hdrs,  
198 212
 	       const string& content_type,
199 213
 	       const string& body);
... ...
@@ -7,6 +7,26 @@ void AmSipTimeoutEvent::operator() (AmSipDialog* dlg)
7 7
     dlg->uasTimeout(this);
8 8
 }
9 9
 
10
+
11
+#if 0
12
+  AmSipReqTimeoutEvent *req_tout_ev = 
13
+    dynamic_cast<AmSipReqTimeoutEvent *>(sip_ev);
14
+  if (req_tout_ev) {
15
+    CALL_EVENT_H(onSipReqTimeout, req_tout_ev->req);
16
+    onSipReqTimeout(req_tout_ev->req);
17
+    return;
18
+  }
19
+
20
+  AmSipRplTimeoutEvent *rpl_tout_ev = 
21
+    dynamic_cast<AmSipRplTimeoutEvent *>(sip_ev);
22
+  if (rpl_tout_ev) {
23
+    CALL_EVENT_H(onSipRplTimeout, rpl_tout_ev->req, rpl_tout_ev->rpl);
24
+    onSipRplTimeout(rpl_tout_ev->req, rpl_tout_ev->rpl);
25
+    return;
26
+  }
27
+#endif
28
+
29
+
10 30
 void AmSipRequestEvent::operator() (AmSipDialog* dlg)
11 31
 {
12 32
     assert(dlg);
... ...
@@ -48,6 +48,7 @@ class AmSipEvent: public AmEvent
48 48
   virtual void operator() (AmSipDialog* dlg)=0;
49 49
 };
50 50
 
51
+#if 0
51 52
 /** \brief UAS reply re-transmission timeout event */
52 53
 class AmSipTimeoutEvent: public AmSipEvent
53 54
 {
... ...
@@ -70,6 +71,34 @@ class AmSipTimeoutEvent: public AmSipEvent
70 71
 
71 72
   virtual void operator() (AmSipDialog* dlg);
72 73
 };
74
+#else
75
+/** \brief UAS reply re-transmission timeout event */
76
+class AmSipTimeoutEvent: public AmSipEvent
77
+{
78
+ public:
79
+
80
+  enum EvType {
81
+    _noEv=0,
82
+    noACK,
83
+    noPRACK,
84
+  };
85
+
86
+  EvType       type;
87
+
88
+  unsigned int cseq;
89
+  AmSipRequest req;
90
+  AmSipReply   rpl;
91
+
92
+  AmSipTimeoutEvent(EvType t, unsigned int cseq_num)
93
+    : AmSipEvent(), type(t)
94
+   {}
95
+  AmSipTimeoutEvent(EvType t, AmSipRequest &_req, AmSipReply &_rpl)
96
+    : AmSipEvent(), req(_req), rpl(_rpl)
97
+    {}
98
+
99
+  virtual void operator() (AmSipDialog* dlg);
100
+};
101
+#endif
73 102
 
74 103
 /** \brief SIP request event */
75 104
 class AmSipRequestEvent: public AmSipEvent
... ...
@@ -97,4 +126,37 @@ class AmSipReplyEvent: public AmSipEvent
97 126
 };
98 127
 
99 128
 
129
+#if 0
130
+// TODO: have a single AmSipT.outEv. class, with type and a _AmSipMsgInDlg
131
+// ptr: it will save the dynamic_cast efforts later.
132
+
133
+class AmSipTimeoutEvent: public AmSipEvent
134
+{
135
+  public:
136
+    AmSipTimeoutEvent(int id = -1) 
137
+      : AmSipEvent(-1)
138
+    {}
139
+};
140
+
141
+class AmSipReqTimeoutEvent : public AmSipTimeoutEvent
142
+{
143
+  public:
144
+    AmSipRequest req;
145
+    AmSipReqTimeoutEvent(AmSipRequest &r):
146
+      AmSipTimeoutEvent(), req(r)
147
+    {}
148
+};
149
+
150
+class AmSipRplTimeoutEvent : public AmSipTimeoutEvent
151
+{
152
+  public:
153
+    AmSipRequest req;
154
+    AmSipReply   rpl;
155
+    AmSipRplTimeoutEvent(AmSipRequest &_req, AmSipReply &_rpl):
156
+      AmSipTimeoutEvent(), req(_req), rpl(_rpl)
157
+    {}
158
+};
159
+#endif
160
+
161
+
100 162
 #endif
... ...
@@ -4,23 +4,31 @@
4 4
 
5 5
 #define SIP_SCHEME_SIP          "sip"
6 6
 
7
+#define SIP_METH_INVITE         "INVITE"
8
+#define SIP_METH_PRACK          "PRACK"
9
+#define SIP_METH_UPDATE         "UPDATE"
10
+
7 11
 #define SIP_HDR_FROM            "From"
8 12
 #define SIP_HDR_TO              "To"
9 13
 #define SIP_HDR_ROUTE           "Route"
10 14
 #define SIP_HDR_CONTENT_TYPE    "Content-Type"
11 15
 #define SIP_HDR_CONTACT         "Contact"
12 16
 #define SIP_HDR_SUPPORTED       "Supported"
13
-#define SIP_HDR_REQUIRED        "Required"
17
+#define SIP_HDR_REQUIRE         "Require"
14 18
 #define SIP_HDR_SERVER          "Server"
15 19
 #define SIP_HDR_USER_AGENT      "User-Agent"
16 20
 #define SIP_HDR_MAX_FORWARDS    "Max-Forwards"
17 21
 #define SIP_HDR_P_ASSERTED_IDENTITY "P-Asserted-Identity"
18 22
 #define SIP_HDR_REFER_TO        "Refer-To"
19 23
 #define SIP_HDR_EXPIRES         "Expires"
24
+#define SIP_HDR_RSEQ            "RSeq"
25
+#define SIP_HDR_RACK            "RAck"
20 26
 #define SIP_HDR_COL(_hdr)       _hdr ":"
21 27
 #define SIP_HDR_COLSP(_hdr)     SIP_HDR_COL(_hdr) " "
22 28
 
23 29
 #define CRLF                    "\r\n"
24 30
 #define SIP_HDR_LEN(_hdr)       (sizeof(_hdr) - /*0-term*/1)
25 31
 
32
+#define SIP_EXT_100REL          "100rel"
33
+
26 34
 #endif /* __AMSIPHEADERS_H__ */
... ...
@@ -129,7 +129,7 @@ bool removeHeader(string& hdrs, const string& hdr_name) {
129 129
       buf += string(name) + ":" + "[" + member + "]" + ";";	\
130 130
   } while (0)
131 131
 
132
-string AmSipRequest::print()
132
+string AmSipRequest::print() const
133 133
 {
134 134
   string buf;
135 135
 
... ...
@@ -155,7 +155,7 @@ string AmSipRequest::print()
155 155
   return buf;
156 156
 }
157 157
 
158
-string AmSipReply::print()
158
+string AmSipReply::print() const
159 159
 {
160 160
   string buf;
161 161
 
... ...
@@ -19,15 +19,16 @@ class _AmSipMsgInDlg
19 19
   string       hdrs;
20 20
   string       body;
21 21
   unsigned int cseq;
22
+  unsigned int rseq;
22 23
   string       callid;
23 24
 
24 25
   // transaction ticket from sip stack
25 26
   trans_ticket tt;
26 27
 
27
- _AmSipMsgInDlg() : cseq(0) { }
28
+  _AmSipMsgInDlg() : cseq(0), rseq(0) { }
28 29
   virtual ~_AmSipMsgInDlg() { };
29 30
 
30
-  virtual string print() = 0;
31
+  virtual string print() const = 0;
31 32
 };
32 33
 
33 34
 /** \brief represents a SIP reply */
... ...
@@ -45,7 +46,7 @@ class AmSipReply : public _AmSipMsgInDlg
45 46
 
46 47
  AmSipReply() : code(0), _AmSipMsgInDlg() { }
47 48
   ~AmSipReply() { }
48
-  string print();
49
+  string print() const;
49 50
 };
50 51
 
51 52
 
... ...
@@ -67,7 +68,7 @@ class AmSipRequest : public _AmSipMsgInDlg
67 68
  AmSipRequest() : _AmSipMsgInDlg() { }
68 69
   ~AmSipRequest() { }
69 70
   
70
-  string print();
71
+  string print() const;
71 72
 };
72 73
 
73 74
 string getHeader(const string& hdrs,const string& hdr_name);
... ...
@@ -28,12 +28,15 @@
28 28
 
29 29
 #include "AmUtils.h"
30 30
 #include "AmSipMsg.h"
31
+#include "AmSipHeaders.h"
31 32
 
32 33
 #include "sip/trans_layer.h"
33 34
 #include "sip/sip_parser.h"
34 35
 #include "sip/parse_header.h"
35 36
 #include "sip/parse_from_to.h"
36 37
 #include "sip/parse_cseq.h"
38
+#include "sip/parse_extensions.h"
39
+#include "sip/parse_100rel.h"
37 40
 #include "sip/hash_table.h"
38 41
 #include "sip/sip_trans.h"
39 42
 #include "sip/wheeltimer.h"
... ...
@@ -300,7 +303,50 @@ int SipCtrlInterface::send(const AmSipReply &rep)
300 303
 	}
301 304
     }
302 305
 
303
-    
306
+
307
+    /* check if we need to store RSeq. */
308
+    // FIXME: shouldn't the global "100rel==on?" check be present here??
309
+    if(100 < rep.code && rep.code < 200 && rep.method == SIP_METH_INVITE) {
310
+        unsigned ext = 0;
311
+        unsigned rseq = 0;
312
+        for(list<sip_header*>::iterator it = msg.hdrs.begin();
313
+                !(ext && rseq) && it != msg.hdrs.end(); ++it) {
314
+            // check if there's a Require field containing 100rel; if there
315
+            // is, look for RSeq and store it's value in transaction;
316
+            DBG("HT:%d, ext:%d, rseq:%d.\n", (*it)->type, ext, rseq);
317
+            switch ((*it)->type) {
318
+            case sip_header::H_REQUIRE:
319
+                if (ext)
320
+                    // there was alrady a(nother?) Require HF
321
+                    continue;
322
+                if(!parse_extensions(&ext, (*it)->value.s, (*it)->value.len)) {
323
+                    ERROR("failed to parse(own?)'" SIP_HDR_REQUIRE "' hdr.\n");
324
+                    continue;
325
+                }
326
+                if (rseq) { // our RSeq's are never 0
327
+                    rep.tt._t->last_rseq = rseq;
328
+                    continue; // the end.
329
+                }
330
+                break;
331
+
332
+            case sip_header::H_RSEQ:
333
+                if (rseq) {
334
+                    ERROR("multiple '" SIP_HDR_RSEQ "' headers in reply.\n");
335
+                    continue;
336
+                }
337
+                if (! parse_rseq(&rseq, (*it)->value.s, (*it)->value.len)) {
338
+                    ERROR("failed to parse (own?) '" SIP_HDR_RSEQ "' hdr.\n");
339
+                    continue;
340
+                }
341
+                if (ext) {
342
+                    rep.tt._t->last_rseq = rseq;
343
+                    continue; // the end.
344
+                }
345
+            }
346
+        }
347
+    }
348
+
349
+
304 350
     unsigned int hdrs_len = copy_hdrs_len(msg.hdrs);
305 351
 
306 352
     if(!rep.body.empty()) {
... ...
@@ -331,17 +377,10 @@ int SipCtrlInterface::send(const AmSipReply &rep)
331 377
     return ret;
332 378
 }
333 379
 
334
-#define DBG_PARAM(p)\
335
-    DBG("%s = <%s>\n",#p,p.c_str());
336 380
 
337
-void SipCtrlInterface::handle_sip_request(const trans_ticket& tt, sip_msg* msg)
381
+inline void SipCtrlInterface::sip_msg2am_request(const sip_msg *msg, 
382
+    AmSipRequest &req)
338 383
 {
339
-    assert(msg);
340
-    assert(msg->from && msg->from->p);
341
-    assert(msg->to && msg->to->p);
342
-    
343
-    AmSipRequest req;
344
-    
345 384
     req.cmd      = "sems";
346 385
     req.method   = c2stlstr(msg->u.request->method_str);
347 386
     req.user     = c2stlstr(msg->u.request->ruri.user);
... ...
@@ -370,7 +409,7 @@ void SipCtrlInterface::handle_sip_request(const trans_ticket& tt, sip_msg* msg)
370 409
 	}
371 410
     }
372 411
     else {
373
-	if (req.method == "INVITE") {
412
+	if (req.method == SIP_METH_INVITE) {
374 413
 	    WARN("Request has no contact header\n");
375 414
 	    WARN("\trequest = '%.*s'\n",msg->len,msg->buf);
376 415
 	}
... ...
@@ -393,50 +432,26 @@ void SipCtrlInterface::handle_sip_request(const trans_ticket& tt, sip_msg* msg)
393 432
     req.cseq     = get_cseq(msg)->num;
394 433
     req.body     = c2stlstr(msg->body);
395 434
 
396
-    req.tt = tt;
435
+    if (msg->rack)
436
+        req.rseq = get_rack(msg)->rseq;
397 437
 
398 438
     if (msg->content_type)
399 439
  	req.content_type = c2stlstr(msg->content_type->value);
400 440
 
401 441
     prepare_routes_uas(msg->record_route, req.route);
402 442
 	
403
-    for (list<sip_header*>::iterator it = msg->hdrs.begin(); 
443
+    for (list<sip_header *>::const_iterator it = msg->hdrs.begin(); 
404 444
 	 it != msg->hdrs.end(); ++it) {
405
-	if((*it)->type == sip_header::H_OTHER){
445
+	if((*it)->type == sip_header::H_OTHER || 
446
+                (*it)->type == sip_header::H_REQUIRE){
406 447
 	    req.hdrs += c2stlstr((*it)->name) + ": " 
407
-		+ c2stlstr((*it)->value) + "\r\n";
448
+		+ c2stlstr((*it)->value) + CRLF;
408 449
 	}
409 450
     }
410
-
411
-    DBG("Received new request\n");
412
-    if (SipCtrlInterface::log_parsed_messages) {
413
-	//     DBG_PARAM(req.cmd);
414
-	DBG_PARAM(req.method);
415
-	//     DBG_PARAM(req.user);
416
-	//     DBG_PARAM(req.domain);
417
-	DBG_PARAM(req.r_uri);
418
-	DBG_PARAM(req.from_uri);
419
-	DBG_PARAM(req.from);
420
-	DBG_PARAM(req.to);
421
-	DBG_PARAM(req.callid);
422
-	DBG_PARAM(req.from_tag);
423
-	DBG_PARAM(req.to_tag);
424
-	DBG("cseq = <%i>\n",req.cseq);
425
-	DBG_PARAM(req.route);
426
-	DBG("hdrs = <%s>\n",req.hdrs.c_str());
427
-	DBG("body = <%s>\n",req.body.c_str());
428
-    }
429
-
430
-    AmSipDispatcher::instance()->handleSipMsg(req);
431 451
 }
432 452
 
433
-void SipCtrlInterface::handle_sip_reply(sip_msg* msg)
453
+inline bool SipCtrlInterface::sip_msg2am_reply(sip_msg *msg, AmSipReply &reply)
434 454
 {
435
-    assert(msg->from && msg->from->p);
436
-    assert(msg->to && msg->to->p);
437
-    
438
-    AmSipReply   reply;
439
-
440 455
     reply.content_type = msg->content_type ? c2stlstr(msg->content_type->value): "";
441 456
 
442 457
     reply.body = msg->body.len ? c2stlstr(msg->body) : "";
... ...
@@ -456,7 +471,7 @@ void SipCtrlInterface::handle_sip_reply(sip_msg* msg)
456 471
 	if(err < 0) {
457 472
 	    
458 473
 	    ERROR("Contact nameaddr parsing failed\n");
459
-	    return;
474
+	    return false;
460 475
 	}
461 476
 	
462 477
 	// 'Contact' header?
... ...
@@ -474,15 +489,78 @@ void SipCtrlInterface::handle_sip_reply(sip_msg* msg)
474 489
     reply.remote_tag = c2stlstr(((sip_from_to*)msg->to->p)->tag);
475 490
     reply.local_tag  = c2stlstr(((sip_from_to*)msg->from->p)->tag);
476 491
 
492
+
477 493
     prepare_routes_uac(msg->record_route, reply.route);
478 494
 
495
+    unsigned rseq;
479 496
     for (list<sip_header*>::iterator it = msg->hdrs.begin(); 
480 497
 	 it != msg->hdrs.end(); ++it) {
481
-	if((*it)->type == sip_header::H_OTHER){
482
-	    reply.hdrs += c2stlstr((*it)->name) + ": " 
483
-		+ c2stlstr((*it)->value) + "\r\n";
484
-	}
498
+        switch ((*it)->type) {
499
+          case sip_header::H_OTHER:
500
+          case sip_header::H_REQUIRE:
501
+	      reply.hdrs += c2stlstr((*it)->name) + ": " 
502
+                  + c2stlstr((*it)->value) + CRLF;
503
+              break;
504
+          case sip_header::H_RSEQ:
505
+              if (! parse_rseq(&rseq, (*it)->value.s, (*it)->value.len)) {
506
+                  ERROR("failed to parse (rcvd) '" SIP_HDR_RSEQ "' hdr.\n");
507
+              } else {
508
+                  reply.rseq = rseq;
509
+              }
510
+              break;
511
+        }
485 512
     }
513
+
514
+    return true;
515
+}
516
+
517
+
518
+#define DBG_PARAM(p)\
519
+    DBG("%s = <%s>\n",#p,p.c_str());
520
+
521
+void SipCtrlInterface::handle_sip_request(const trans_ticket& tt, sip_msg* msg)
522
+{
523
+    assert(msg);
524
+    assert(msg->from && msg->from->p);
525
+    assert(msg->to && msg->to->p);
526
+    
527
+    AmSipRequest req;
528
+
529
+    sip_msg2am_request(msg, req);
530
+
531
+    req.tt = tt;
532
+
533
+    DBG("Received new request\n");
534
+    if (SipCtrlInterface::log_parsed_messages) {
535
+	//     DBG_PARAM(req.cmd);
536
+	DBG_PARAM(req.method);
537
+	//     DBG_PARAM(req.user);
538
+	//     DBG_PARAM(req.domain);
539
+	DBG_PARAM(req.r_uri);
540
+	DBG_PARAM(req.from_uri);
541
+	DBG_PARAM(req.from);
542
+	DBG_PARAM(req.to);
543
+	DBG_PARAM(req.callid);
544
+	DBG_PARAM(req.from_tag);
545
+	DBG_PARAM(req.to_tag);
546
+	DBG("cseq = <%i>\n",req.cseq);
547
+	DBG_PARAM(req.route);
548
+	DBG("hdrs = <%s>\n",req.hdrs.c_str());
549
+	DBG("body = <%s>\n",req.body.c_str());
550
+    }
551
+
552
+    AmSipDispatcher::instance()->handleSipMsg(req);
553
+}
554
+
555
+void SipCtrlInterface::handle_sip_reply(sip_msg* msg)
556
+{
557
+    assert(msg->from && msg->from->p);
558
+    assert(msg->to && msg->to->p);
559
+    
560
+    AmSipReply   reply;
561
+
562
+    if (! sip_msg2am_reply(msg, reply))
563
+      return;
486 564
     
487 565
     DBG("Received reply: %i %s\n",reply.code,reply.reason.c_str());
488 566
     DBG_PARAM(reply.callid);
... ...
@@ -493,8 +571,7 @@ void SipCtrlInterface::handle_sip_reply(sip_msg* msg)
493 571
     AmSipDispatcher::instance()->handleSipMsg(reply);
494 572
 }
495 573
 
496
-#undef DBG_PARAM
497
-
574
+#if 0
498 575
 void SipCtrlInterface::timer_expired(sip_trans* trans, sip_timer_type tt)
499 576
 {
500 577
     assert(trans);
... ...
@@ -542,6 +619,66 @@ void SipCtrlInterface::timer_expired(sip_trans* trans, sip_timer_type tt)
542 619
     AmEventDispatcher::instance()->post(c2stlstr(trans->to_tag),
543 620
 					new AmSipTimeoutEvent(ev, cseq->num));
544 621
 }
622
+#endif
623
+
624
+
625
+void SipCtrlInterface::handle_reply_timeout(AmSipTimeoutEvent::EvType evt,
626
+    sip_trans *tr, trans_bucket *buk)
627
+{
628
+  AmSipTimeoutEvent *tmo_evt;
629
+  
630
+  switch (evt) {
631
+  case AmSipTimeoutEvent::noACK: {
632
+      sip_cseq* cseq = dynamic_cast<sip_cseq*>(tr->msg->cseq->p);
633
+
634
+      if(!cseq){
635
+          ERROR("missing CSeq\n");
636
+          return;
637
+      }
638
+    tmo_evt = new AmSipTimeoutEvent(evt, cseq->num);
639
+    }
640
+    break;
641
+
642
+  case AmSipTimeoutEvent::noPRACK: {
643
+      sip_msg msg(tr->retr_buf, tr->retr_len);
644
+
645
+      char* err_msg=0;
646
+      int err = parse_sip_msg(&msg, err_msg);
647
+      if (err) {
648
+          ERROR("failed to parse (own) reply[%d]: %s.\n", err, 
649
+              err_msg ? err_msg : "???");
650
+          return;
651
+      }
652
+
653
+      AmSipReply reply;
654
+      if (! sip_msg2am_reply(&msg, reply)) {
655
+          ERROR("failed to convert sip_msg to AmSipReply.\n");
656
+          return;
657
+      }
658
+
659
+      AmSipRequest request;
660
+      sip_msg2am_request(tr->msg, request);
661
+      request.tt = trans_ticket(tr, buk);
662
+
663
+      DBG("Reply timed out: %i %s\n",reply.code,reply.reason.c_str());
664
+      DBG_PARAM(reply.callid);
665
+      DBG_PARAM(reply.local_tag);
666
+      DBG_PARAM(reply.remote_tag);
667
+      DBG("cseq = <%i>\n",reply.cseq);
668
+
669
+      tmo_evt = new AmSipTimeoutEvent(evt, request, reply);
670
+    }
671
+    break;
672
+
673
+  default:
674
+    ERROR("BUG: unexpected timout event type '%d'.\n", evt);
675
+    return;
676
+  }
677
+
678
+  AmEventDispatcher::instance()->post(c2stlstr(tr->to_tag), tmo_evt);
679
+}
680
+
681
+#undef DBG_PARAM
545 682
 
546 683
 void SipCtrlInterface::prepare_routes_uac(const list<sip_header*>& routes, string& route_field)
547 684
 {
... ...
@@ -49,6 +49,9 @@ class udp_trsp;
49 49
 class SipCtrlInterface:
50 50
     public sip_ua
51 51
 {
52
+    void sip_msg2am_request(const sip_msg *msg, AmSipRequest &request);
53
+    bool sip_msg2am_reply(sip_msg *msg, AmSipReply &reply);
54
+    
52 55
     void prepare_routes_uac(const list<sip_header*>& routes, string& route_field);
53 56
     void prepare_routes_uas(const list<sip_header*>& routes, string& route_field);
54 57
 
... ...
@@ -104,7 +107,11 @@ public:
104 107
      */
105 108
     void handle_sip_request(const trans_ticket& tt, sip_msg* msg);
106 109
     void handle_sip_reply(sip_msg* msg);
110
+    void handle_reply_timeout(AmSipTimeoutEvent::EvType evt,
111
+        sip_trans *tr, trans_bucket *buk=0);
112
+#if 0
107 113
     void timer_expired(sip_trans* trans, sip_timer_type tt);
114
+#endif
108 115
 };
109 116
 
110 117
 
... ...
@@ -119,7 +119,7 @@ bool SessionTimer::onSendReply(const AmSipRequest& req,
119 119
     
120 120
   if (((session_refresher_role==UAC) && (session_refresher==refresh_remote)) 
121 121
       || ((session_refresher_role==UAS) && remote_timer_aware))
122
-    m_hdrs += SIP_HDR_COLSP(SIP_HDR_REQUIRED)  "timer"  CRLF;
122
+    m_hdrs += SIP_HDR_COLSP(SIP_HDR_REQUIRE)  "timer"  CRLF;
123 123
     
124 124
   hdrs += m_hdrs;
125 125
 
... ...
@@ -28,11 +28,13 @@
28 28
 #include "hash_table.h"
29 29
 #include "hash.h"
30 30
 
31
+#include "AmSipHeaders.h"
31 32
 #include "sip_parser.h"
32 33
 #include "parse_header.h"
33 34
 #include "parse_cseq.h"
34 35
 #include "parse_via.h"
35 36
 #include "parse_from_to.h"
37
+#include "parse_100rel.h"
36 38
 #include "sip_trans.h"
37 39
 
38 40
 #include "log.h"
... ...
@@ -112,14 +114,15 @@ sip_trans* trans_bucket::match_request(sip_msg* msg)
112 114
 	    }
113 115
 
114 116
 	    if(msg->u.request->method != (*it)->msg->u.request->method) {
115
-		if( (msg->u.request->method == sip_request::ACK) &&
116
-		    ((*it)->msg->u.request->method == sip_request::INVITE) ) {
117
-		    
118
-		    t = match_200_ack(*it,msg);
119
-		    if(t){
120
-			break;
121
-		    }
122
-		}
117
+                if((*it)->msg->u.request->method == sip_request::INVITE) {
118
+                    if(msg->u.request->method == sip_request::ACK) {
119
+                        if ((t = match_200_ack(*it,msg)))
120
+                            break;
121
+                    } else if(msg->u.request->method == sip_request::PRACK) {
122
+                        if ((t = match_1xx_prack(*it,msg)))
123
+                            break;
124
+                    }
125
+                }
123 126
 		
124 127
 		continue;
125 128
 	    }
... ...
@@ -348,6 +351,50 @@ sip_trans* trans_bucket::match_200_ack(sip_trans* t, sip_msg* msg)
348 351
     return t;
349 352
 }
350 353
 
354
+sip_trans* trans_bucket::match_1xx_prack(sip_trans* t, sip_msg* msg)
355
+{
356
+  /* first, check quickly if lenghts match (From tag, To tag, Call-ID) */
357
+
358
+  sip_from_to* from = dynamic_cast<sip_from_to*>(msg->from->p);
359
+  sip_from_to* t_from = dynamic_cast<sip_from_to*>(t->msg->from->p);
360
+  if(from->tag.len != t_from->tag.len)
361
+    return 0;
362
+
363
+  sip_from_to* to = dynamic_cast<sip_from_to*>(msg->to->p);
364
+  if(to->tag.len != t->to_tag.len)
365
+    return 0;
366
+
367
+  if(msg->callid->value.len != t->msg->callid->value.len)
368
+    return 0;
369
+
370
+
371
+  sip_rack *rack = dynamic_cast<sip_rack *>(msg->rack->p);
372
+  sip_cseq* t_cseq = dynamic_cast<sip_cseq*>(t->msg->cseq->p);
373
+  if (rack->cseq != t_cseq->num)
374
+    return 0;
375
+
376
+  if (rack->rseq != t->last_rseq)
377
+    return 0;
378
+
379
+  if (rack->method != t->msg->u.request->method)
380
+    return 0;
381
+
382
+
383
+  /* numbers fit, try content */
384
+
385
+  if(memcmp(from->tag.s,t_from->tag.s,from->tag.len))
386
+      return NULL;
387
+
388
+  if(memcmp(msg->callid->value.s,t->msg->callid->value.s,
389
+            msg->callid->value.len))
390
+      return NULL;
391
+  
392
+  if(memcmp(to->tag.s,t->to_tag.s,to->tag.len))
393
+      return NULL;
394
+  
395
+  return t;
396
+}
397
+
351 398
 sip_trans* trans_bucket::add_trans(sip_msg* msg, int ttype)
352 399
 {
353 400
     sip_trans* t = new sip_trans();
... ...
@@ -66,6 +66,7 @@ private:
66 66
     trans_list::iterator find_trans(sip_trans* t);
67 67
 
68 68
     sip_trans* match_200_ack(sip_trans* t,sip_msg* msg);
69
+    sip_trans* match_1xx_prack(sip_trans* t,sip_msg* msg);
69 70
 
70 71
 public:
71 72
 
72 73
new file mode 100644
... ...
@@ -0,0 +1,122 @@
1
+
2
+#include <limits.h>
3
+
4
+#include "log.h"
5
+#include "AmSipHeaders.h"
6
+#include "sip_parser.h"
7
+#include "parse_common.h"
8
+#include "parse_100rel.h"
9
+
10
+
11
+#define EAT_WS(_c_, _end_) \
12
+  do { \
13
+    while ((_c_) < (_end_) && (*(_c_) == ' ' || *(_c_) == '\t')) \
14
+      (_c_) ++; \
15
+  } while (0)
16
+
17
+#define READ_NUMBER(_no, _c_, _end_) \
18
+  do { \
19
+    register bool fin; \
20
+    (_no) = 0; \
21
+    for (fin = false; !fin && (_c_) < (_end_); ) { \
22
+      switch (*(_c_)) { \
23
+        case '0' ... '9': \
24
+          if (UINT_MAX - (_no) < (unsigned)*(_c_)) { \
25
+            INFO("not an uint32_t.\n"); \
26
+            goto error; \
27
+          } \
28
+          (_no) = (_no) * 10 + *(_c_) - '0'; \
29
+          break; \
30
+        default: \
31
+          fin = true; \
32
+          continue; \
33
+      } \
34
+      (_c_) ++; \
35
+    } \
36
+  } while (0)
37
+
38
+#define EAT_TOKEN(_c_, _end_) \
39
+  do { \
40
+    while ((_c_) < (_end_) && IS_TOKEN(*(_c_))) \
41
+      (_c_) ++; \
42
+  } while (0)
43
+
44
+
45
+bool parse_rseq(unsigned *_rseq, const char *start, int len)
46
+{
47
+  unsigned rseq;
48
+  const char *pos = start;
49
+  const char *end = start + len;
50
+  const char *sav;
51
+
52
+  EAT_WS(pos, end);
53
+
54
+  sav = pos;
55
+  READ_NUMBER(rseq, pos, end);
56
+  if (sav == pos)
57
+    goto error;
58
+
59
+  *_rseq = rseq;
60
+  DBG("parsed sequence content: %u.\n", rseq);
61
+  return true;
62
+
63
+error:
64
+  INFO("invalid content in sequence header content <%.*s>.\n", len, start);
65
+  return false;
66
+}
67
+
68
+
69
+bool parse_rack(sip_rack *rack, const char *start, int len)
70
+{
71
+  const char *pos;
72
+  const char *sav;
73
+  const char *end = start + len;
74
+  unsigned rseq, cseq;
75
+  cstring method_str;
76
+  cstring cseq_str;
77
+  int method;
78
+
79
+  pos = start;
80
+
81
+  EAT_WS(pos, end);
82
+
83
+  sav = pos;
84
+  READ_NUMBER(rseq, pos, end);
85
+  if (pos == sav)
86
+    goto error;
87
+  
88
+  EAT_WS(pos, end);
89
+
90
+  sav = pos;
91
+  READ_NUMBER(cseq, pos, end);
92
+  if (pos == sav)
93
+    goto error;
94
+  cseq_str.s = sav;
95
+  cseq_str.len = pos - sav;
96
+
97
+  EAT_WS(pos, end);
98
+
99
+  sav = pos;
100
+  EAT_TOKEN(pos, end);
101
+  method_str.s = sav;
102
+  method_str.len = pos - sav;
103
+