اطلاعیه

بستن
هنوز اطلاعیه ای در دست نیست.

تفاوت asterisk با freeswitch چیست ؟

بستن
X
 
  • فیلتر کردن
  • زمان
  • نمایش
Clear All
پست های جدید

    تفاوت asterisk با freeswitch چیست ؟

    فری سوئیچ (freeswitch) در مقایسه با استریسک (asterisk) چطور عمل می کند؟ چرا شما با applicationهای جدید کارتون رو شروع کردید؟ اینها سوالاتی هستند که اخیرا از من پرسیده می شوند و باعث شدند که تصمیم به ارائه پاسخ در مورد آنها به فعالان و مشتاقان در زمینه سیستم های تلفنی نرم افزاری و کسانی که علاقه مند به دانستن تفاوت های موجود بین این دو نرم افزار هستند، بگیرم. تجربه بسیار زیادی در زمینه کارکردن با هردوی این نرم افزارها داشته ام، در حدود ۳ سال در زمینه توسعه استریسک و به عنوان بنیان گذار فری سوئیچ. در ابتدا تجربیات خود در مورد استریسک را مطرح نموده سپس انگیزه و نحوه ایجاد فری سوئیچ را شرح خواهم داد.

    اولین بار در سال ۲۰۰۳ از استریسک استفاده کردم. در آن زمان نسخه پیش از 1.0 استفاده می شد و آشنایی کمی با VoIP داشتم. پس از دانلود و نصب، از اینکه پس از اتصال تلفن به کامپیوترم صدای بوق در آن شنیده می شد بسیار خوشحال بودم. چند روز مشغول کار با dial plan بودم و ایده هایی که با یک تلفن متصل به لینوکس قابل پیاده سازی بودند اجرایی می کردم. به علت سابقه زیاد در زمینه برنامه نویسی وب انواع ایده ها مانند تطابق شماره تماس گیرنده با شماره اکانت به منظور تشخیص علت تماس و موارد مشابه به ذهنم می رسید. سپس زمانی که قصد استفاده از تطابق الگوهای تماس در dial plan را داشتم نوشتن اولین ماژول را آغاز کردم. به این ترتیب نسخه ابتدایی app_perl، که اکنون res_perl نامیده می شود، شکل گرفت و یک مفسر Perl5 را به استریسک اضافه کرده بودم.

    پس از کسب این تجربیات، ایجاد زیرساختی مبتنی بر استریسک را برای صف تماس های ورودی شروع کردم. نمونه اولیه را بر اساس app_queue و واسط مدیریت استریسک که به اختصار AMI نامیده می شود ایجاد کردم (استفاده از مخفف احساس خوبی ایجاد می کند). واقعا جالب بود! این امکان وجود داشت که از خطوط تلفن با یک خط T1 تماس گرفت و وارد صف تماس شد و اپراتوها نیز پس از تماس وارد صف شده و تماس ها را پاسخ می دادند. وقتی که از طریق وب وضعیت صف ها و حضور اپراتورها را مشاهده می کردم به نظرم همه چیز عالی بود. حتی صفحه به طور خودکار هر چند ثانیه بروزرسانی می شد اما زمانیکه مرورگر مدت زیادی در حال بارگزاری باقی ماند برای اولین بار این لغت را شنیدم، Deadlock! لغتی که هیچوقت نمی توانم فراموش کنم.

    این اولین بار بود که چنین اتفاقی رخ می داد اما آخرین بار نبود. مطالب زیادی در مورد GNU Debugger یادگرفتم که آغاز اتفاقات زیادی بود. وقوع Deadlock در ماژول صف، Deadlock در ماژول مدیریت و کنسول. این اتفاقات اندکی باعث ناامیدی من شد اما ادامه دادم. در همین حین با Segmentation Fault برخورد کردم که برنامه نویسان با آن آشنایی کامل دارند. پس از حدود یک سال درگیری با باگ ها، بر زبان c تسلط زیادی پیدا کرده بودم و مهارتم در زمینه دیباگ کردن افزایش پیدا کرده بود. بستر مورد استفاده من شامل ۷ سرور استریسک بود که تعداد زیادی تماس در حد DS3 را سرویس دهی می کرد و مقدار زیادی کد برای استریسک نوشته بودم که کپی رایت آن ها را در اختیار دارم:


    در سال ۲۰۰۵ شهرت خوبی بین برنامه نویسان استریسک داشتم. حتی در فایل CREDITS و در کتاب “Asterisk, The Future of Telephony” از من قدردانی شد. علاوه بر نرم افزارهای متعددی که در سورس کد اصلی استریسک داشتم مجموعه ای از کدها که برای استریسک نوشته بودم روی سایت خود ایجاد و نگهداری می کردم. (هنوز در آدرس http://www.freeswitch.org/node/50 در دسترس هستند).

    علی رغم این تجربیات همچنان نتوانسته بودم از deadlockها و crashها رها شوم. با استفاده از اسکریپت های راه اندازی مجدد و استفاده از 7 سرور، به خوبی این مشکلات را مخفی کرده بودم اما هیچ راهی برای توسعه بیشتر بستر مورد استفاده خود پیدا نمی کردم. باید از برخی قابلیت ها چشم پوشی می کردم چرا که به علت نحوه طراحی استریسک به درستی عمل نمی کردند.

    استریسک ازطراحی ماژولار برخوردار است به طوریکه یک هسته مرکزی سایر ماژول ها را بارگذاری می کند که این ماژول ها هر کدام قابلیت های بیشتری را به سیستم اضافه می کنند. ماژول ها برای پیاده سازی پروتکل های خاصی مانند SIP و یا کاربردهایی نظیر IVRها و هچنین اینترفیس های خارجی مانند Management Interface استفاده می شوند. هسته استریسک از مدل threading استفاده می کند اما در مصرف threadها بسیار محافظه کارانه عمل می کند. تنها کانال هایی که آغاز کننده تماس و یا اجرا کننده یک کاربرد هستند دارای thread مجزا هستند. طرف B تماس (دریافت کننده تماس) از thread مربوط به طرف A تماس (ایجاد کننده تماس) استفاده می کند مگر اینکه اتفاقی مانند ترانسفر رخ دهد. در این حالت کانال وارد مد thread شده و فرآیندی به نام channel masquerade آغاز می شود. در این فرآیند تمامی داده های داخلی یک کانال از یک شئ موجود در حافظه به شئ دیگری در حافظه منتقل می شوند. روشی که در توضیحاتی که در کد استریسک آورده شده ناخوشایند توصیف شده است. همین اتفاق در جهت عکس نیز رخ می دهد یعنی زمانیکه که یک thread پس از ایجاد نسخه کپی از یک کانال از بین می رود و کانال اصلی قطع می شود. برای این کار ساختار cdr باید به نحوی تغییر کند که کانال کپی ایجاد شده را به عنوان یک تماس جدید در نظر نگیرد. این باعث می شود هنگام ترانسفر کردن یک تماس ۳ یا ۴ کانال در سیستم مشاهده شود.

    بخش زیر از توضیحات موجود درکد استریسک گرفته شده است:
    ” این یک عمل واقعا کشنده است. ما یک کانال جدید ایجاد کرده و داده های کانالی که قرار است thread آن ازبین برود را در کانال جدید قرار می دهیم. این کار بعد از حذف داده های داخلی شئ مربوط به کانالی که جدیدا ایجاد شده است آغاز می شود. مطمئن نیستم که قراره در آینده هم این کار حفظ بشه، چراکه علی رغم قابلیت های مفید، روش بسیار ناخوشایندی هست.”

    این روش برای انتقال یک کانال از یک thread به thread دیگر بوده و سرآغاز بسیاری از مشکلات برای برنامه نویسان است. این روش نامطئن استفاده از threadها یکی از انگیزه های من برای کدنویسی مجدد استریسک بود.

    استریسک از linked-listها برای نگهداری داده های کانال های موجود در سیستم استفاده می کند که مجموعه ای از بخش های حافظه است که به طور زنجیروار با استفاده از اشاره گرها به هم متصل هستند و قابلیت ایجاد و حفظ کانال ها را به طور نامحدود (تنها محدودیت میزان حافظه سیستم می باشد) ایجاد می کنند.

    این لیست ها در برنامه نویسی کاربردهای بسیاری دارند اما زمانیکه در یک برنامه که threadهای متعددی در آن ایجاد می شود مورد استفاده قرار می گیرند مدیریت آن ها بسیار مشکل می شود. در چنین برنامه ای نیاز به استفاده از میوتکس می باشد که نقش چراغ راهنما را برای threadها ایفا می کند تا تنها یک thread اجازه دسترسی به لیست را داشته باشد چرا که در غیر این صورت هنگامی یک thread در حال دنبال کردن زنجیره لیست است ممکن است یک thread دیگر ساختار این زنجره را تغییر دهد. نتیجه این اتفاق می تواند بسیار مهلک باشد، به عنوان مثال زمانیکه یک thread در حال از بین بردن یک کانال می باشد ممکن است thread دیگر به آن دسترسی پیدا کند که این امر باعث وقوع segmentation fault شده که نتیجه آن قطع اجرای برنامه و باالتبع قطع تمامی تماس ها می شود. اکثر ما پیام Avoiding initial deadlock را دیده ایم، این پیام نشانگر تلاش برای دسترسی انحصاری به یک کانال است که ممکن است تا ۱۰ بار انجام شود و در صورت عدم موفقیت بدون دسترسی انحصاری کانال در اختیار گرفته شود.

    واسط مدیریت یا AMI این قابلیت را ایجاد می کند که در سوکت ایجاد شده توسط کلاینت برای کنترل استریسک به طور مستقیم هر گونه داده ای به فرمت Manager Event نوشته شود. این داده ها اغلب خیلی ساخت یافته نیستند که همین موضوع تحلیل پیام های این پروتکل را مشکل می سازد.

    هسته استریسک به بعضی از ماژول های آن وابستگی دارد، نتیجه این وابستگی آنست که استریسک بدون وجود این ماژول ها قابل اجرا نیست چرا که بخشی از کد مورد نیاز برای اجرا در فایل مربوط به ماژول مورد نظر قرار دارد. برای برقراری تماس در استریسک، حداقل در نسخه 1.2، هیچ راهی غیر از بکاربردن app_dial و res_features وجود ندارد چرا که کد مربوط به ایجاد تماس در این دو ماژول قرار دارد. منطق ایجاد یک تماس و انجام اعمالی مانند یک تماس با چند مقصد (forked) در app_dial قرار دارد نه در هسته استریسک، و res_features شامل اعمال سطح بالا مانند برقراری ارتباط صدا می باشد.

    استریسک هیچ نظارتی بر API خود ندارد. اکثر توابع و ساختمان داده ها دارای دسترسی عمومی هستند و امکان استفاده اشتباه و یا دورزدن روش های مشخص از پیش تعیین شده وجود دارد. کد هسته بر این فرض استوار است که هر کانال دارای یک file descriptor است در صورتیکه این امر همیشه الزامی نیست. در بسیاری از قسمت های کد یک الگوریتم که عمل مشخصی را انجام می دهد، در کاربردهای مختلف به اشکال مختلف پیاده سازی و تکرار شده است.

    این تنها مختصری از مشکلاتی است که در استفاده از استریسک با آن ها روبه رو بودم. وقت خود را به عنوان یک کدنویس و سرورهای خود را برای نگهداری کد استریسک استفاده کردم و در نگهداری کد و رفع باگ های آن نقش داشتم. یک کنفرانس تلفنی هفتگی برای برنامه ریزی در مورد آینده استریسک و رفع مشکلاتی که عنوان شد سازماندهی کردم. مشکل این بود که وقتی کسی به لیست تغییرات اساسی که باید صورت گیرند نگاه می کرد و یا به حجم کاری که باید انجام شود و مقدار کدی که باید پاک شده و یا بازنویسی شود فکر می کرد انگیزه خود برای رفع این مشکلات را از دست می داد. من می دانستم که افراد زیادی حاضر به همراهی من در ایجاد نسخه ۲ و بازنویسی کد نیستند. این موارد علت تصمیم من برای انجام چنین کاری در سال 2005 بود.

    تمرکز اصلی من در فری سوئیچ این بود که از هسته شروع کرده و تمام اعمال مشترک را در آن قرار داده و در اختیار لایه های بالاتر برنامه قرار دهم. مانند استریسک، وب سرور آپاچی الهام بخش من بود و باعث شد یک طراحی ماژولار را در نظر بگیرم. از همان ابتدا اساس کار این بود که هر کانال thread مربوط به خود را داشته باشد بدون در نظر گرفتن اینکه چه کاری انجام می دهد و آن thread از یک ماشین حالت برای تعیین اعمالی که باید در هسته روی کانال انجام شوند استفاده می کند. این کار باعث می شود تا هر کانال مسیر مشخص و قابل پیش بینی را طی کند. در این صورت می توان در صورت لزوم برای انجام اعمال مهم در حالت های مختلفی که کانال در آن ها قرار می گیرد توابعی را برای اجرا مشخص کرد، مانند نحوه ی عملکرد متدها در ارث بری و برنامه نویسی شئ گرا.

    انجام این کار آسان نبود. در حین نوشتن کد فری سوئیچ بارها و بارها با مشکلاتی مانند segmentation fault و deadlock مواجه شدم (البته مورد اول بسیار بیشتر از مورد دوم بود). اما کد را از هسته ایجاد کرده و ادامه دادم. از آنجایی که هر کانال دارای thread مجزا می باشد، در مواقعی که نیاز به انجام عملی روی کانال می باشد از lock کردن برای خواند/نوشتن استفاده می شود و به جای استفاده از link list از الگوریتم های درهم سازی (hashing) استفاده شده. در نتیجه به هیچ عنوان امکان از بین رفتن یک کانال تا زمانی که یک thread به آن دسترسی دارد وجود ندارد. این عمل به تنهایی می تواند نیاز به فرایندی مانند Channel Masquerade را برطرف کند.

    از اکثر توابع و اشیاء که در هسته فری سوئیچ وجود دارند در مقابل فراخوانی نادرست محافظت می شود. این کار با مجبور کردن فراخوان به استفاده از روش های از پیش تعیین شده برای دسترسی صورت می گیرد. هر مفهومی که قابلیت توسعه داشته باشد یا توسط یک ماژول ارائه شود دارای یک اینترفیس کاملا مشخص می باشد که امکان استفاده از آن را فراهم می کند، بنابراین هسته هیچ وابستگی به ماژول ها ندارد.

    API قابل استفاده در فری سوئیچ به صورت لایه لایه ایجاد شده است، به این صورت که توابع هسته در پایین ترین سطح قرار داشته و در لایه های بالاتر تعداد توابع با افزایش قابلیت ها کم تر می شود.

    به عنوان مثال این امکان وجود دارد که یک تابع طولانی نوشته شود که برای بازکردن و پخش یک فایل صوتی به فرمت دلخواه در یک کانال از یک ماژول استفاده می کند. در لایه بعدی یک تابع ساده وجود خواهد داشت که یک فایل را برای یک کانال پخش می کند و این تابع تبدیل به یک کاربرد ساده می شود که روی یک کانال قابل اجراست. بنابر این شما می توانید آن را در dial plan یا در کد c خود استفاده کنید و یا در صورت لزوم یک ماژول دلخواه بنویسید که فایل را باز کرده و با استفاده از سایر ماژول های پخش فایل آن را در کانال پخش نمائید.

    فری سوئیچ شامل اینترفیس های مختلفی برای دسته های مختلف ماژول ها می باشد.

    Dialplan: نحوه برخورد با تماس را مشخص کرده و اطلاعات تماس را دریافت و مقصد تماس را مشخص می کند.

    Endpoint: اینترفیس پروتکل هایی مانند SIP، TDM و . . .

    ARS/TTS: تشخیص گفتار و خواند متن

    Directory: جستجوی پایگاه داده با فرمت درخواست هایی مشابه با LDAP

    Events: ماژول ها می توانند رخدادهای پیش فرض تعریف شده در هسته و یا رخدادهای مختص خود را وارد سیستم رخداد فری سوئیچ نمایند. این رخدادها توسط مصرف کننده های مورد نظر که می توانند سایر ماژول ها باشند قابل دریافت و استفاده هستند.

    Event Handlers: دسترسی به رخدادها و CDR از راه دور

    Formats: پخش فایل ها با فرمت های مختلف مانند wav

    Loggers: ثبت لاگ در کنسول یا فایل

    Languages: استفاده از زبان های embedded مانند Python و Javascript برای ارتباط با فری سوئیچ و استفاده از API فری سوئیچ

    Say: ماژول های مربوط به زبان های مختلف برای ایجاد و پخش پیام

    Timers: شمارنده های دقیق برای کاربردهایی مانند تعیین زمانبندی بین بسته ها

    Applications: کاربردهای قابل اجرا روی یک تماس، مانند پیامگیر

    FSAPI (FreeSWITCH API interface) : توابع قابل اجرا در کنسول، توابع XMLRPC، توابعی از نوع CGI و توابعی که در dial plan قابل اجرا هستند و ورودی و خروجی آن ها به صورت رشته است.

    XML: روش هایی برای دسترسی به تنظیمات XML از طریق هسته وجود دارد که امکان بروزرسانی پیکربندی و تنظیمات فری سوئیچ را به صورت بلادرنگ و در حین اجرا فراهم می کنند.

    ارتباط و تعامل بین همه ی ماژول های فری سوئیچ تنها از طریق API هسته و سیستم رخداد داخلی آن می باشد. تلاش بسیاری برای انجام این کار و جلوگیری از رفتار ناخواسته ماژول های خارجی انجام شده است.

    سیستم رخداد در فری سوئیچ تا حد ممکن با هدف ایجاد امکان پیگیری وقایع ایجاد شده است. طراحی فری سوئیچ با این فرض انجام شده است که بیشتر کاربران این نرم افزار از راه دور به آن متصل می شوند و یا از یک ماژول دلخواه برای دسترسی به اطلاعات تماس استفاده می کنند. بنابراین هر چیز مهمی که در فری سوئیچ اتفاق می افتد باعث ایجاد یک رخداد می شود. ساختار رخدادها بسیار شبیه ایمیل می باشد که دارای چندین سرآیند و متن اصلی هستند. رخدادها را می توان به صورت یک رشته با فرمت استاندارد و یا XML نمایش داد. می توان به تعداد دلخواه ماژول ایجاد کرد که به سیستم رخداد فری سوئیچ متصل شوند و رخدادهایی در مورد presence، وضعیت تماس و خطاهای موجود در سیستم دریافت کنند. ماژول mod_event_socket که جزو ماژول های اصلی فری سوئیچ می باشد امکان برقراری اتصال TCP و دریافت رخدادها را به منظور استفاده و ثبت لاگ فراهم می کند. به علاوه از همین اینترفیس می توان برای ارسال دستورات کنترل تماس و یا پخش و دریافت صوت به صورت دو طرفه استفاده کرد. این ارتباط می تواند هنگام برقراری یک تماس (ایجاد سوکت به یک سرور خارجی برای دریافت اطلاعات مورد نیاز برای مسیریابی تماس و سایر اعمال) ایجاد شود و یا توسط یک کاربر از راه دور به صورت ورودی برقرار شود.

    یک مفهوم مهم دیگر در فری سوئیچ XML Registry متمرکز می باشد. زمانیکه فری سوئیچ اجرا می شود یک فایل xml اصلی را باز و پس از پارس کردن محتوای آن در صورت لزوم چندین فایل xml دیگر را باز کرده و مقادیر موجود در آن ها را وارد حافظه می کند (این کار بدون محدودیت قابل تکرار است). برخی از این مقادیر متغیرهایی هستند که پس از بارگذاری، در سایر فایل های xml نیز قابل دسترسی هستند. به عنوان مثال با استفاده از قطعه xml زیر می توان یک متغیر global را مقداردهی کرد:

    کد PHP:
     <X-PRE-PROCESS cmd=”set” data=”moh_uri=local_stream://moh”/> 
    پس از این قطعه xml در تمام خطوط بعدی هر جایی که $${moh_uri} قرار گرفته باشد مقدار local_stream://moh با آن جایگزین می شود. در نهایت بخش های که وارد رجیستری شده و توسط ماژول ها و هسته قابل استفاده هستند موارد زیر هستند:

    Configuration: داده های مربوط به پیکربندی فری سوئیچ که رفتار نرم افزار را تعیین می کنند.

    Dialplan: نمایش dial plan به شکل xml که توسط mod_dialplan_xml برای مسیریابی تماس ها و اجرای کاربردها روی تماس ها استفاده می شود.

    Phrases: xmlهایی که برای ایجاد قابلیت چند زبانه بودن و پخش عباراتی که شامل چند فایل صوتی هستند، استفاده می شوند.

    Directory: مجموعه ای از domainها و کاربرها که برای registration و مدیریت اکانت ها استفاده می شود.

    با استفاد از ماژول های ارتباط با XML، شما می توانید ماژول خود را به XML Registry متصل کرده و به صورت بلادرنگ اطلاعات مورد نظر را هنگام ایجاد تماس، به جای فایل های XML استاتیک موجود استفاده کنید. با استفاده از این قابلیت قادر خواهید بود که registration را به صورت کاملا پویا انجام داده و یا پیام گیرهای صوتی و پیکربندی چندین نمونه فری سوئیچ در حال کار را به طور پویا ایجاد نمائید، درست مانند مدل استفاده شده در ارتباط مرورگرها با یک برنامه CGI.

    با استفاده از زبان هایی که در فری سوئیچ embed شده اند مانند Javascript، Java و Perl این امکان وجود دارد که کاربردهایی به شکل اسکریپت ایجاد کرد که می توانند قابلیت های موجود در فری سوئیچ را در قالب زبان های سطح بالاتر به کار گیرند.

    اولین جمله در تعریف پروژه فری سوئیچ این بود که یک هسته پایدار ایجاد شود تا بر مبنای آن سایر کاربردهای قابل توسعه ایجاد شوند. خوشحالم از اینکه اعلام کنم این پروژه در تاریخ 26 می 2008 با ارائه نسخه 1.0 فری سوئیچ به نام phonix کامل خواهد شد. به شهادت کسانی که نسخه فعلی را تست کرده اند کارآیی فری سوئیچ در مقابل استریسک در شرایط مشابه تا 10 برابر بهتر گزارش شده است.

    امیدوارم این توضیحات برای مشخص نمودن تفاوت های فری سوئیچ و استریسک کافی و علل آغاز پروژه فری سوئیچ را روشن کرده باشند. به علت درگیری گسترده با پروژه استریسک همواره به عنوان یک برنامه نویس آن باقی خواهم ماند و برای همه افراد آن پروژه در طراحی آینده آن آرزوی موفقیت دارم. حتی در صورت امکان برای نشان دادن حسن نیت خود در رابطه با استریسک، که شروع کار من در عرصه تلفن بود، کدهای قدیمی خود را برای عموم در دسترس قرار خواهم داد.

    استریسک یک PBX کدباز است و فری سوئیچ یک سوییچ نرم افزاری کدباز٫ فضای بسیار زیادی برای هر دو نرم افزار در بین سایر نرم افزارها مانند Call Weaver، Bayonne، sipX، OpenSER و سایرین وجود دارد. امیدوارم هر ساله با توسعه دهندگان این نرم افزارها و پروژه ها در ClueCon در Chicago ملاقات و گفتگو داشته باشم.
    ClueCon is an annual real-time communications conference created by the developers of FreeSWITCH and SignalWire. Join the FreeSWITCH team August 14th-17th in Chicago.


    ما می توانیم الهام بخش یکدیگر برای پیشرفت صنعت تلفن باشیم. مهمترین سوالی که شما می توانید از خود بپرسید اینست که “آیا این ابزار مناسب کار شما هست؟”

    نویسنده:
    Anthony Minessale (سازنده فری سوییچ)

    منبع 1:


    منبع 2:

    ترجمه مقاله :بابک یخ چالی
    آخرین ویرایش توسط Habili; در تاریخ/ساعت 02-06-2015, 10:27 PM.

درباره انجمن منطقه لینوکسی ها

انجمن منطقه لینوکسی ها با هدف ارتقاء سطح علمی کاربران در سطح جهانی و همچنین کمک به بالا بردن سطح علمی عمومی در زمینه های تخصصی فوق پایه گذاری شده است. انجمن منطقه لینوکسی ها از طریق کارشناسان و متخصصان پاسخگوی سوالات گوناگون کاربران مبتدی یا پیشرفته میباشد تا حد امکان تلاش شده که محیطی متنوع و کاربر پسند و به دور از هرگونه حاشیه جهت فعالیت کاربران در این انجمن ایجاد شود. لذا ما به صورت مستمر برای پیشرفت کمی و کیفی محتوی و اطلاعات انجمنمان میکوشیم که این برای ما ارزشمند و حائز اهمیت است. کلیه حقوق،اطلاعات و مقالات در این انجمن متعلق به سایت منطقه لینوکسی ها میباشد، و هرگونه نسخه برداری بدون ذکر منبع مورد پیگرد قانونی خواهد شد.

شبکه های اجتماعی
در حال انجام ...
X