الخلاصة

  • يستخدم Replaces قيم Call-ID وto-tag وfrom-tag لتحديد حوار SIP واحد نشأ عن INVITE. نجاح المطابقة لا يثبت هوية المبادر ولا يمنحه تلقائياً حق إزاحة مشارك آخر.
  • يحتفظ وكيل المستخدم المستقبِل بقرار محلي مستقل: يتحقق من التفويض، ثم يحاول قبول INVITE الجديد، وبعد نجاحه فقط يرسل BYE للحوار المؤكد أو CANCEL لحوار مبكر مناسب. وإذا فشل القبول يبقى الحوار القديم بلا تغيير.
  • قبول REFER، ومصدر Referred-By، واستجابة 2xx لدعوة الاستبدال، وإنهاء الطرف القديم، واستمرار الوسائط والتسجيل والفوترة كلها أدلة مختلفة. ضغطها في إشارة نجاح واحدة يمنح المنصة سلطة لم يمنحها البروتوكول.

بين طلب التحويل وتسليم المحادثة

لنفترض أن موظف دعم يتحدث مع عميل، ثم يفتح حواراً استشارياً مع خبير. بعد الاتفاق يرسل الموظف REFER إلى جهاز العميل. وقد يحمل Refer-To قيمة Replaces مرمّزة تشير إلى الحوار الاستشاري بين الموظف والخبير. يقبل جهاز العميل REFER.

هذا القبول لا يعني أن العميل صار يتحدث مع الخبير. على جهازه أن ينشئ INVITE جديداً. يجب أن يصل إلى نسخة UA الصحيحة عند الخبير. ويجب أن يطابق Replaces حواراً واحداً بعينه. ثم يقرر الطرف المستهدف هل المبادر مخوّل بالاستبدال، وبعد ذلك هل يمكن قبول الوسائط والمفاتيح والموارد المطلوبة.

يمكن أن تنجح خطوة وتفشل التالية من دون تناقض. قد يقبل الجهاز REFER ثم يرفض المستخدم متابعته. قد يجد الهدف الحوار لكنه لا يعترف بسلطة المبادر. وقد يقر بالسلطة ثم يعجز عن إنشاء التشفير أو الوسائط. وقد يقبل الحوار الجديد بينما يظل إنهاء الطرف القديم بحاجة إلى إثبات.

يعرّف RFC 3891 أداة محدودة: INVITE جديد يقترح استبدال حوار موجود منطقياً. لا يحوّل من يعرف المعرّفات أو يمرر الرسالة إلى صاحب المحادثة.

لماذا يأتي الاستبدال في INVITE جديد

لا يعدّل Replaces الحوار القديم في مكانه. يظهر في INVITE جديد له Call-ID مستقل. وبذلك يبقى الاستبدال المقترح خاضعاً لقواعد قبول جلسة جديدة.

يمنع الحقل الصريح الاستنتاجات الضمنية من حقول عادية، ويجعل القصد قابلاً للرؤية. ويفصل Call-ID الجديد هوية الحوار المرشح عن الحوار القديم. وإذا لم يفهم UA الامتداد، يمكن أن يفشل من دون أن يعبث بالمكالمة العاملة.

يعلن الطرف دعمه عبر Supported: replaces، ويمكن للمرسل استخدام Require: replaces إذا احتاج إلى فشل صريح عند غياب الدعم. وما زال سجل IANA لمعاملات SIP يسجل الحقل وoption tag.

لكن الدعم وصف لقدرة البرنامج، لا موافقة مسبقة على كل استبدال. قد يسمح الجهاز بالانتقال بين جهازين للمستخدم نفسه، ويرفضه بين مستأجرين مختلفين، أو يشترط تأكيداً بشرياً في سياق حساس.

ولا يساوي Replaces طريقة REFER. تطلب REFER من المستقبِل الاتصال بمورد. أما Replaces فيخبر هدف INVITE الجديد أي حوار محلي يُقترح استبداله. يجتمعان كثيراً في التحويل، لكن لكل منهما موافقته ونتيجته.

المعرّفات تُقرأ من منظور الطرف المستقبِل

تتضمن قيمة Replaces قيمة Call-ID وto-tag واحداً وfrom-tag واحداً. يقارن UAS المستقبِل to-tag بالـlocal tag لديه، وfrom-tag بالـremote tag.

لا يكفي نسخ ترتيب To وFrom من رسالة سابقة. ذلك الترتيب يصف منظور تلك الرسالة، بينما يجب بناء منظور الطرف الذي تلقى INVITE الجديد. ويمكن أن يزداد الالتباس مع SBC أو B2BUA أو استعادة الحالة بعد failover.

يقدم سجل تصحيحات RFC 3891 مثالاً واضحاً: تصحيح تحريري Verified عكس tags وُضعت في الاتجاه الخطأ في مثال early dialog، وهناك تصحيح تحريري آخر Held for Document Update. لا يغيران القاعدة المعيارية، بل يثبتان أن المثال لا يغني عن فهم local/remote.

ويقتصر النطاق على حوار واحد. ينص RFC 3891 على أن Replaces لا يطابق حوارات متعددة، ولا مكالمة كاملة، ولا transaction كاملة، ولا شجرة proxy forking. فإذا أنشأ INVITE الأصلي عدة فروع مبكرة، لا يشمل استبدال فرع واحد أشقاءه. وإذا أُعيد توجيه INVITE الجديد إلى Contacts مختلفة، فلا يرث منطق التفرع الأصلي.

أما النظام التجاري فقد يجمع تحت «مكالمة» واحدة طرف العميل وطرف الموظف والاستشارة والطابور والتسجيل والفوترة. لا يجوز أن يغيّر حدث حوار SIP واحد كل هذا التجميع بلا قاعدة مصالحة صريحة.

المعرّف لا يحتوي الفعل

يرفض UAS بأمر 400 وجود أكثر من Replaces، أو وجوده خارج INVITE، أو اقترانه بدلالة call control متناقضة. وإذا طابق أكثر من حوار، يعامله UA كأنه لم يطابق شيئاً.

يوضح RFC 3911 الفرق. يستخدم Join أيضاً Call-ID وtags لتحديد حوار، لكنه يقترح إضافة الحوار الجديد إلى conversation space. يقترح Replaces إنهاء الحوار المحدد وإحلال الجديد محله. المعرّفات متشابهة والفعلان متعارضان.

إذن يجيب الثلاثي عن «أي كائن؟». ويجيب اسم الحقل عن «أي عملية؟». وتجيب المصادقة عن «من يطلب؟». ويجيب التفويض عن «هل يملك الحق؟». ويجيب القبول عن «هل يمكن التنفيذ الآن؟». وتجيب الأحداث اللاحقة عن «ماذا حدث فعلاً؟».

غياب المطابقة، أو مطابقة حوار لم ينشأ عن INVITE، يؤدي إلى 481. وإذا كان الحوار قد انتهى بالفعل، فمن المستحسن رفض الطلب بـ603 كي لا تتحول رسالة متأخرة إلى رنين جديد بلا سياق.

ولا يعني 481 احتيالاً تلقائياً. قد يكون السبب عكس tags، أو حالة قديمة، أو نسخة جهاز خاطئة، أو ضياع replication عند failover، أو retargeting، أو سباقاً مع نهاية طبيعية. يجب أن تسجل المراقبة السبب الفعلي.

المطابقة الصحيحة تفتح سؤال التفويض ولا تغلقه

عندما يطابق Replaces حواراً نشطاً، يفرض RFC 3891 على UA التحقق من أن مبادر INVITE الجديد مخوّل باستبداله. المطابقة وحدها لا تؤدي هذه الوظيفة.

قد تسمح السياسة بهوية صودق عليها باعتبارها مكافئة للمستخدم المستبدَل. وقد تقدم Referred-By دليلاً على أن المشارك الآخر بدأ العملية عبر REFER. كما يمكن لـUAS استخدام سياسة محلية أخرى، بل تطبيق سياسة مختلفة على الحوار الجديد والقديم.

هذه أمثلة لأساس القرار، وليست تفويضاً عاماً. يمكن لمفاتيح مشتركة أن تمثل أجهزة متعددة لهوية واحدة أو نيابة مشروعة. وإذا اتسع نطاقها، منحت service account قدرة على استبدال مكالمات مستخدمين أو tenants آخرين. تثبت المصادقة السيطرة على credential، لا الإرادة الحالية لشخص ولا سلطته القانونية أو التعاقدية.

ويضع RFC 3892 حدوداً لـReferred-By. ينقل referee معلومات referrer إلى refer target، ولذلك يستطيع رؤيتها أو تعديلها. يمكن لقيمة غير محمية أن تدخل في السياسة، لكنها تظل مشبوهة إذا أثرت في القبول أو عُرضت للمستخدم. يستطيع الهدف طلب token صالح ورفض token غير صالح.

حتى token الصحيح يحمي تصريحات محددة عن referral. ولا يثبت تلقائياً استمرار الموافقة على التسجيل، أو سلطة إدارية، أو تفويضاً مالياً، أو أهلية لخدمة منظمة. يجب أن تحدد السياسة المحلية هذا النطاق وتوثقه.

معرفة الحوار دليل مشروط وليست ملكية

يعرّف RFC 4538 حقل Target-Dialog للطلبات التي قد يعتمد تفويضها على معرفة حوار قائم. إذا أُنشئ الحوار عبر SIPS، وكانت identifiers عشوائية بالقدر الكافي، وكانت الثقة بمسار الطلب الأصلي واضحة، فقد تدعم هذه المعرفة قرار التفويض.

الشروط جزء من الدليل. في مسار غير محمي قد يتعلم متنصت القيم نفسها. وحتى في SIPS يستطيع UAS أن يفهم Target-Dialog ويقرر أن العلاقة المثبتة لا تكفي للعمل المطلوب.

معرفة السياق ليست بلا قيمة، لكنها ليست سيادة عليه. يحدد method والهوية وحماية المسار والسياسة المحلية قيمة المعرفة ونطاقها.

وهنا يظهر سؤال Heng Lu في مستوى البروتوكول: من كانت لديه، تحديداً، سلطة إقرار القرار الملزم؟ ليست الإجابة «من حضر أو عرف المعرّف»، بل الطرف المحلي الذي يحتفظ بالحوار ويتحمل أثر قطعه.

قبول الجلسة حاجز مستقل بعد التفويض

بعد نجاح التفويض، يحاول UAS قبول INVITE الجديد، وإعادة تخصيص الواجهة والموارد، ثم إغلاق الحوار القديم. ويمكن للمحاولة أن تفشل.

يذكر RFC 3891 تعذر QoS أو keying المطلوبين وعدم توافق الوسائط. وقد تفشل الموارد أو أي قاعدة قبول عادية أخرى. عندئذ يجب إرسال الخطأ المناسب وترك الحوار القديم بلا تغيير.

يحصر هذا الترتيب الضرر. الإنهاء عند match يعطي identifiers مسربة قدرة على قطع المكالمة. والإنهاء بعد المصادقة يضحي بجلسة تعمل أمام عرض تقني غير قابل للقبول. انتظار قبول الحوار الجديد يحافظ على مسار الرجوع.

في confirmed dialog من دون early-only، يرسل UAS أولاً 2xx إلى INVITE الجديد ثم BYE إلى الحوار القديم. وفي early dialog بدأه UA نفسه، يقبل الجديد ثم يرسل CANCEL إلى الدعوة السابقة.

يقيّد early-only القصد بفرع ما زال في طور الرنين. فإذا تأكد الحوار، تكون الاستجابة 486. وإذا لم يبدأ UA المستقبِل الحوار المبكر، تكون الاستجابة 481 وتبقى الحالة؛ فآلية الحوار الواحد لا تستطيع إعادة إنتاج forking بدأه طرف آخر.

يثبت 2xx قبول الحوار الجديد بقوة. لكنه لا يثبت وحده وصول BYE، أو اختفاء legs داخل B2BUA، أو انتهاء الفروع الأخرى، أو استمرار RTP، أو انتقال التسجيل والفوترة بطريقة صحيحة.

لـREFER موافقة سابقة ونتيجة لاحقة

ينصح RFC 3515 UA الذي يقبل REFER صحيحاً بطلب موافقة المستخدم، تفاعلياً أو عبر policy معدة. وبعد الموافقة فقط يتصل بالمورد المحدد وفق القواعد العادية.

في attended transfer يحمل Refer-To غالباً Replaces بعد escaping. ينشئ transferee INVITE الجديد، ثم ينفذ transfer target المطابقة والتفويض والقبول بصورة مستقلة. قبول REFER لا يلزم الهدف.

وتختلف حالة REFER transaction عن نتيجة referenced action. تنقل NOTIFY التقدم والنتيجة النهائية. يحدّث RFC 7647 استجابة القبول إلى 200 بدلاً من 202، ويوضح GRUU، ويفرض Target-Dialog عند الحاجة إلى تحديد حوار قائم. مع ذلك يبقى 200 جواباً عن REFER لا شهادة على اكتمال النقل كله.

ويؤكد RFC 5589 أن نجاح REFER transaction لا ينهي الجلسة بين transferor وtransferee؛ يلزم BYE لاحق. وعند busy أو no answer أو رفض Replaces، تتيح مسارات الفشل إخراج الطرف من hold واستعادة الجلسة المعروفة.

لذلك ينبغي الفصل بين موافقة REFER، واستجابته، ونتيجة INVITE المحفّز، وإنهاء الحوار القديم، ونتيجة الخدمة للمستخدم.

بناء سجل يمكن إعادة تركيبه

احتفظ بـCall-ID الخاص بـINVITE الجديد منفصلاً عن ثلاثي Replaces. أضف منظور local/remote لدى المستقبِل، وحالة الحوار وقت المطابقة، وContact، وroute، وfork branch، وearly-only.

سجل أساس القرار: الهوية المصادق عليها، وReferred-By، والتحقق من token، وإصدار policy، وقاعدة المستخدم أو tenant، ومصدر الموافقة وسببها. لا يكفي authorized=true بلا مصدر.

في القبول احفظ response code وSDP fingerprint وcodecs والاتجاهات وkeying وQoS والنسخة المستهدفة وACK. وبعد 2xx اربط BYE أو CANCEL للحوار القديم، واستجابته، وretransmissions، وآخر signaling وmedia شوهدت عليه.

ضع REFER في timeline نفسها مع Call-ID وCSeq وTarget-Dialog وRefer-To fingerprint والموافقة والاستجابة وNOTIFY، لكن لا تجعله يكتب فوق نتيجة INVITE. قارن النجاح المعلن بالأطراف الفعلية.

يجب أن تشمل الاختبارات tags معكوسة، ومطابقة غامضة، وحواراً منتهياً، وسباق early-only، وغياب الدعم، ورفض التفويض بعد match صحيح، وtoken غير صالح، وفشل media/key/QoS، وbusy/no-answer، وضياع BYE بعد 2xx، وفروعاً متبقية، وretargeting، وضياع الحالة عند failover، وB2BUA mappings لا يمكن استعادتها.

السجل الجيد يجيب في حقول منفصلة: ماذا طُلب، وأي حوار سُمّي، ومن كان مخولاً، وما الذي قُبل، وما الذي انتهى، وما الخدمة التي وصلت إلى الإنسان فعلاً.

المصادر