mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-26 15:48:09 +00:00
Merge branch 'dev_refactor_cpp' of git://git.linphone.org/linphone into dev_refactor_cpp
This commit is contained in:
commit
8191c7b4f7
22 changed files with 619 additions and 1545 deletions
|
|
@ -31,6 +31,9 @@ Requires: %{pkg_prefix}ortp
|
|||
Requires: %{pkg_prefix}mediastreamer
|
||||
Requires: %{pkg_prefix}belle-sip
|
||||
Requires: %{pkg_prefix}belr
|
||||
%if @ENABLE_VCARD@
|
||||
Requires: %{pkg_prefix}belcard
|
||||
%endif
|
||||
%if @ENABLE_SOCI_STORAGE@
|
||||
Requires: %{pkg_prefix}soci
|
||||
%endif
|
||||
|
|
@ -83,7 +86,7 @@ rm -rf $RPM_BUILD_ROOT
|
|||
%files
|
||||
%defattr(-,root,root)
|
||||
%doc AUTHORS ChangeLog COPYING NEWS README.md TODO
|
||||
%if @ENABLE_DAEMON@ || @ENABLE_CONSOLE_UI@
|
||||
%if @ENABLE_DAEMON@ || @ENABLE_CONSOLE_UI@ || @ENABLE_TOOLS@
|
||||
%{_bindir}/*
|
||||
%endif
|
||||
%{_libdir}/*.so.*
|
||||
|
|
|
|||
|
|
@ -42,8 +42,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include <ortp/telephonyevents.h>
|
||||
#include <mediastreamer2/zrtp.h>
|
||||
#include <mediastreamer2/dtls_srtp.h>
|
||||
#include <bctoolbox/defs.h>
|
||||
#include <belr/grammarbuilder.h>
|
||||
#include "bctoolbox/defs.h"
|
||||
#include "bctoolbox/regex.h"
|
||||
#include "belr/grammarbuilder.h"
|
||||
|
||||
#include "mediastreamer2/dtmfgen.h"
|
||||
#include "mediastreamer2/mediastream.h"
|
||||
|
|
@ -53,6 +54,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include "mediastreamer2/msjpegwriter.h"
|
||||
#include "mediastreamer2/msogl.h"
|
||||
#include "mediastreamer2/msvolume.h"
|
||||
|
||||
#include "bctoolbox/charconv.h"
|
||||
|
||||
#include "chat/chat-room/client-group-chat-room-p.h"
|
||||
|
|
@ -69,6 +71,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include "address/address-p.h"
|
||||
#include "c-wrapper/c-wrapper.h"
|
||||
|
||||
|
||||
#ifdef INET6
|
||||
#ifndef _WIN32
|
||||
#include <netdb.h>
|
||||
|
|
@ -1274,6 +1277,34 @@ static void sound_config_read(LinphoneCore *lc)
|
|||
_linphone_core_set_tone(lc,LinphoneReasonBusy,LinphoneToneBusy,NULL);
|
||||
}
|
||||
|
||||
static int _linphone_core_tls_postcheck_callback(void *data, const bctbx_x509_certificate_t *peer_cert){
|
||||
LinphoneCore *lc = (LinphoneCore *) data;
|
||||
const char *tls_certificate_subject_regexp = lp_config_get_string(lc->config,"sip","tls_certificate_subject_regexp", NULL);
|
||||
int ret = 0;
|
||||
if (tls_certificate_subject_regexp){
|
||||
ret = -1;
|
||||
/*the purpose of this handling is to a peer certificate for which there is no single subject matching the regexp given
|
||||
* in the "tls_certificate_subject_regexp" property.
|
||||
*/
|
||||
bctbx_list_t *subjects = bctbx_x509_certificate_get_subjects(peer_cert);
|
||||
bctbx_list_t *elem;
|
||||
for(elem = subjects; elem != NULL; elem = elem->next){
|
||||
const char *subject = (const char *)elem->data;
|
||||
ms_message("_linphone_core_tls_postcheck_callback: subject=%s", subject);
|
||||
if (bctbx_is_matching_regex(subject, tls_certificate_subject_regexp)){
|
||||
ret = 0;
|
||||
ms_message("_linphone_core_tls_postcheck_callback(): successful by matching '%s'", subject);
|
||||
break;
|
||||
}
|
||||
}
|
||||
bctbx_list_free_with_data(subjects, bctbx_free);
|
||||
}
|
||||
if (ret == -1){
|
||||
ms_message("_linphone_core_tls_postcheck_callback(): postcheck failed, nothing matched.");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void certificates_config_read(LinphoneCore *lc) {
|
||||
LinphoneFactory *factory = linphone_factory_get();
|
||||
const char *data_dir = linphone_factory_get_data_resources_dir(factory);
|
||||
|
|
@ -1295,6 +1326,8 @@ static void certificates_config_read(LinphoneCore *lc) {
|
|||
linphone_core_verify_server_certificates(lc, !!lp_config_get_int(lc->config,"sip","verify_server_certs",TRUE));
|
||||
linphone_core_verify_server_cn(lc, !!lp_config_get_int(lc->config,"sip","verify_server_cn",TRUE));
|
||||
bctbx_free(root_ca_path);
|
||||
|
||||
lc->sal->setTlsPostcheckCallback(_linphone_core_tls_postcheck_callback, lc);
|
||||
}
|
||||
|
||||
static void sip_config_read(LinphoneCore *lc) {
|
||||
|
|
|
|||
|
|
@ -1530,6 +1530,9 @@ public:
|
|||
jcontent,
|
||||
jbytes,
|
||||
size);
|
||||
if (jbytes) {
|
||||
env->DeleteLocalRef(jbytes);
|
||||
}
|
||||
if (jcontent) {
|
||||
env->DeleteLocalRef(jcontent);
|
||||
}
|
||||
|
|
@ -6226,6 +6229,9 @@ static jobject create_java_linphone_content(JNIEnv *env, const LinphoneContent *
|
|||
env->DeleteLocalRef(contentClass);
|
||||
env->DeleteLocalRef(jtype);
|
||||
env->DeleteLocalRef(jsubtype);
|
||||
if (jdata) {
|
||||
env->DeleteLocalRef(jdata);
|
||||
}
|
||||
if (jencoding) {
|
||||
env->DeleteLocalRef(jencoding);
|
||||
}
|
||||
|
|
@ -6252,6 +6258,7 @@ static jobject create_java_linphone_buffer(JNIEnv *env, const LinphoneBuffer *bu
|
|||
}
|
||||
|
||||
jobject jobj = env->NewObject(bufferClass, ctor, jdata, jsize);
|
||||
if (jdata) env->DeleteLocalRef(jdata);
|
||||
env->DeleteLocalRef(bufferClass);
|
||||
return jobj;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include "mediastreamer2/mediastream.h"
|
||||
#include "ortp/rtpsession.h"
|
||||
#include "belle-sip/belle-sip.h"
|
||||
#include "bctoolbox/crypto.h"
|
||||
|
||||
#ifndef LINPHONE_PUBLIC
|
||||
#if defined(_MSC_VER)
|
||||
|
|
|
|||
|
|
@ -457,8 +457,8 @@ int SalOp::getAddressFamily() const {
|
|||
|
||||
|
||||
if (mRefresher) {
|
||||
belle_sip_response_t *resp = belle_sip_transaction_get_response(tr);
|
||||
belle_sip_header_via_t *via = resp ?belle_sip_message_get_header_by_type(resp,belle_sip_header_via_t):NULL;
|
||||
belle_sip_message_t *msg = belle_sip_transaction_get_response(tr) ? (belle_sip_message_t*) belle_sip_transaction_get_response(tr) : (belle_sip_message_t*) belle_sip_transaction_get_request(tr);
|
||||
belle_sip_header_via_t *via = msg ? belle_sip_message_get_header_by_type(msg,belle_sip_header_via_t):NULL;
|
||||
const char *host;
|
||||
if (!via){
|
||||
ms_error("Unable to determine IP version from signaling operation, no via header found.");
|
||||
|
|
|
|||
|
|
@ -469,6 +469,7 @@ void Sal::setTlsProperties(){
|
|||
if (!mRootCaData.empty())
|
||||
belle_tls_crypto_config_set_root_ca_data(crypto_config, mRootCaData.c_str());
|
||||
if (mSslConfig != NULL) belle_tls_crypto_config_set_ssl_config(crypto_config, mSslConfig);
|
||||
if (mTlsPostcheckCb) belle_tls_crypto_config_set_postcheck_callback(crypto_config, mTlsPostcheckCb, mTlsPostcheckCbData);
|
||||
belle_sip_tls_listening_point_set_crypto_config(tlp, crypto_config);
|
||||
belle_sip_object_unref(crypto_config);
|
||||
}
|
||||
|
|
@ -707,6 +708,11 @@ void Sal::setSslConfig(void *ssl_config) {
|
|||
setTlsProperties();
|
||||
}
|
||||
|
||||
void Sal::setTlsPostcheckCallback(int (*cb)(void *, const bctbx_x509_certificate_t *), void *data){
|
||||
mTlsPostcheckCb = cb;
|
||||
mTlsPostcheckCbData = data;
|
||||
}
|
||||
|
||||
int Sal::createUuid(char *uuid, size_t len) {
|
||||
if (generateUuid(uuid, len) == 0) {
|
||||
setUuid(uuid);
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ public:
|
|||
|
||||
void verifyServerCertificates (bool value);
|
||||
void verifyServerCn (bool value);
|
||||
|
||||
void setTlsPostcheckCallback(int (*cb)(void *, const bctbx_x509_certificate_t *), void *data);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DNS resolution
|
||||
|
|
@ -317,6 +317,8 @@ private:
|
|||
void *mSslConfig = nullptr;
|
||||
std::vector<std::string> mSupportedContentTypes;
|
||||
std::string mLinphoneSpecs;
|
||||
belle_tls_crypto_config_postcheck_callback_t mTlsPostcheckCb;
|
||||
void *mTlsPostcheckCbData;
|
||||
|
||||
// Cache values
|
||||
mutable std::string mDnsUserHostsFile;
|
||||
|
|
|
|||
|
|
@ -71,6 +71,8 @@ set(CERTIFICATE_CLIENT_FILES
|
|||
certificates/client/key.pem
|
||||
certificates/client/cert2.pem
|
||||
certificates/client/key2.pem
|
||||
certificates/client/cert3.pem
|
||||
certificates/client/key3.pem
|
||||
certificates/client/cert2-signed-by-other-ca.pem
|
||||
)
|
||||
|
||||
|
|
|
|||
78
tester/certificates/client/cert3.pem
Normal file
78
tester/certificates/client/cert3.pem
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 19 (0x13)
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
Issuer: C=FR, ST=Some-State, L=Grenoble, O=Belledonne Communications, OU=LAB, CN=Jehan Monnier/emailAddress=jehan.monnier@belledonne-communications.com
|
||||
Validity
|
||||
Not Before: Apr 3 13:55:30 2018 GMT
|
||||
Not After : Mar 31 13:55:30 2028 GMT
|
||||
Subject: C=FR, ST=Some-State, L=Grenoble, O=Internet Widgits Pty Ltd, CN=sip:gandalf@sip.example.org
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (2048 bit)
|
||||
Modulus:
|
||||
00:c4:b0:07:de:24:1a:46:3c:63:2c:f2:a6:9a:ba:
|
||||
fd:3d:e4:f0:1e:96:6c:74:f7:ad:cf:b3:3c:51:31:
|
||||
45:11:af:0a:ca:ba:f2:cc:ef:82:05:28:c3:5d:6c:
|
||||
bf:65:47:27:ec:37:37:f3:ab:42:52:83:a0:e9:4f:
|
||||
ef:99:cd:07:04:ee:bb:87:6d:40:a6:dd:2f:b5:79:
|
||||
1b:f1:c1:3a:22:75:29:e3:9e:64:67:ae:bf:93:36:
|
||||
a9:3a:0a:00:bc:4e:b6:ab:db:83:01:7f:35:5e:52:
|
||||
2c:bc:21:52:d7:76:0b:48:1d:d7:09:90:be:1b:7c:
|
||||
98:3f:a2:70:78:48:99:95:eb:5d:95:07:ca:6f:f7:
|
||||
43:f0:c5:0d:3d:60:95:ef:6d:53:8f:7f:6c:95:2d:
|
||||
51:b5:3c:84:3a:56:57:b2:2d:d8:6e:36:fe:66:3f:
|
||||
e5:80:89:6d:98:e0:67:f6:ed:65:ab:dc:ad:fb:04:
|
||||
90:2f:30:60:30:8b:9f:a1:a0:9b:b9:54:6b:ae:83:
|
||||
41:38:18:60:a3:fe:32:ef:38:46:bc:44:0b:e1:ca:
|
||||
4c:41:7e:e2:9c:35:36:eb:f7:54:83:67:c8:9f:83:
|
||||
68:00:c3:58:ee:54:be:d6:05:d1:97:c8:e1:47:c0:
|
||||
25:eb:55:a6:a0:1b:c6:86:35:ba:f5:7a:63:75:79:
|
||||
7d:d7
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
Netscape Comment:
|
||||
OpenSSL Generated Certificate
|
||||
X509v3 Subject Key Identifier:
|
||||
47:91:DE:5F:F9:8A:85:B5:0F:68:02:38:5D:67:3F:ED:C6:3C:F2:66
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:06:5F:5D:C7:16:AF:62:F8:2D:6E:71:03:88:A0:D6:1D:2B:04:7F:BA
|
||||
|
||||
X509v3 Subject Alternative Name:
|
||||
URI:sip:gandalf@sip.linphone.org, URI:sip:gandalf@sip.example.org
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
38:eb:34:2f:d3:fe:c6:8f:3f:8b:a3:4c:73:2c:84:75:1e:f0:
|
||||
c0:41:6a:78:63:ad:81:99:ac:cf:9a:27:28:4f:19:3c:81:7f:
|
||||
dd:81:31:f4:f3:6a:ee:6d:13:fd:99:3f:96:75:94:1f:44:e6:
|
||||
9c:1e:d8:9e:34:4d:e6:c9:1c:51:59:e2:6a:96:fc:84:95:d3:
|
||||
a3:0f:d7:38:db:89:2d:df:67:58:32:88:83:e8:76:7f:5c:28:
|
||||
08:3c:75:c0:e1:ae:68:78:0c:4d:9b:71:57:ee:76:99:80:69:
|
||||
73:7f:fd:fe:ec:e5:f1:f8:92:e4:1a:c1:8e:9e:70:3e:e3:de:
|
||||
1c:2f
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID9zCCA2CgAwIBAgIBEzANBgkqhkiG9w0BAQUFADCBuzELMAkGA1UEBhMCRlIx
|
||||
EzARBgNVBAgMClNvbWUtU3RhdGUxETAPBgNVBAcMCEdyZW5vYmxlMSIwIAYDVQQK
|
||||
DBlCZWxsZWRvbm5lIENvbW11bmljYXRpb25zMQwwCgYDVQQLDANMQUIxFjAUBgNV
|
||||
BAMMDUplaGFuIE1vbm5pZXIxOjA4BgkqhkiG9w0BCQEWK2plaGFuLm1vbm5pZXJA
|
||||
YmVsbGVkb25uZS1jb21tdW5pY2F0aW9ucy5jb20wHhcNMTgwNDAzMTM1NTMwWhcN
|
||||
MjgwMzMxMTM1NTMwWjB+MQswCQYDVQQGEwJGUjETMBEGA1UECAwKU29tZS1TdGF0
|
||||
ZTERMA8GA1UEBwwIR3Jlbm9ibGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg
|
||||
UHR5IEx0ZDEkMCIGA1UEAwwbc2lwOmdhbmRhbGZAc2lwLmV4YW1wbGUub3JnMIIB
|
||||
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxLAH3iQaRjxjLPKmmrr9PeTw
|
||||
HpZsdPetz7M8UTFFEa8KyrryzO+CBSjDXWy/ZUcn7Dc386tCUoOg6U/vmc0HBO67
|
||||
h21Apt0vtXkb8cE6InUp455kZ66/kzapOgoAvE62q9uDAX81XlIsvCFS13YLSB3X
|
||||
CZC+G3yYP6JweEiZletdlQfKb/dD8MUNPWCV721Tj39slS1RtTyEOlZXsi3Ybjb+
|
||||
Zj/lgIltmOBn9u1lq9yt+wSQLzBgMIufoaCbuVRrroNBOBhgo/4y7zhGvEQL4cpM
|
||||
QX7inDU26/dUg2fIn4NoAMNY7lS+1gXRl8jhR8Al61WmoBvGhjW69XpjdXl91wID
|
||||
AQABo4HCMIG/MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2Vu
|
||||
ZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBRHkd5f+YqFtQ9oAjhdZz/txjzy
|
||||
ZjAfBgNVHSMEGDAWgBQGX13HFq9i+C1ucQOIoNYdKwR/ujBEBgNVHREEPTA7hhxz
|
||||
aXA6Z2FuZGFsZkBzaXAubGlucGhvbmUub3JnhhtzaXA6Z2FuZGFsZkBzaXAuZXhh
|
||||
bXBsZS5vcmcwDQYJKoZIhvcNAQEFBQADgYEAOOs0L9P+xo8/i6NMcyyEdR7wwEFq
|
||||
eGOtgZmsz5onKE8ZPIF/3YEx9PNq7m0T/Zk/lnWUH0TmnB7YnjRN5skcUVniapb8
|
||||
hJXTow/XONuJLd9nWDKIg+h2f1woCDx1wOGuaHgMTZtxV+52mYBpc3/9/uzl8fiS
|
||||
5BrBjp5wPuPeHC8=
|
||||
-----END CERTIFICATE-----
|
||||
28
tester/certificates/client/key3.pem
Normal file
28
tester/certificates/client/key3.pem
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDEsAfeJBpGPGMs
|
||||
8qaauv095PAelmx0963PszxRMUURrwrKuvLM74IFKMNdbL9lRyfsNzfzq0JSg6Dp
|
||||
T++ZzQcE7ruHbUCm3S+1eRvxwToidSnjnmRnrr+TNqk6CgC8Trar24MBfzVeUiy8
|
||||
IVLXdgtIHdcJkL4bfJg/onB4SJmV612VB8pv90PwxQ09YJXvbVOPf2yVLVG1PIQ6
|
||||
VleyLdhuNv5mP+WAiW2Y4Gf27WWr3K37BJAvMGAwi5+hoJu5VGuug0E4GGCj/jLv
|
||||
OEa8RAvhykxBfuKcNTbr91SDZ8ifg2gAw1juVL7WBdGXyOFHwCXrVaagG8aGNbr1
|
||||
emN1eX3XAgMBAAECggEAMFb/KAaBep+e1E4yyjaIxOx+Y1YfA8RXsINhoKbWTdlS
|
||||
cq7tu5ZlwzTYhx6SD6ckVbbghn+hxjvZkV33CjrMrdaqukcMq7YS4qwIRStzSUK4
|
||||
b9ve22ikZt75Sm1o7t79oFFL6lNEX5Ecs4QRIyk87pV+4zfysTi3BnS65aUaMjD/
|
||||
K+zpK3pBqWcb5dUCjCUMHKw99XC+HjtKFiVWS7CXUQoF1kzTiSNtnz+yX/RBkHYZ
|
||||
eVM/Gex1WtgCqt9P5zPaFstvxQO8osXZsOrSK2oRZjpiaD5FyyVOBLhTVF1cKHTS
|
||||
oI89g5SO0GXq+hbfVgx90clnIyLSB83bB5czUJKYIQKBgQDn7vF5pYtF1uxZsRdk
|
||||
zoDxENfKCghHq7QYULez5mzC8p8COvzXUBq9VKc1zFWhK1R6c+Nl+IXJwf59uO83
|
||||
L97cRvminEA3g/CkJet0CoHCHAZ0Z5XrZwuPAX3kjiz3XrThveAJGqO6R239kib2
|
||||
V7zXG0aQNP/1VeR1XfPm0e2+ZwKBgQDZGNKTOGI3OnbKlDPTIXhfWixSTR3EU+zE
|
||||
9Udoeuut0GGZRe/aveciiBGQIrMUpIVPRYJEyP2o3R43z/ZwRjNwDUDA+s0evQ8n
|
||||
2LFlpNVlN1xdcZrMv7j3hvFGbr362ZPPz7FC6H7FgeHjDq2sHGgjwJMucO0qnNuA
|
||||
iNoOuLe/EQKBgA+C5HVtQ9483Hu3I0hjoy38IWJqv1kDu7ywkUifzYBQN6Avj79a
|
||||
pR8qbTBk5QktW64A2CF2uIPgzINd/emj8vSqboGYj5bm5Q4lVxTgqwLvWuMoFlez
|
||||
AYvj3qaNd6ZnmBNM3pHdTTvlEQ8XWjG5dnCwa0yzrraasvfCe9BhE9RbAoGAdkTu
|
||||
muNxpjLEenIolZG7WP7v/FokqEssRtR09XdZo5RNR0nxdFJWc9p67vHoa4uBUIFG
|
||||
iaCRiAgGKVOzJtEnvpiJuVgonOFUO1nysrQMyRpSyFlWgsrDwp2SHdPAzcLwopq8
|
||||
L/4m6gRrAd6CQKwtE6UayYcdvUQ81JY5bSG2gHECgYEAg2KU1y5lwws4rjo0HD5f
|
||||
xeR5JnAVUZ5g2T99SzA1VnNy2klCVF7DorviPiZOlJBvLp0n25tGArRAHqiFSfbH
|
||||
dUPWhOXiLTozP9Gb0ODAK4k1EtgISNbec1FdhTA+YBOk4CvVCvLFWr2h9yNCzNpd
|
||||
TCuUz4z2lSsg1YRHT4lJJr8=
|
||||
-----END PRIVATE KEY-----
|
||||
360
tester/certificates/client/openssl-altname.cnf
Normal file
360
tester/certificates/client/openssl-altname.cnf
Normal file
|
|
@ -0,0 +1,360 @@
|
|||
#
|
||||
# OpenSSL example configuration file.
|
||||
# This is mostly being used for generation of certificate requests.
|
||||
# This variant is used for generating client certificate with subjectAltName.uri.
|
||||
#
|
||||
|
||||
# This definition stops the following lines choking if HOME isn't
|
||||
# defined.
|
||||
HOME = .
|
||||
RANDFILE = $ENV::HOME/.rnd
|
||||
|
||||
# Extra OBJECT IDENTIFIER info:
|
||||
#oid_file = $ENV::HOME/.oid
|
||||
oid_section = new_oids
|
||||
|
||||
# To use this configuration file with the "-extfile" option of the
|
||||
# "openssl x509" utility, name here the section containing the
|
||||
# X.509v3 extensions to use:
|
||||
# extensions =
|
||||
# (Alternatively, use a configuration file that has only
|
||||
# X.509v3 extensions in its main [= default] section.)
|
||||
|
||||
[ new_oids ]
|
||||
|
||||
# We can add new OIDs in here for use by 'ca', 'req' and 'ts'.
|
||||
# Add a simple OID like this:
|
||||
# testoid1=1.2.3.4
|
||||
# Or use config file substitution like this:
|
||||
# testoid2=${testoid1}.5.6
|
||||
|
||||
# Policies used by the TSA examples.
|
||||
tsa_policy1 = 1.2.3.4.1
|
||||
tsa_policy2 = 1.2.3.4.5.6
|
||||
tsa_policy3 = 1.2.3.4.5.7
|
||||
|
||||
####################################################################
|
||||
[ ca ]
|
||||
default_ca = CA_default # The default ca section
|
||||
|
||||
####################################################################
|
||||
[ CA_default ]
|
||||
|
||||
dir = . # Where everything is kept
|
||||
certs = $dir/certs # Where the issued certs are kept
|
||||
crl_dir = $dir/crl # Where the issued crl are kept
|
||||
database = $dir/index.txt # database index file.
|
||||
#unique_subject = no # Set to 'no' to allow creation of
|
||||
# several ctificates with same subject.
|
||||
new_certs_dir = $dir/newcerts # default place for new certs.
|
||||
|
||||
certificate = $dir/cacert.pem # The CA certificate
|
||||
serial = $dir/serial # The current serial number
|
||||
crlnumber = $dir/crlnumber # the current crl number
|
||||
# must be commented out to leave a V1 CRL
|
||||
crl = $dir/crl.pem # The current CRL
|
||||
private_key = $dir/private/cakey.pem# The private key
|
||||
RANDFILE = $dir/private/.rand # private random number file
|
||||
|
||||
x509_extensions = usr_cert # The extentions to add to the cert
|
||||
|
||||
# Comment out the following two lines for the "traditional"
|
||||
# (and highly broken) format.
|
||||
name_opt = ca_default # Subject Name options
|
||||
cert_opt = ca_default # Certificate field options
|
||||
|
||||
# Extension copying option: use with caution.
|
||||
# copy_extensions = copy
|
||||
|
||||
# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
|
||||
# so this is commented out by default to leave a V1 CRL.
|
||||
# crlnumber must also be commented out to leave a V1 CRL.
|
||||
# crl_extensions = crl_ext
|
||||
|
||||
default_days = 3650 # how long to certify for
|
||||
default_crl_days= 30 # how long before next CRL
|
||||
default_md = sha1 # use public key default MD
|
||||
preserve = no # keep passed DN ordering
|
||||
|
||||
# A few difference way of specifying how similar the request should look
|
||||
# For type CA, the listed attributes must be the same, and the optional
|
||||
# and supplied fields are just that :-)
|
||||
policy = policy_match
|
||||
|
||||
# For the CA policy
|
||||
[ policy_match ]
|
||||
countryName = match
|
||||
stateOrProvinceName = match
|
||||
organizationName = match
|
||||
organizationalUnitName = optional
|
||||
commonName = supplied
|
||||
emailAddress = optional
|
||||
|
||||
# For the 'anything' policy
|
||||
# At this point in time, you must list all acceptable 'object'
|
||||
# types.
|
||||
[ policy_anything ]
|
||||
countryName = optional
|
||||
stateOrProvinceName = optional
|
||||
localityName = optional
|
||||
organizationName = optional
|
||||
organizationalUnitName = optional
|
||||
commonName = supplied
|
||||
emailAddress = optional
|
||||
|
||||
####################################################################
|
||||
[ req ]
|
||||
default_bits = 1024
|
||||
default_keyfile = privkey.pem
|
||||
distinguished_name = req_distinguished_name
|
||||
attributes = req_attributes
|
||||
x509_extensions = v3_ca # The extentions to add to the self signed cert
|
||||
|
||||
# Passwords for private keys if not present they will be prompted for
|
||||
# input_password = secret
|
||||
# output_password = secret
|
||||
|
||||
# This sets a mask for permitted string types. There are several options.
|
||||
# default: PrintableString, T61String, BMPString.
|
||||
# pkix : PrintableString, BMPString (PKIX recommendation before 2004)
|
||||
# utf8only: only UTF8Strings (PKIX recommendation after 2004).
|
||||
# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
|
||||
# MASK:XXXX a literal mask value.
|
||||
# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings.
|
||||
string_mask = utf8only
|
||||
|
||||
# req_extensions = v3_req # The extensions to add to a certificate request
|
||||
[ req_distinguished_name ]
|
||||
countryName = Country Name (2 letter code)
|
||||
countryName_default = FR
|
||||
countryName_min = 2
|
||||
countryName_max = 2
|
||||
|
||||
stateOrProvinceName = State or Province Name (full name)
|
||||
stateOrProvinceName_default = France
|
||||
|
||||
localityName = Locality Name (eg, city)
|
||||
localityName_default = Grenoble
|
||||
|
||||
0.organizationName = Organization Name (eg, company)
|
||||
0.organizationName_default = Belledonne Communications
|
||||
|
||||
# we can do this but it is not needed normally :-)
|
||||
#1.organizationName = Second Organization Name (eg, company)
|
||||
#1.organizationName_default = World Wide Web Pty Ltd
|
||||
|
||||
organizationalUnitName = Organizational Unit Name (eg, section)
|
||||
organizationalUnitName_default = LAB
|
||||
#organizationalUnitName_default =
|
||||
|
||||
0.commonName = Common Name (e.g. server FQDN or YOUR name)
|
||||
0.commonName_max = 64
|
||||
0.commonName_default = sip2.linphone.org
|
||||
|
||||
1.commonName = Common Name (e.g. server FQDN or YOUR name)
|
||||
1.commonName_max = 64
|
||||
1.commonName_default = *.wildcard1.linphone.org
|
||||
|
||||
emailAddress = Email Address
|
||||
emailAddress_max = 64
|
||||
emailAddress_default = jehan.monnier@belledonne-communications.com
|
||||
|
||||
# SET-ex3 = SET extension number 3
|
||||
|
||||
[ req_attributes ]
|
||||
challengePassword = A challenge password
|
||||
challengePassword_min = 4
|
||||
challengePassword_max = 20
|
||||
|
||||
unstructuredName = An optional company name
|
||||
|
||||
[ usr_cert ]
|
||||
|
||||
# These extensions are added when 'ca' signs a request.
|
||||
|
||||
# This goes against PKIX guidelines but some CAs do it and some software
|
||||
# requires this to avoid interpreting an end user certificate as a CA.
|
||||
|
||||
basicConstraints=CA:FALSE
|
||||
|
||||
# Here are some examples of the usage of nsCertType. If it is omitted
|
||||
# the certificate can be used for anything *except* object signing.
|
||||
|
||||
# This is OK for an SSL server.
|
||||
# nsCertType = server
|
||||
|
||||
# For an object signing certificate this would be used.
|
||||
# nsCertType = objsign
|
||||
|
||||
# For normal client use this is typical
|
||||
# nsCertType = client, email
|
||||
|
||||
# and for everything including object signing:
|
||||
# nsCertType = client, email, objsign
|
||||
|
||||
# This is typical in keyUsage for a client certificate.
|
||||
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
|
||||
# This will be displayed in Netscape's comment listbox.
|
||||
nsComment = "OpenSSL Generated Certificate"
|
||||
|
||||
# PKIX recommendations harmless if included in all certificates.
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid,issuer
|
||||
|
||||
# This stuff is for subjectAltName and issuerAltname.
|
||||
# Import the email address.
|
||||
# subjectAltName=email:copy
|
||||
# An alternative to produce certificates that aren't
|
||||
# deprecated according to PKIX.
|
||||
# subjectAltName=email:move
|
||||
|
||||
subjectAltName=URI:sip:gandalf@sip.linphone.org, URI:sip:gandalf@sip.example.org
|
||||
|
||||
# Copy subject details
|
||||
# issuerAltName=issuer:copy
|
||||
|
||||
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
|
||||
#nsBaseUrl
|
||||
#nsRevocationUrl
|
||||
#nsRenewalUrl
|
||||
#nsCaPolicyUrl
|
||||
#nsSslServerName
|
||||
|
||||
# This is required for TSA certificates.
|
||||
# extendedKeyUsage = critical,timeStamping
|
||||
|
||||
[ v3_req ]
|
||||
|
||||
# Extensions to add to a certificate request
|
||||
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
|
||||
[ v3_ca ]
|
||||
|
||||
|
||||
# Extensions for a typical CA
|
||||
|
||||
|
||||
# PKIX recommendation.
|
||||
|
||||
subjectKeyIdentifier=hash
|
||||
|
||||
authorityKeyIdentifier=keyid:always,issuer
|
||||
|
||||
# This is what PKIX recommends but some broken software chokes on critical
|
||||
# extensions.
|
||||
#basicConstraints = critical,CA:true
|
||||
# So we do this instead.
|
||||
basicConstraints = CA:true
|
||||
|
||||
# Key usage: this is typical for a CA certificate. However since it will
|
||||
# prevent it being used as an test self-signed certificate it is best
|
||||
# left out by default.
|
||||
# keyUsage = cRLSign, keyCertSign
|
||||
|
||||
# Some might want this also
|
||||
# nsCertType = sslCA, emailCA
|
||||
|
||||
# Include email address in subject alt name: another PKIX recommendation
|
||||
# subjectAltName=email:copy
|
||||
# Copy issuer details
|
||||
# issuerAltName=issuer:copy
|
||||
|
||||
# DER hex encoding of an extension: beware experts only!
|
||||
# obj=DER:02:03
|
||||
# Where 'obj' is a standard or added object
|
||||
# You can even override a supported extension:
|
||||
# basicConstraints= critical, DER:30:03:01:01:FF
|
||||
|
||||
[ crl_ext ]
|
||||
|
||||
# CRL extensions.
|
||||
# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
|
||||
|
||||
# issuerAltName=issuer:copy
|
||||
authorityKeyIdentifier=keyid:always
|
||||
|
||||
[ proxy_cert_ext ]
|
||||
# These extensions should be added when creating a proxy certificate
|
||||
|
||||
# This goes against PKIX guidelines but some CAs do it and some software
|
||||
# requires this to avoid interpreting an end user certificate as a CA.
|
||||
|
||||
basicConstraints=CA:FALSE
|
||||
|
||||
# Here are some examples of the usage of nsCertType. If it is omitted
|
||||
# the certificate can be used for anything *except* object signing.
|
||||
|
||||
# This is OK for an SSL server.
|
||||
# nsCertType = server
|
||||
|
||||
# For an object signing certificate this would be used.
|
||||
# nsCertType = objsign
|
||||
|
||||
# For normal client use this is typical
|
||||
# nsCertType = client, email
|
||||
|
||||
# and for everything including object signing:
|
||||
# nsCertType = client, email, objsign
|
||||
|
||||
# This is typical in keyUsage for a client certificate.
|
||||
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
|
||||
# This will be displayed in Netscape's comment listbox.
|
||||
nsComment = "OpenSSL Generated Certificate"
|
||||
|
||||
# PKIX recommendations harmless if included in all certificates.
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid,issuer
|
||||
|
||||
# This stuff is for subjectAltName and issuerAltname.
|
||||
# Import the email address.
|
||||
# subjectAltName=email:copy
|
||||
# An alternative to produce certificates that aren't
|
||||
# deprecated according to PKIX.
|
||||
# subjectAltName=email:move
|
||||
|
||||
# Copy subject details
|
||||
# issuerAltName=issuer:copy
|
||||
|
||||
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
|
||||
#nsBaseUrl
|
||||
#nsRevocationUrl
|
||||
#nsRenewalUrl
|
||||
#nsCaPolicyUrl
|
||||
#nsSslServerName
|
||||
|
||||
# This really needs to be in place for it to be a proxy certificate.
|
||||
proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
|
||||
|
||||
####################################################################
|
||||
[ tsa ]
|
||||
|
||||
default_tsa = tsa_config1 # the default TSA section
|
||||
|
||||
[ tsa_config1 ]
|
||||
|
||||
# These are used by the TSA reply generation only.
|
||||
dir = . # TSA root directory
|
||||
serial = $dir/tsaserial # The current serial number (mandatory)
|
||||
crypto_device = builtin # OpenSSL engine to use for signing
|
||||
signer_cert = $dir/tsacert.pem # The TSA signing certificate
|
||||
# (optional)
|
||||
certs = $dir/cacert.pem # Certificate chain to include in reply
|
||||
# (optional)
|
||||
signer_key = $dir/private/tsakey.pem # The TSA private key (optional)
|
||||
|
||||
default_policy = tsa_policy1 # Policy if request did not specify it
|
||||
# (optional)
|
||||
other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional)
|
||||
digests = md5, sha1 # Acceptable message digests (mandatory)
|
||||
accuracy = secs:1, millisecs:500, microsecs:100 # (optional)
|
||||
clock_precision_digits = 0 # number of digits after dot. (optional)
|
||||
ordering = yes # Is ordering defined for timestamps?
|
||||
# (optional, default: no)
|
||||
tsa_name = yes # Must the TSA name be included in the reply?
|
||||
# (optional, default: no)
|
||||
ess_cert_id_chain = no # Must the ESS cert id chain be included?
|
||||
# (optional, default: no)
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
flexisip.conf : is the configuration of the flexisip running on sip2.linphone.org. It has lots of IP addresses hardcoded because this machine is running multiple instances on different IP addresses.
|
||||
flexisip-generic.conf : is the same configuration without any IP address hardcoded and relative paths. It can be run on any machine from the "tester" directory of linphone.
|
||||
|
||||
Make sure that any changes performed on one of these two files is also made on the other.
|
||||
|
||||
|
|
|
|||
|
|
@ -115,6 +115,9 @@ filter= from.uri.domain contains 'sip.example.org' || from.uri.domain contains '
|
|||
# Default value:
|
||||
auth-domains= sip.example.org auth.example.org auth1.example.org auth2.example.org
|
||||
|
||||
#regexp for test "TLS authentication - client rejected due to unmatched certificate subject" of Flexisip test suite.
|
||||
tls-client-certificate-required-subject=galadrielle|sip:sip.example.org
|
||||
|
||||
|
||||
|
||||
# List of whitespace separated IP which will not be challenged.
|
||||
|
|
|
|||
|
|
@ -115,6 +115,8 @@ filter= from.uri.domain contains 'sip.example.org' || from.uri.domain contains '
|
|||
# Default value:
|
||||
auth-domains= sip.example.org auth.example.org auth1.example.org auth2.example.org
|
||||
|
||||
#regexp for test "TLS authentication - client rejected due to unmatched certificate subject" of Flexisip test suite.
|
||||
tls-client-certificate-required-subject=galadrielle|sip:sip.example.org
|
||||
|
||||
|
||||
# List of whitespace separated IP which will not be challenged.
|
||||
|
|
|
|||
|
|
@ -1342,14 +1342,28 @@ static void tls_authentication_requested_bad(LinphoneCore *lc, LinphoneAuthInfo
|
|||
}
|
||||
}
|
||||
|
||||
static void tls_client_auth_try_register(const char *identity, bool_t with_good_cert, bool_t must_work){
|
||||
static void tls_authentication_provide_altname_cert(LinphoneCore *lc, LinphoneAuthInfo *auth_info, LinphoneAuthMethod method){
|
||||
if (method == LinphoneAuthTls){
|
||||
|
||||
char *cert = bc_tester_res("certificates/client/cert3.pem");
|
||||
char *key = bc_tester_res("certificates/client/key3.pem");
|
||||
|
||||
linphone_auth_info_set_tls_cert_path(auth_info, cert);
|
||||
linphone_auth_info_set_tls_key_path(auth_info, key);
|
||||
linphone_core_add_auth_info(lc, auth_info);
|
||||
bc_free(cert);
|
||||
bc_free(key);
|
||||
}
|
||||
}
|
||||
|
||||
static void tls_client_auth_try_register(const char *identity, LinphoneCoreAuthenticationRequestedCb cb, bool_t good_cert, bool_t must_work){
|
||||
LinphoneCoreManager *lcm;
|
||||
LinphoneCoreCbs *cbs = linphone_factory_create_core_cbs(linphone_factory_get());
|
||||
LinphoneProxyConfig *cfg;
|
||||
|
||||
lcm = linphone_core_manager_new(NULL);
|
||||
|
||||
linphone_core_cbs_set_authentication_requested(cbs, with_good_cert ? tls_authentication_requested_good : tls_authentication_requested_bad);
|
||||
linphone_core_cbs_set_authentication_requested(cbs, cb);
|
||||
linphone_core_add_callbacks(lcm->lc, cbs);
|
||||
linphone_core_cbs_unref(cbs);
|
||||
cfg = linphone_core_create_proxy_config(lcm->lc);
|
||||
|
|
@ -1368,7 +1382,8 @@ static void tls_client_auth_try_register(const char *identity, bool_t with_good_
|
|||
/*we should expect at least 2 "auth_requested": one for the TLS certificate, another one because the server rejects the REGISTER with 401,
|
||||
with eventually MD5 + SHA256 challenge*/
|
||||
/*If the certificate isn't recognized at all, the connection will not happen and no SIP response will be received from server.*/
|
||||
if (with_good_cert) BC_ASSERT_GREATER(lcm->stat.number_of_auth_info_requested,2, int, "%d");
|
||||
if (good_cert) BC_ASSERT_GREATER(lcm->stat.number_of_auth_info_requested,2, int, "%d");
|
||||
|
||||
else BC_ASSERT_EQUAL(lcm->stat.number_of_auth_info_requested,1, int, "%d");
|
||||
}
|
||||
|
||||
|
|
@ -1380,9 +1395,9 @@ void tls_client_auth_bad_certificate_cn(void) {
|
|||
if (transport_supported(LinphoneTransportTls)) {
|
||||
/*first register to the proxy with galadrielle's identity, and authenticate by supplying galadrielle's certificate.
|
||||
* It must work.*/
|
||||
tls_client_auth_try_register("sip:galadrielle@sip.example.org", TRUE, TRUE);
|
||||
tls_client_auth_try_register("sip:galadrielle@sip.example.org", tls_authentication_requested_good, TRUE, TRUE);
|
||||
/*now do the same thing, but trying to register as "Arwen". It must fail.*/
|
||||
tls_client_auth_try_register("sip:arwen@sip.example.org", TRUE, FALSE);
|
||||
tls_client_auth_try_register("sip:arwen@sip.example.org", tls_authentication_requested_good, TRUE, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1390,7 +1405,18 @@ void tls_client_auth_bad_certificate(void) {
|
|||
if (transport_supported(LinphoneTransportTls)) {
|
||||
/*first register to the proxy with galadrielle's identity, and authenticate by supplying galadrielle's certificate.
|
||||
* It must work.*/
|
||||
tls_client_auth_try_register("sip:galadrielle@sip.example.org", FALSE, FALSE);
|
||||
tls_client_auth_try_register("sip:galadrielle@sip.example.org", tls_authentication_requested_bad, FALSE, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This test verifies that the flexisip certificate postcheck works.
|
||||
* Here, the certificate presented for gandalf is valid and matches the SIP from. However we've set the regexp in flexisip.conf to only accept
|
||||
* certificates with subjects containing either galadrielle or sip:sip.example.org.
|
||||
*/
|
||||
static void tls_client_rejected_due_to_unmatched_subject(void){
|
||||
if (transport_supported(LinphoneTransportTls)) {
|
||||
tls_client_auth_try_register("sip:gandalf@sip.example.org", tls_authentication_provide_altname_cert, TRUE, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1659,13 +1685,14 @@ void sequential_forking_with_timeout_for_highest_priority(void) {
|
|||
|
||||
linphone_core_invite_address(pauline->lc,marie->identity);
|
||||
|
||||
/*second and third devices should have received the call*/
|
||||
BC_ASSERT_TRUE(wait_for_list(lcs,&marie2->stat.number_of_LinphoneCallIncomingReceived,1,13000));
|
||||
BC_ASSERT_TRUE(wait_for_list(lcs,&marie3->stat.number_of_LinphoneCallIncomingReceived,1,3000));
|
||||
/*pauline should hear ringback*/
|
||||
BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallOutgoingRinging,1,3000));
|
||||
/*first device should receive nothing since it is disconnected*/
|
||||
BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallIncomingReceived, 0, int, "%d");
|
||||
/*second and third devices should have received the call*/
|
||||
BC_ASSERT_TRUE(wait_for_list(lcs,&marie2->stat.number_of_LinphoneCallIncomingReceived,1,3000));
|
||||
BC_ASSERT_TRUE(wait_for_list(lcs,&marie3->stat.number_of_LinphoneCallIncomingReceived,1,3000));
|
||||
|
||||
|
||||
LinphoneCall *call = linphone_core_get_current_call(marie3->lc);
|
||||
if (!BC_ASSERT_PTR_NOT_NULL(call)) goto end;
|
||||
|
|
@ -1718,10 +1745,10 @@ void sequential_forking_with_no_response_for_highest_priority(void) {
|
|||
|
||||
linphone_core_invite_address(pauline->lc,marie->identity);
|
||||
|
||||
/*pauline should hear ringback*/
|
||||
BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallOutgoingRinging,1,3000));
|
||||
/*first device should receive the call*/
|
||||
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallIncomingReceived,1,3000));
|
||||
/*pauline should hear ringback*/
|
||||
BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallOutgoingRinging,1,3000));
|
||||
/*second device should have not received the call yet*/
|
||||
BC_ASSERT_EQUAL(marie2->stat.number_of_LinphoneCallIncomingReceived, 0, int, "%d");
|
||||
|
||||
|
|
@ -1781,12 +1808,12 @@ void sequential_forking_with_insertion_of_higher_priority(void) {
|
|||
|
||||
linphone_core_invite_address(pauline->lc,marie->identity);
|
||||
|
||||
/*second device should have received the call*/
|
||||
BC_ASSERT_TRUE(wait_for_list(lcs,&marie2->stat.number_of_LinphoneCallIncomingReceived,1,13000));
|
||||
/*pauline should hear ringback*/
|
||||
BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallOutgoingRinging,1,3000));
|
||||
/*first device should receive nothing since it is disconnected*/
|
||||
BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallIncomingReceived, 0, int, "%d");
|
||||
/*second device should have received the call*/
|
||||
BC_ASSERT_TRUE(wait_for_list(lcs,&marie2->stat.number_of_LinphoneCallIncomingReceived,1,3000));
|
||||
|
||||
/*we create a new device*/
|
||||
LinphoneCoreManager* marie3 = linphone_core_manager_new("marie_rc");
|
||||
|
|
@ -1864,14 +1891,14 @@ void sequential_forking_with_fallback_route(void) {
|
|||
/*marie invites pauline2 on the other server*/
|
||||
linphone_core_invite_address(marie->lc,pauline2->identity);
|
||||
|
||||
/*the call should be routed to the first server with pauline account*/
|
||||
BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallIncomingReceived,1,13000));
|
||||
|
||||
/*marie should hear ringback*/
|
||||
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallOutgoingRinging,1,3000));
|
||||
/*pauline2 should receive nothing since it is disconnected*/
|
||||
BC_ASSERT_EQUAL(pauline2->stat.number_of_LinphoneCallIncomingReceived, 0, int, "%d");
|
||||
|
||||
/*the call should be routed to the first server with pauline account*/
|
||||
BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallIncomingReceived,1,3000));
|
||||
|
||||
LinphoneCall *call = linphone_core_get_current_call(pauline->lc);
|
||||
if (!BC_ASSERT_PTR_NOT_NULL(call)) goto end;
|
||||
|
||||
|
|
@ -1934,6 +1961,7 @@ test_t flexisip_tests[] = {
|
|||
TEST_ONE_TAG("Redis Publish/subscribe", redis_publish_subscribe, "Skip"),
|
||||
TEST_NO_TAG("TLS authentication - client rejected due to CN mismatch", tls_client_auth_bad_certificate_cn),
|
||||
TEST_NO_TAG("TLS authentication - client rejected due to unrecognized certificate chain", tls_client_auth_bad_certificate),
|
||||
TEST_NO_TAG("TLS authentication - client rejected due to unmatched certificate subject", tls_client_rejected_due_to_unmatched_subject),
|
||||
TEST_NO_TAG("Transcoder", transcoder_tester),
|
||||
TEST_NO_TAG("Removing old tport on flexisip for the same client", test_removing_old_tport),
|
||||
/*TEST_NO_TAG("Resend of REFER with other devices", resend_refer_other_devices),*/
|
||||
|
|
|
|||
|
|
@ -999,6 +999,36 @@ static void tls_certificate_failure(void){
|
|||
}
|
||||
}
|
||||
|
||||
static void tls_certificate_subject_check(void){
|
||||
if (transport_supported(LinphoneTransportTls)) {
|
||||
LinphoneCoreManager* lcm;
|
||||
LinphoneCore *lc;
|
||||
char *rootcapath = bc_tester_res("certificates/cn/cafile.pem");
|
||||
lcm=linphone_core_manager_new2("pauline_alt_rc",FALSE);
|
||||
lc=lcm->lc;
|
||||
linphone_core_set_root_ca(lc, rootcapath);
|
||||
/*let's search for a subject that is not in the certificate, it should fail*/
|
||||
lp_config_set_string(linphone_core_get_config(lc), "sip", "tls_certificate_subject_regexp", "cotcotcot.org");
|
||||
linphone_core_set_network_reachable(lc,TRUE);
|
||||
BC_ASSERT_TRUE(wait_for(lcm->lc,lcm->lc,&lcm->stat.number_of_LinphoneRegistrationFailed,1));
|
||||
|
||||
/*let's search for a subject (in subjectAltNames and CN) that exist in the certificate, it should pass*/
|
||||
lp_config_set_string(linphone_core_get_config(lc), "sip", "tls_certificate_subject_regexp", "altname.linphone.org");
|
||||
linphone_core_refresh_registers(lcm->lc);
|
||||
BC_ASSERT_TRUE(wait_for(lc,lc,&lcm->stat.number_of_LinphoneRegistrationOk,1));
|
||||
linphone_core_set_network_reachable(lc,FALSE);
|
||||
|
||||
/*let's search for a subject (in subjectAltNames and CN) that exist in the certificate, it should pass*/
|
||||
lp_config_set_string(linphone_core_get_config(lc), "sip", "tls_certificate_subject_regexp", "Jehan Monnier");
|
||||
linphone_core_set_network_reachable(lc,TRUE);
|
||||
BC_ASSERT_TRUE(wait_for(lc,lc,&lcm->stat.number_of_LinphoneRegistrationOk,2));
|
||||
|
||||
BC_ASSERT_EQUAL(lcm->stat.number_of_LinphoneRegistrationFailed,1, int, "%d");
|
||||
linphone_core_manager_destroy(lcm);
|
||||
bc_free(rootcapath);
|
||||
}
|
||||
}
|
||||
|
||||
char *read_file(const char *path) {
|
||||
long numbytes = 0;
|
||||
size_t readbytes;
|
||||
|
|
@ -1331,6 +1361,7 @@ test_t register_tests[] = {
|
|||
TEST_NO_TAG("TLS register with alt. name certificate", tls_alt_name_register),
|
||||
TEST_NO_TAG("TLS register with wildcard certificate", tls_wildcard_register),
|
||||
TEST_NO_TAG("TLS certificate not verified",tls_certificate_failure),
|
||||
TEST_NO_TAG("TLS certificate subjects check",tls_certificate_subject_check),
|
||||
TEST_NO_TAG("TLS certificate given by string instead of file",tls_certificate_data),
|
||||
TEST_NO_TAG("TLS with non tls server",tls_with_non_tls_server),
|
||||
TEST_NO_TAG("Simple authenticated register", simple_authenticated_register),
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ static void linphone_version_test(void){
|
|||
static void core_init_test(void) {
|
||||
LinphoneCore* lc;
|
||||
lc = linphone_factory_create_core_2(linphone_factory_get(),NULL,NULL,NULL, NULL, system_context);
|
||||
|
||||
|
||||
/* until we have good certificates on our test server... */
|
||||
linphone_core_verify_server_certificates(lc,FALSE);
|
||||
if (BC_ASSERT_PTR_NOT_NULL(lc)) {
|
||||
|
|
@ -131,8 +131,18 @@ static void core_init_test(void) {
|
|||
}
|
||||
|
||||
static void linphone_address_test(void) {
|
||||
LinphoneAddress *address;
|
||||
|
||||
linphone_address_unref(create_linphone_address(NULL));
|
||||
BC_ASSERT_PTR_NULL(linphone_address_new("sip:@sip.linphone.org"));
|
||||
|
||||
address = linphone_address_new("sip:90.110.127.31");
|
||||
if (!BC_ASSERT_PTR_NOT_NULL(address)) return;
|
||||
linphone_address_unref(address);
|
||||
|
||||
address = linphone_address_new("sip:[::ffff:90.110.127.31]");
|
||||
if (!BC_ASSERT_PTR_NOT_NULL(address)) return;
|
||||
linphone_address_unref(address);
|
||||
}
|
||||
|
||||
static void core_sip_transport_test(void) {
|
||||
|
|
|
|||
|
|
@ -1,454 +0,0 @@
|
|||
/*
|
||||
linphone
|
||||
Copyright (C) 2013 Belledonne Communications SARL
|
||||
Simon Morlat (simon.morlat@linphone.org)
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "generator.hh"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
|
||||
#define strncasecmp _strnicmp
|
||||
#endif
|
||||
|
||||
|
||||
string to_lower(const string &str){
|
||||
string res=str;
|
||||
for(string::iterator it=res.begin();it!=res.end();++it){
|
||||
*it=tolower(*it);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
CplusplusGenerator::CplusplusGenerator(){
|
||||
}
|
||||
|
||||
void CplusplusGenerator::generate(Project *proj){
|
||||
list<Class*> classes=proj->getClasses();
|
||||
mCurProj=proj;
|
||||
#ifndef _WIN32
|
||||
mkdir(proj->getName().c_str(),S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IROTH);
|
||||
#else
|
||||
_mkdir(proj->getName().c_str());
|
||||
#endif
|
||||
for_each(classes.begin(),classes.end(),bind1st(mem_fun(&CplusplusGenerator::writeClass),this));
|
||||
}
|
||||
|
||||
void CplusplusGenerator::writeEnumMember(ConstField *cf, bool isLast){
|
||||
writeTabs(1);
|
||||
mOutfile<<cf->getName()<<"="<<cf->getValue();
|
||||
if (!isLast) mOutfile<<",";
|
||||
if (!cf->getHelp().empty()) mOutfile<<"\t/**< "<<cf->getHelp()<<" */";
|
||||
mOutfile<<endl;
|
||||
}
|
||||
|
||||
void CplusplusGenerator::writeClass(Class *klass){
|
||||
ostringstream filename;
|
||||
|
||||
filename<<mCurProj->getName()<<"/"<<klass->getName()<<".hh";
|
||||
mOutfile.open(filename.str().c_str());
|
||||
if (!mOutfile.is_open()){
|
||||
cerr<<"Could not write into "<<filename.str()<<endl;
|
||||
return;
|
||||
}
|
||||
list<Method*> methods=klass->getMethods();
|
||||
list<ConstField*> constFields=klass->getConstFields();
|
||||
mCurClass=klass;
|
||||
mOutfile<<"/* Wrapper generated by lp-gen-wrappers, do not edit*/"<<endl;
|
||||
mOutfile<<endl;
|
||||
mOutfile<<"#include <string>"<<endl;
|
||||
mOutfile<<endl;
|
||||
if (!mCurProj->getName().empty())
|
||||
mOutfile<<"namespace "<<mCurProj->getName()<<"{"<<endl<<endl;
|
||||
if (klass->getType()==Type::Enum){
|
||||
mOutfile<<"enum "<<klass->getName()<<"{"<<endl;
|
||||
list<ConstField*>::iterator cfit,next;
|
||||
for (cfit=constFields.begin();cfit!=constFields.end();){
|
||||
ConstField *cf=*cfit;
|
||||
writeEnumMember(cf,++cfit==constFields.end());
|
||||
}
|
||||
}else{
|
||||
mOutfile<<"class "<<klass->getName()<<"{"<<endl;
|
||||
mOutfile<<"public:"<<endl;
|
||||
for_each(methods.begin(),methods.end(),bind1st(mem_fun(&CplusplusGenerator::writeMethod),this));
|
||||
}
|
||||
|
||||
mOutfile<<"};"<<endl<<endl;
|
||||
if (!mCurProj->getName().empty())
|
||||
mOutfile<<"} //end of namespace "<<mCurProj->getName()<<endl;
|
||||
mOutfile<<endl;
|
||||
mOutfile.close();
|
||||
}
|
||||
|
||||
void CplusplusGenerator::writeArgument(Argument *arg, bool isReturn){
|
||||
Type *type=arg->getType();
|
||||
|
||||
if (type->getBasicType()==Type::Class){
|
||||
if (arg->isConst()){
|
||||
mOutfile<<"const ";
|
||||
}
|
||||
mOutfile<<type->getName();
|
||||
if (arg->isPointer())
|
||||
mOutfile<<"*";
|
||||
}else if (type->getBasicType()==Type::Integer){
|
||||
mOutfile<<"int";
|
||||
}else if (type->getBasicType()==Type::Enum){
|
||||
mOutfile<<type->getName();
|
||||
}else if (type->getBasicType()==Type::String){
|
||||
if (!isReturn)
|
||||
mOutfile<<"const std::string &";
|
||||
else
|
||||
mOutfile<<"std::string";
|
||||
}else if (type->getBasicType()==Type::Void){
|
||||
mOutfile<<"void";
|
||||
}else if (type->getBasicType()==Type::Boolean){
|
||||
mOutfile<<"bool";
|
||||
}
|
||||
if (!isReturn && !arg->getName().empty())
|
||||
mOutfile<<" "<<arg->getName();
|
||||
}
|
||||
|
||||
void CplusplusGenerator::writeTabs(int ntabs){
|
||||
int i;
|
||||
for(i=0;i<ntabs;++i)
|
||||
mOutfile<<"\t";
|
||||
}
|
||||
|
||||
void CplusplusGenerator::writeHelpComment(const std::string &comment, int ntabs){
|
||||
size_t i;
|
||||
int curindex=0;
|
||||
writeTabs(ntabs);
|
||||
mOutfile<<" * ";
|
||||
for(i=0;i<comment.size();i++,curindex++){
|
||||
|
||||
if (comment[i]=='\n' || (curindex>100 && comment[i]==' ')){
|
||||
mOutfile<<endl;
|
||||
writeTabs(ntabs);
|
||||
mOutfile<<" * ";
|
||||
curindex=0;
|
||||
}else mOutfile<<comment[i];
|
||||
}
|
||||
}
|
||||
|
||||
void CplusplusGenerator::writeMethod(Method *method){
|
||||
if (method->isCallback()) return;
|
||||
|
||||
Argument *retarg=method->getReturnArg();
|
||||
const list<Argument*> &args=method->getArgs();
|
||||
list<Argument*>::const_iterator it;
|
||||
|
||||
writeTabs(1);
|
||||
mOutfile<<"/**"<<endl;
|
||||
writeHelpComment(method->getHelp(),1);
|
||||
mOutfile<<endl;
|
||||
writeTabs(1);
|
||||
mOutfile<<"**/"<<endl;
|
||||
|
||||
writeTabs(1);
|
||||
writeArgument(retarg,true);
|
||||
mOutfile<<" "<<method->getName()<<"(";
|
||||
|
||||
for(it=args.begin();it!=args.end();++it){
|
||||
if (it!=args.begin()) mOutfile<<", ";
|
||||
writeArgument(*it);
|
||||
}
|
||||
mOutfile<<")";
|
||||
if (method->isConst()) mOutfile<<"const";
|
||||
mOutfile<<";"<<endl;
|
||||
mOutfile<<endl;
|
||||
}
|
||||
|
||||
|
||||
JavascriptGenerator::JavascriptGenerator(){
|
||||
}
|
||||
|
||||
void JavascriptGenerator::generate(Project *proj){
|
||||
list<Class*> classes=proj->getClasses();
|
||||
mCurProj=proj;
|
||||
#ifndef _WIN32
|
||||
remove(to_lower(proj->getName()).c_str());
|
||||
mkdir(to_lower(proj->getName()).c_str(),S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IROTH);
|
||||
#else
|
||||
_mkdir(to_lower(proj->getName()).c_str());
|
||||
#endif
|
||||
ostringstream filename;
|
||||
|
||||
/*write a file for the namespace*/
|
||||
filename<<to_lower(mCurProj->getName())<<"/"<<to_lower(mCurProj->getName())<<".js";
|
||||
mOutfile.open(filename.str().c_str());
|
||||
if (!mOutfile.is_open()){
|
||||
cerr<<"Could not write into "<<filename.str()<<endl;
|
||||
return;
|
||||
}
|
||||
mOutfile<<"/**"<<endl;
|
||||
mOutfile<<" * Namespace for non-external variables and objects."<<endl;
|
||||
mOutfile<<" * @namespace "<<mCurProj->getName()<<endl;
|
||||
mOutfile<<"**/"<<endl;
|
||||
mOutfile<<"var "<<proj->getName()<<" = {};"<<endl;
|
||||
mOutfile.close();
|
||||
for_each(classes.begin(),classes.end(),bind1st(mem_fun(&JavascriptGenerator::writeEnum),this));
|
||||
for_each(classes.begin(),classes.end(),bind1st(mem_fun(&JavascriptGenerator::writeClass),this));
|
||||
}
|
||||
|
||||
string JavascriptGenerator::getEnumName(Class *klass){
|
||||
string enum_name=klass->getName();
|
||||
if (strncasecmp(enum_name.c_str(),mCurProj->getName().c_str(),mCurProj->getName().size())==0){
|
||||
//since enum is part of the namespace, drop the namespace part of the enum if any.
|
||||
enum_name.erase(0,mCurProj->getName().size());
|
||||
}
|
||||
return enum_name;
|
||||
}
|
||||
|
||||
void JavascriptGenerator::writeEnum(Class *klass){
|
||||
if (klass->getType()!=Type::Enum) return;
|
||||
|
||||
ostringstream filename;
|
||||
list<ConstField*> members=klass->getConstFields();
|
||||
list<ConstField*>::iterator it;
|
||||
string enum_name=getEnumName(klass);
|
||||
|
||||
filename<<to_lower(mCurProj->getName())<<"/"<<to_lower(enum_name)<<".js";
|
||||
mOutfile.open(filename.str().c_str());
|
||||
if (!mOutfile.is_open()){
|
||||
cerr<<"Could not write into "<<filename.str()<<endl;
|
||||
return;
|
||||
}
|
||||
mOutfile<<"/* Wrapper generated by lp-gen-wrappers, do not edit*/"<<endl<<endl;
|
||||
|
||||
mOutfile<<"var "<<mCurProj->getName()<<" = "<<mCurProj->getName()<<" || {};"<<endl;
|
||||
mOutfile<<"/**"<<endl;
|
||||
writeHelpComment(klass->getHelp(),0);
|
||||
mOutfile<<endl;
|
||||
mOutfile<<" * "<<"@readonly"<<endl;
|
||||
mOutfile<<" * "<<"@enum {number}"<<endl;
|
||||
mOutfile<<"**/"<<endl;
|
||||
mOutfile<<mCurProj->getName()<<"."<<enum_name<<" = {"<<endl;
|
||||
string prefix=ConstField::getCommonPrefix(members);
|
||||
size_t prefix_size=prefix.size();
|
||||
|
||||
for(it=members.begin();it!=members.end();){
|
||||
ConstField *cf=(*it);
|
||||
if (!cf->getHelp().empty()){
|
||||
writeTabs(1);
|
||||
mOutfile<<"/**"<<endl;
|
||||
writeHelpComment(cf->getHelp(),1);
|
||||
mOutfile<<endl;
|
||||
writeTabs(1);
|
||||
mOutfile<<"*/"<<endl;
|
||||
}
|
||||
writeTabs(1);
|
||||
mOutfile<<cf->getName().substr(prefix_size,string::npos)<<" : "<<cf->getValue();
|
||||
if (++it!=members.end()) mOutfile<<",";
|
||||
mOutfile<<endl;
|
||||
}
|
||||
mOutfile<<"};"<<endl;
|
||||
|
||||
mOutfile << "/**" << endl;
|
||||
mOutfile << " * Get the name of a value of the " << enum_name << " enum as a string." << endl;
|
||||
mOutfile << " * @function linphone#get" << enum_name << "Text" << endl;
|
||||
mOutfile << " * @param { number } value - One of the values of the " << enum_name << " enum." << endl;
|
||||
mOutfile << "**/" << endl;
|
||||
mOutfile << mCurProj->getName() << ".get" << enum_name << "Text = function(value) {" << endl;
|
||||
mOutfile << "\tswitch (value) {" << endl;
|
||||
for (it = members.begin(); it != members.end(); it++) {
|
||||
ConstField *cf = *it;
|
||||
mOutfile << "\tcase " << mCurProj->getName() << "." << enum_name << "." << cf->getName().substr(prefix_size, string::npos) << ":" << endl;
|
||||
mOutfile << "\t\treturn \"" << cf->getName().substr(prefix_size, string::npos) << "\";" << endl;
|
||||
}
|
||||
mOutfile << "\tdefault:" << endl;
|
||||
mOutfile << "\t\treturn \"?\";" << endl;
|
||||
mOutfile << "\t}" << endl;
|
||||
mOutfile << "};" << endl;
|
||||
|
||||
mOutfile.close();
|
||||
}
|
||||
|
||||
void JavascriptGenerator::writeClass(Class *klass){
|
||||
ostringstream filename;
|
||||
|
||||
if (klass->getType()==Type::Enum) {
|
||||
return;
|
||||
}
|
||||
const list<Method*> &methods=klass->getMethods();
|
||||
if (methods.empty()) return;//skip empty classes
|
||||
|
||||
filename<<to_lower(mCurProj->getName())<<"/"<<to_lower(klass->getName())<<".js";
|
||||
mOutfile.open(filename.str().c_str());
|
||||
if (!mOutfile.is_open()){
|
||||
cerr<<"Could not write into "<<filename.str()<<endl;
|
||||
return;
|
||||
}
|
||||
mCurClass=klass;
|
||||
mOutfile<<"/* Wrapper generated by lp-gen-wrappers, do not edit*/"<<endl;
|
||||
mOutfile<<endl;
|
||||
|
||||
//if (!mCurProj->getName().empty())
|
||||
// mOutfile<<"namespace "<<mCurProj->getName()<<"{"<<endl<<endl;
|
||||
mOutfile<<"/**"<<endl;
|
||||
mOutfile<<" * "<<klass->getHelp()<<endl;
|
||||
mOutfile<<" * @external "<<klass->getName()<<endl;
|
||||
mOutfile<<"**/"<<endl;
|
||||
|
||||
list<Property*> properties=klass->getProperties();
|
||||
for_each(properties.begin(),properties.end(),bind1st(mem_fun(&JavascriptGenerator::writeProperty),this));
|
||||
mOutfile<<endl;
|
||||
for_each(methods.begin(),methods.end(),bind1st(mem_fun(&JavascriptGenerator::writeMethod),this));
|
||||
for_each(methods.begin(),methods.end(),bind1st(mem_fun(&JavascriptGenerator::writeEvent),this));
|
||||
//if (!mCurProj->getName().empty())
|
||||
// mOutfile<<"} //end of namespace "<<mCurProj->getName()<<endl;
|
||||
mOutfile<<endl;
|
||||
mOutfile.close();
|
||||
}
|
||||
|
||||
void JavascriptGenerator::writeType(Type *type){
|
||||
switch(type->getBasicType()){
|
||||
case Type::Float:
|
||||
case Type::Integer:
|
||||
mOutfile<<"number";
|
||||
break;
|
||||
case Type::String:
|
||||
mOutfile<<"string";
|
||||
break;
|
||||
case Type::Boolean:
|
||||
mOutfile<<"boolean";
|
||||
break;
|
||||
case Type::Class:
|
||||
mOutfile<<"external:"<<type->getName();
|
||||
break;
|
||||
case Type::Enum:
|
||||
mOutfile<<mCurProj->getName()<<"."<<getEnumName(mCurProj->getClass(type->getName()));
|
||||
break;
|
||||
case Type::Void:
|
||||
mOutfile<<"void";
|
||||
break;
|
||||
case Type::Callback:
|
||||
break;
|
||||
case Type::Array:
|
||||
mOutfile<<"Array.<Object>";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void JavascriptGenerator::writeArgument(Argument *arg, ArgKind kind){
|
||||
switch(kind){
|
||||
case Normal:
|
||||
mOutfile<<" * @param {";
|
||||
writeType(arg->getType());
|
||||
mOutfile<<"} "<<arg->getName()<<" - "<<arg->getHelp()<<endl;
|
||||
break;
|
||||
case Return:
|
||||
mOutfile<<" * @returns {";
|
||||
writeType(arg->getType());
|
||||
mOutfile<<"} "<<arg->getHelp()<<endl;
|
||||
break;
|
||||
case PropertyArg:
|
||||
mOutfile<<" * @property {";
|
||||
writeType(arg->getType());
|
||||
mOutfile<<"} "<<arg->getName()<<" - "<<arg->getHelp()<<endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void JavascriptGenerator::writeTabs(int ntabs){
|
||||
int i;
|
||||
for(i=0;i<ntabs;++i)
|
||||
mOutfile<<"\t";
|
||||
}
|
||||
|
||||
void JavascriptGenerator::writeHelpComment(const std::string &comment, int ntabs){
|
||||
size_t i;
|
||||
int curindex=0;
|
||||
writeTabs(ntabs);
|
||||
mOutfile<<" * ";
|
||||
for(i=0;i<comment.size();i++,curindex++){
|
||||
if (comment[i]=='\n' || (curindex>100 && comment[i]==' ')){
|
||||
mOutfile<<endl;
|
||||
writeTabs(ntabs);
|
||||
mOutfile<<" * ";
|
||||
curindex=0;
|
||||
}else mOutfile<<comment[i];
|
||||
}
|
||||
}
|
||||
|
||||
void JavascriptGenerator::writeProperty(Property *prop){
|
||||
if (prop->getName()=="userData" || prop->getName()=="userPointer") return;
|
||||
mOutfile<<"/**"<<endl;
|
||||
writeHelpComment(prop->getHelp(),0);
|
||||
mOutfile<<endl;
|
||||
mOutfile<<" * @member {";
|
||||
writeType(prop->getType());
|
||||
mOutfile<<"} external:"<<mCurClass->getName()<<"#"<<prop->getName()<<endl;
|
||||
if (prop->getAttribute()==Property::ReadOnly)
|
||||
mOutfile<<" * @readonly"<<endl;
|
||||
mOutfile<<"**/"<<endl;
|
||||
}
|
||||
|
||||
void JavascriptGenerator::writeMethod(Method *method){
|
||||
Argument *retarg=method->getReturnArg();
|
||||
const list<Argument*> &args=method->getArgs();
|
||||
list<Argument*>::const_iterator it;
|
||||
|
||||
if (method->isCallback()) return;
|
||||
if (method->getPropertyBehaviour()!=Method::None) return;
|
||||
if (method->getName()=="ref" || method->getName()=="unref") return;
|
||||
|
||||
mOutfile<<"/**"<<endl;
|
||||
writeHelpComment(method->getHelp(),0);
|
||||
mOutfile<<endl;
|
||||
mOutfile<<" * @function external:"<<mCurClass->getName()<<"#"<<method->getName()<<endl;
|
||||
|
||||
for(it=args.begin();it!=args.end();++it){
|
||||
writeArgument(*it);
|
||||
}
|
||||
writeArgument(retarg,Return);
|
||||
mOutfile<<"**/"<<endl;
|
||||
mOutfile<<endl;
|
||||
}
|
||||
|
||||
string JavascriptGenerator::getEventHelp(const string &help){
|
||||
size_t i=help.find("Callback");
|
||||
if (i==string::npos){
|
||||
i=help.find("callback");
|
||||
if (i==string::npos) return help;
|
||||
}
|
||||
string res(help);
|
||||
res.replace(i,8,"event");
|
||||
return res;
|
||||
}
|
||||
|
||||
void JavascriptGenerator::writeEvent(Method* event){
|
||||
const list<Argument*> &args=event->getArgs();
|
||||
list<Argument*>::const_iterator it;
|
||||
|
||||
if (!event->isCallback()) return;
|
||||
mOutfile<<"/**"<<endl;
|
||||
writeHelpComment(getEventHelp(event->getHelp()),0);
|
||||
mOutfile<<endl;
|
||||
mOutfile<<" * @event external:"<<mCurClass->getName()<<"#"<<event->getName()<<endl;
|
||||
mOutfile<<" * @type {object}"<<endl;
|
||||
|
||||
for(it=args.begin();it!=args.end();++it){
|
||||
writeArgument(*it,PropertyArg);
|
||||
}
|
||||
mOutfile<<"**/"<<endl;
|
||||
mOutfile<<endl;
|
||||
}
|
||||
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
linphone
|
||||
Copyright (C) 2013 Belledonne Communications SARL
|
||||
Simon Morlat (simon.morlat@linphone.org)
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef generator_hh
|
||||
#define generator_hh
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "software-desc.hh"
|
||||
|
||||
class OutputGenerator{
|
||||
public:
|
||||
virtual void generate(Project *proj)=0;
|
||||
};
|
||||
|
||||
class CplusplusGenerator : public OutputGenerator{
|
||||
public:
|
||||
CplusplusGenerator();
|
||||
virtual void generate(Project *proj);
|
||||
private:
|
||||
void writeClass(Class *klass);
|
||||
void writeArgument(Argument *arg, bool isReturn=false);
|
||||
void writeTabs(int ntabs);
|
||||
void writeHelpComment(const std::string &comment, int ntabs);
|
||||
void writeMethod(Method *method);
|
||||
void writeEnumMember(ConstField *cf, bool isLast);
|
||||
ofstream mOutfile;
|
||||
Project *mCurProj;
|
||||
Class *mCurClass;
|
||||
};
|
||||
|
||||
class JavascriptGenerator : public OutputGenerator{
|
||||
public:
|
||||
JavascriptGenerator();
|
||||
virtual void generate(Project *proj);
|
||||
private:
|
||||
void writeClass(Class *klass);
|
||||
void writeEnum(Class *klass);
|
||||
void writeType(Type *type);
|
||||
enum ArgKind { Normal, Return, PropertyArg};
|
||||
void writeArgument(Argument *arg, ArgKind kind=Normal);
|
||||
void writeTabs(int ntabs);
|
||||
void writeHelpComment(const std::string &comment, int ntabs);
|
||||
void writeProperty(Property *prop);
|
||||
void writeMethod(Method *method);
|
||||
void writeEvent(Method *event);
|
||||
string getEventHelp(const string &ref);
|
||||
string getEnumName(Class *klass);
|
||||
ofstream mOutfile;
|
||||
Project *mCurProj;
|
||||
Class *mCurClass;
|
||||
};
|
||||
|
||||
string to_lower(const string &str);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,482 +0,0 @@
|
|||
/*
|
||||
linphone
|
||||
Copyright (C) 2013 Belledonne Communications SARL
|
||||
Simon Morlat (simon.morlat@linphone.org)
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "software-desc.hh"
|
||||
#include "generator.hh"
|
||||
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
|
||||
static bool isSpace(const char *str){
|
||||
for(;*str!='\0';++str){
|
||||
if (!isspace(*str)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//Convenient class for examining node recursively
|
||||
class XmlNode{
|
||||
public:
|
||||
XmlNode(const xmlNode *node=NULL) : mNode(node){
|
||||
}
|
||||
XmlNode getChild(const string &name)const{
|
||||
if (mNode==NULL) return XmlNode();
|
||||
xmlNode *it;
|
||||
for(it=mNode->children;it!=NULL;it=it->next){
|
||||
if (xmlStrcmp(it->name,(const xmlChar*)name.c_str())==0)
|
||||
return XmlNode(it);
|
||||
}
|
||||
return XmlNode();
|
||||
}
|
||||
XmlNode getChildRecursive(const string &name)const{
|
||||
if (mNode==NULL) return XmlNode();
|
||||
xmlNode *it;
|
||||
//find in direct children
|
||||
for(it=mNode->children;it!=NULL;it=it->next){
|
||||
if (xmlStrcmp(it->name,(const xmlChar*)name.c_str())==0)
|
||||
return XmlNode(it);
|
||||
}
|
||||
//recurse into children
|
||||
for(it=mNode->children;it!=NULL;it=it->next){
|
||||
XmlNode res=XmlNode(it).getChildRecursive(name);
|
||||
if (!res.isNull()) return res;
|
||||
}
|
||||
return XmlNode();
|
||||
}
|
||||
list<XmlNode> getChildren(const string &name)const{
|
||||
xmlNode *it;
|
||||
list<XmlNode> nodes;
|
||||
|
||||
if (mNode==NULL) return nodes;
|
||||
for(it=mNode->children;it!=NULL;it=it->next){
|
||||
if (xmlStrcmp(it->name,(const xmlChar*)name.c_str())==0)
|
||||
nodes.push_back(XmlNode(it));
|
||||
}
|
||||
if (nodes.empty()) cerr<<"getChildren() no "<<name<<" found"<<endl;
|
||||
return nodes;
|
||||
}
|
||||
string getText()const{
|
||||
if (mNode==NULL) return "";
|
||||
XmlNode node=getChild("text");
|
||||
if (!node.isNull()) {
|
||||
const char *text=(const char*)node.mNode->content;
|
||||
if (!isSpace(text)) return string(text);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
string getProp(const string &propname)const{
|
||||
if (mNode==NULL) return "";
|
||||
xmlChar *value;
|
||||
value=xmlGetProp((xmlNode*)mNode,(const xmlChar*)propname.c_str());
|
||||
if (value) return string((const char*)value);
|
||||
return "";
|
||||
}
|
||||
bool isNull()const{
|
||||
return mNode==NULL;
|
||||
}
|
||||
private:
|
||||
const xmlNode *mNode;
|
||||
};
|
||||
|
||||
static Argument *parseArgument(XmlNode node, bool isReturn){
|
||||
string name=node.getChild("declname").getText();
|
||||
Type *type=NULL;
|
||||
string typecontent=node.getChild("type").getText();
|
||||
bool isConst=false;
|
||||
bool isPointer=false;
|
||||
|
||||
//find documented type if any
|
||||
string tname=node.getChild("type").getChild("ref").getText();
|
||||
if (!tname.empty()){
|
||||
type=Type::getType(tname);
|
||||
}else type=Type::getType(typecontent);
|
||||
|
||||
//find const attribute if any
|
||||
if (typecontent.find("const")!=string::npos)
|
||||
isConst=true;
|
||||
|
||||
if (typecontent.find("*")!=string::npos)
|
||||
isPointer=true;
|
||||
|
||||
if (type==NULL) {
|
||||
return NULL;
|
||||
}
|
||||
//cout<<"Parsed argument "<<name<<" with type "<<type->getBasicType()<<" "<<type->getName()<<endl;
|
||||
return new Argument(type,!isReturn ? name : "",isConst,isPointer);
|
||||
}
|
||||
|
||||
static string classNameToPrefix(const std::string &classname){
|
||||
char *tmp = new char[classname.size()*2];
|
||||
char *w=tmp;
|
||||
size_t i;
|
||||
|
||||
for(i=0;i<classname.size();i++){
|
||||
char p=classname[i];
|
||||
if (isupper(p)){
|
||||
if (i!=0){
|
||||
*w++='_';
|
||||
}
|
||||
*w++=tolower(p);
|
||||
}else *w++=p;
|
||||
}
|
||||
*w++='\0';
|
||||
string ret(tmp);
|
||||
delete[] tmp;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static string makeMethodName(const string & suffix){
|
||||
char *tmp = new char[suffix.size()];
|
||||
char *w=tmp;
|
||||
size_t i;
|
||||
bool useUpper=false;
|
||||
|
||||
for(i=0;i<suffix.size();i++){
|
||||
char p=suffix[i];
|
||||
|
||||
if (p=='_'){
|
||||
if (i>0)
|
||||
useUpper=true;
|
||||
}else{
|
||||
if (useUpper)
|
||||
*w++=toupper(p);
|
||||
else
|
||||
*w++=p;
|
||||
useUpper=false;
|
||||
}
|
||||
}
|
||||
*w++='\0';
|
||||
string ret(tmp);
|
||||
delete[] tmp;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static string extractMethodName(const string &c_name, const std::string& class_name){
|
||||
string prefix=classNameToPrefix(class_name);
|
||||
if (c_name.find(prefix)==0){
|
||||
return makeMethodName(c_name.substr(prefix.size(),string::npos));
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static string getHelpBody(XmlNode myNode){
|
||||
ostringstream result;
|
||||
XmlNode brief=myNode.getChild("briefdescription");
|
||||
XmlNode detailed=myNode.getChild("detaileddescription");
|
||||
|
||||
result<<brief.getText();
|
||||
result<<detailed.getChild("para").getText();
|
||||
//cout<<"getHelpBody():"<<result.str();
|
||||
return result.str();
|
||||
}
|
||||
|
||||
static void parseFunction(Project *proj, xmlNode *node){
|
||||
string name;
|
||||
Argument *first_arg=NULL;
|
||||
string className;
|
||||
string methodName;
|
||||
Argument *retarg=NULL;
|
||||
list<Argument*> args;
|
||||
string help;
|
||||
XmlNode funcnode(node);
|
||||
XmlNode parameterlist;
|
||||
list<XmlNode> params;
|
||||
list<XmlNode> paramsHelp;
|
||||
list<XmlNode>::iterator it,helpit;
|
||||
|
||||
name=funcnode.getChild("name").getText();
|
||||
params=funcnode.getChildren("param");
|
||||
parameterlist=funcnode.getChild("detaileddescription").getChildRecursive("parameterlist");
|
||||
if (parameterlist.isNull()) cerr<<"parameterlist not found"<<endl;
|
||||
paramsHelp=parameterlist.getChildren("parameteritem");
|
||||
|
||||
for (it=params.begin(),helpit=paramsHelp.begin();it!=params.end();++it){
|
||||
Argument *a=parseArgument(*it,false);
|
||||
if (a){
|
||||
//add argument help
|
||||
if (!args.empty()){
|
||||
if (helpit!=paramsHelp.end()){
|
||||
XmlNode item=*helpit;
|
||||
a->setHelp(item.getChild("parameterdescription").getChild("para").getText());
|
||||
}else cerr<<"Undocumented parameter "<<a->getName()<<" in function "<<name<<endl;
|
||||
}
|
||||
args.push_back(a);
|
||||
if (helpit!=paramsHelp.end()) ++helpit;
|
||||
}
|
||||
else return;
|
||||
}
|
||||
help=getHelpBody(funcnode);
|
||||
|
||||
retarg=parseArgument(funcnode,true);
|
||||
if (!retarg){
|
||||
cerr<<"Could not parse return argument of function "<<name<<endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.empty()) first_arg=args.front();
|
||||
if (!first_arg){
|
||||
cerr<<"Could not determine first argument of "<<name<<endl;
|
||||
return;
|
||||
}
|
||||
if (first_arg->getType()->getBasicType()!=Type::Class) return;
|
||||
className=first_arg->getType()->getName();
|
||||
methodName=extractMethodName(name,className);
|
||||
if (!methodName.empty() && methodName!="destroy"){
|
||||
//cout<<"Found "<<className<<"."<<methodName<<"()"<<endl;
|
||||
args.pop_front();
|
||||
Method *method=new Method("",retarg,methodName,args,first_arg->isConst(),false);
|
||||
method->setHelp(help);
|
||||
proj->getClass(className)->addMethod(method);
|
||||
delete first_arg;
|
||||
}
|
||||
}
|
||||
|
||||
static string findCommon(const string &c1, const string & c2){
|
||||
size_t i;
|
||||
ostringstream res;
|
||||
for(i=0;i<c1.size() && i<c2.size();++i){
|
||||
if (tolower(c1[i])==tolower(c2[i]))
|
||||
res<<(char)c1[i];
|
||||
else break;
|
||||
}
|
||||
return res.str();
|
||||
}
|
||||
|
||||
static string extractCallbackName(const string &c_name, const string & classname){
|
||||
string prefix=findCommon(c_name,classname);
|
||||
string res=c_name.substr(prefix.size(),string::npos);
|
||||
res[0]=tolower(res[0]);
|
||||
size_t pos=res.find("Cb");
|
||||
if (pos!=string::npos) res=res.substr(0,pos);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static void parseCallback(Project *proj, XmlNode node){
|
||||
string argsstring=node.getChild("argsstring").getText();
|
||||
string name=node.getChild("name").getText();
|
||||
list<XmlNode> params=node.getChildRecursive("parameterlist").getChildren("parameteritem");
|
||||
list<XmlNode>::iterator it=params.begin();
|
||||
string rettype=node.getChild("type").getText();
|
||||
argsstring=argsstring.substr(argsstring.find('(')+1,string::npos);
|
||||
bool cont=true;
|
||||
list<Argument*> args;
|
||||
Type *firstArgType=NULL;
|
||||
|
||||
rettype=rettype.substr(0,rettype.find('('));
|
||||
Argument *retarg=new Argument(Type::getType(rettype),"",false,rettype.find('*')!=string::npos);
|
||||
|
||||
do{
|
||||
size_t comma=argsstring.find(',');
|
||||
size_t end=argsstring.find(')');
|
||||
if (comma!=string::npos && comma<end) end=comma;
|
||||
else cont=false;
|
||||
string arg=argsstring.substr(0,end);
|
||||
bool isConst=false;
|
||||
bool isPointer=false;
|
||||
|
||||
size_t endtype=arg.find('*');
|
||||
if (endtype==string::npos) endtype=arg.rfind(' ');
|
||||
else isPointer=true;
|
||||
string typestring=arg.substr(0,endtype+1);
|
||||
Type *type=Type::getType(typestring);
|
||||
|
||||
if (type==NULL) return;
|
||||
|
||||
if (firstArgType==NULL) firstArgType=type;
|
||||
|
||||
//find const attribute if any
|
||||
if (typestring.find("const")!=string::npos)
|
||||
isConst=true;
|
||||
|
||||
string argname=arg.substr(endtype+1,end);
|
||||
argsstring=argsstring.substr(end+1,string::npos);
|
||||
Argument *argobj=new Argument(type,makeMethodName(argname),isConst,isPointer);
|
||||
if (it!=params.end()){
|
||||
argobj->setHelp((*it).getChild("parameterdescription").getChild("para").getText());
|
||||
++it;
|
||||
}
|
||||
args.push_back(argobj);
|
||||
}while(cont);
|
||||
|
||||
if (firstArgType->getBasicType()!=Type::Class) return;
|
||||
Class *klass=proj->getClass(firstArgType->getName());
|
||||
Method *callback=new Method("", retarg, extractCallbackName(name,klass->getName()), args, false, false, true);
|
||||
//cout<<"Found callback "<<callback->getName()<<" with "<<args.size()<<" arguments."<<endl;
|
||||
callback->setHelp(node.getChild("detaileddescription").getChild("para").getText());
|
||||
klass->addMethod(callback);
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void parseEnum(Project *proj, XmlNode node){
|
||||
string name=node.getChild("name").getText();
|
||||
if (name[0]=='_') name.erase(0,1);
|
||||
Class *klass=proj->getClass(name);
|
||||
klass->setHelp(node.getChild("detaileddescription").getChild("para").getText());
|
||||
list<XmlNode> enumValues=node.getChildren("enumvalue");
|
||||
list<XmlNode>::iterator it;
|
||||
int value = 0;
|
||||
for (it=enumValues.begin();it!=enumValues.end();++it){
|
||||
string initializer = (*it).getChild("initializer").getText();
|
||||
if ((initializer.length() > 1) && (initializer.at(0) == '=')) {
|
||||
std::stringstream ss;
|
||||
if ((initializer.length() > 2) && (initializer.at(1) == '0')) {
|
||||
if ((initializer.length() > 3) && (initializer.at(2) == 'x')) {
|
||||
ss << std::hex << initializer.substr(3);
|
||||
} else {
|
||||
ss << std::oct << initializer.substr(2);
|
||||
}
|
||||
} else {
|
||||
ss << std::dec << initializer.substr(1);
|
||||
}
|
||||
ss >> value;
|
||||
}
|
||||
ConstField *cf=new ConstField(Type::getType("int"),(*it).getChild("name").getText(),value);
|
||||
cf->setHelp((*it).getChild("detaileddescription").getChild("para").getText());
|
||||
klass->addConstField(cf);
|
||||
value++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void parseTypedef(Project *proj, xmlNode *node){
|
||||
XmlNode tdef(node);
|
||||
string typecontent=tdef.getChild("type").getText();
|
||||
string name=tdef.getChild("name").getText();
|
||||
if (typecontent.find("enum")==0){
|
||||
Type::addType(Type::Enum,name);
|
||||
}else if (typecontent.find("(*")!=string::npos){
|
||||
parseCallback(proj,node);
|
||||
}else
|
||||
proj->getClass(name)->setHelp(getHelpBody(node));
|
||||
}
|
||||
|
||||
static void parseMemberDef(Project *proj, xmlNode *node){
|
||||
XmlNode member(node);
|
||||
string brief;
|
||||
string detailed;
|
||||
string kind;
|
||||
|
||||
if (member.getChild("briefdescription").getText().empty() &&
|
||||
member.getChild("detaileddescription").getChild("para").getText().empty())
|
||||
return;
|
||||
if (member.getProp("id").find("group__")!=0)
|
||||
return;
|
||||
if (member.getChild("detaileddescription").getChildRecursive("xreftitle").getText()=="Deprecated")
|
||||
return;
|
||||
|
||||
kind=member.getProp("kind");
|
||||
if (kind=="function"){
|
||||
parseFunction(proj,node);
|
||||
}else if (kind=="typedef"){
|
||||
parseTypedef(proj,node);
|
||||
}else if (kind=="enum"){
|
||||
parseEnum(proj,node);
|
||||
}
|
||||
}
|
||||
|
||||
static void inspectNode(Project *proj, xmlNode *a_node){
|
||||
xmlNode *cur_node;
|
||||
|
||||
for (cur_node = a_node; cur_node != NULL ; cur_node = cur_node->next) {
|
||||
if (cur_node->type == XML_ELEMENT_NODE) {
|
||||
//printf("node type: Element, name: %s\n", cur_node->name);
|
||||
if (strcmp((const char*)cur_node->name,"memberdef")==0 ){
|
||||
//cout<<"Found memberdef"<<endl;
|
||||
parseMemberDef(proj,cur_node);
|
||||
}
|
||||
}
|
||||
if (cur_node->children) inspectNode(proj,cur_node->children);
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_file(Project *proj, const char *filename){
|
||||
xmlDoc *doc = NULL;
|
||||
xmlNode *root_element = NULL;
|
||||
|
||||
|
||||
/*parse the file and get the DOM */
|
||||
doc = xmlReadFile(filename, NULL, XML_PARSE_RECOVER);
|
||||
|
||||
if (doc == NULL) {
|
||||
cerr<<"xmlReadFile failed."<<endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*Get the root element node */
|
||||
root_element = xmlDocGetRootElement(doc);
|
||||
|
||||
inspectNode(proj,root_element);
|
||||
|
||||
/*free the document */
|
||||
xmlFreeDoc(doc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]){
|
||||
int i;
|
||||
string projectName="wrapper";
|
||||
OutputGenerator *gen=NULL;
|
||||
list<string> files;
|
||||
list<string>::iterator it;
|
||||
|
||||
LIBXML_TEST_VERSION
|
||||
|
||||
for(i=1;i<argc;i++){
|
||||
if (strcmp(argv[i],"--help")==0){
|
||||
fprintf(stderr,"%s: [--help] --output (c++, javascript) --project <project name> file1 file2...\nParses xml files generated by doxygen to output wrappers in a specified language.\n",argv[0]);
|
||||
return -1;
|
||||
}else if (strcmp(argv[i],"--output")==0){
|
||||
i++;
|
||||
if (strcmp(argv[i],"c++")==0){
|
||||
gen=new CplusplusGenerator();
|
||||
}else if (strcmp(argv[i],"javascript")==0){
|
||||
gen=new JavascriptGenerator();
|
||||
}
|
||||
}else if (strcmp(argv[i],"--project")==0){
|
||||
i++;
|
||||
projectName=argv[i];
|
||||
}else{
|
||||
files.push_back(argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (gen==NULL) {
|
||||
cerr<<"No output generator selected !"<<endl;
|
||||
return -1;
|
||||
}
|
||||
Project *proj=new Project(projectName);
|
||||
for(it=files.begin();it!=files.end();it++){
|
||||
if (parse_file(proj,(*it).c_str())==-1){
|
||||
cerr<<"Parsing aborted."<<endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
proj->analyse();
|
||||
gen->generate(proj);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
linphone
|
||||
Copyright (C) 2013 Belledonne Communications SARL
|
||||
Simon Morlat (simon.morlat@linphone.org)
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "software-desc.hh"
|
||||
|
||||
Type Type::sStringType(Type::String);
|
||||
Type Type::sIntegerType(Type::Integer);
|
||||
Type Type::sVoidType(Type::Void);
|
||||
Type Type::sBooleanType(Type::Boolean);
|
||||
Type Type::sFloatType(Type::Float);
|
||||
Type Type::sArrayType(Type::Array);
|
||||
|
||||
std::map<string,Type*> Type::mTypes;
|
||||
const char *Type::sBasicTypeNames[]={
|
||||
"Void",
|
||||
"Boolean",
|
||||
"Integer",
|
||||
"Float",
|
||||
"String",
|
||||
"Enum",
|
||||
"Class",
|
||||
"Callback",
|
||||
"Array",
|
||||
"undef",
|
||||
"undef"
|
||||
};
|
||||
|
|
@ -1,470 +0,0 @@
|
|||
/*
|
||||
linphone
|
||||
Copyright (C) 2013 Belledonne Communications SARL
|
||||
Simon Morlat (simon.morlat@linphone.org)
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef software_desc_hh
|
||||
#define software_desc_hh
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
|
||||
class Type{
|
||||
public:
|
||||
enum BasicType{
|
||||
Void,
|
||||
Boolean,
|
||||
Integer,
|
||||
Float,
|
||||
String,
|
||||
Enum,
|
||||
Class,
|
||||
Callback,
|
||||
Array
|
||||
};
|
||||
static const char *sBasicTypeNames[];
|
||||
static Type* addType(BasicType bt, const std::string &name){
|
||||
Type* ret;
|
||||
if ((ret=mTypes[name])==0){
|
||||
//cout<<"Adding new "<<sBasicTypeNames[(int)bt]<<" type '"<<name<<"'"<<endl;
|
||||
ret=mTypes[name]=new Type(bt,name);
|
||||
}else if (bt!=Class){
|
||||
ret->mBasic=bt;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static Type *getType(const std::std::string &tname){
|
||||
if (tname.find("(")!=std::string::npos) return NULL; //arrives when parsing function pointer declared inside function prototype
|
||||
if (strstr(tname.c_str(),"char")!=0 && strchr(tname.c_str(),'*')!=0){
|
||||
return &sStringType;
|
||||
}else if (tname.find("int")!=std::string::npos){
|
||||
return &sIntegerType;
|
||||
}else if (tname.find("size_t")!=std::string::npos){
|
||||
return &sIntegerType;
|
||||
}else if (tname.find("float")!=std::string::npos){
|
||||
return &sFloatType;
|
||||
}else if (tname.find("bool_t")!=std::string::npos){
|
||||
return &sBooleanType;
|
||||
}else if (tname.find("void")!=std::string::npos){
|
||||
return &sVoidType;
|
||||
}else if (tname.find("enum")!=std::string::npos){
|
||||
return addType(Enum,tname.c_str()+strlen("enum "));
|
||||
}else if (tname.find("MSList")!=std::string::npos){
|
||||
return &sArrayType;
|
||||
}else{/*an object?*/
|
||||
|
||||
std::string tmp=tname;
|
||||
size_t pos;
|
||||
|
||||
/*really ugly and slow*/
|
||||
|
||||
pos=tmp.find('*');
|
||||
if (pos!=std::string::npos)
|
||||
tmp.erase(pos,1);
|
||||
|
||||
pos=tmp.find("const");
|
||||
if (pos!=std::string::npos)
|
||||
tmp.erase(pos,strlen("const"));
|
||||
|
||||
while ((pos=tmp.find(' '))!=std::string::npos){
|
||||
tmp.erase(pos,1);
|
||||
}
|
||||
return addType(Class,tmp);
|
||||
}
|
||||
std::cerr<<"Unhandled type name"<<tname<<std::endl;
|
||||
return NULL;
|
||||
}
|
||||
const std::string &getName()const{
|
||||
return mName;
|
||||
}
|
||||
BasicType getBasicType()const{
|
||||
return mBasic;
|
||||
}
|
||||
private:
|
||||
BasicType mBasic;
|
||||
std::string mName;
|
||||
Type(BasicType basic, const std::string &tname="") : mBasic(basic), mName(tname){
|
||||
}
|
||||
static Type sStringType;
|
||||
static Type sIntegerType;
|
||||
static Type sVoidType;
|
||||
static Type sBooleanType;
|
||||
static Type sFloatType;
|
||||
static Type sArrayType;
|
||||
static std::map<std::string,Type*> mTypes;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Argument{
|
||||
public:
|
||||
Argument(Type *type, const std::string &argname, bool isConst, bool isPointer) : mType(type), mName(argname), mConst(isConst), mPointer(isPointer){
|
||||
if (!isPointer) mConst=false;
|
||||
}
|
||||
Type *getType()const{
|
||||
return mType;
|
||||
}
|
||||
bool isConst()const{
|
||||
return mConst;
|
||||
}
|
||||
const std::string &getName()const{
|
||||
return mName;
|
||||
}
|
||||
bool isPointer()const{
|
||||
return mPointer;
|
||||
}
|
||||
const std::string &getHelp()const{
|
||||
return mHelp;
|
||||
}
|
||||
void setHelp(const std::string &help){
|
||||
mHelp=help;
|
||||
}
|
||||
private:
|
||||
|
||||
Type *mType;
|
||||
std::string mName;
|
||||
std::string mHelp;
|
||||
bool mConst;
|
||||
bool mPointer;
|
||||
};
|
||||
|
||||
class Method{
|
||||
public:
|
||||
enum PropertyBehaviour{
|
||||
None,
|
||||
Read,
|
||||
Write
|
||||
};
|
||||
Method(const std::string &uid, Argument* return_arg, const std::string &name, const list<Argument*> &args, bool isConst, bool isStatic, bool isCallback=false){
|
||||
mUid=uid;
|
||||
mReturn=return_arg;
|
||||
mName=name;
|
||||
mArgs=args;
|
||||
mConst=isConst;
|
||||
mStatic=isStatic;
|
||||
mIsCallback=isCallback;
|
||||
analyseProperties();
|
||||
}
|
||||
void setHelp(const std::string &help){
|
||||
mHelp=help;
|
||||
}
|
||||
Argument *getReturnArg()const{
|
||||
return mReturn;
|
||||
}
|
||||
const string &getName()const{
|
||||
return mName;
|
||||
}
|
||||
const list<Argument*> &getArgs()const {
|
||||
return mArgs;
|
||||
}
|
||||
bool isConst()const{
|
||||
return mConst;
|
||||
}
|
||||
bool isStatic()const{
|
||||
return mStatic;
|
||||
}
|
||||
bool isCallback()const{
|
||||
return mIsCallback;
|
||||
}
|
||||
const string &getHelp(){
|
||||
return mHelp;
|
||||
}
|
||||
PropertyBehaviour getPropertyBehaviour()const{
|
||||
return mPropertyBehaviour;
|
||||
}
|
||||
const string &getPropertyName()const{
|
||||
return mPropertyName;
|
||||
}
|
||||
private:
|
||||
void analyseProperties(){
|
||||
size_t enabled_pos;
|
||||
mPropertyBehaviour=None;
|
||||
|
||||
if (mName.find("get")==0 && mArgs.size()==0){
|
||||
mPropertyName=mName.substr(3,string::npos);
|
||||
if (!mPropertyName.empty()){
|
||||
mPropertyName[0]=tolower(mPropertyName[0]);
|
||||
mPropertyBehaviour=Read;
|
||||
}
|
||||
}else if (mName.find("is")==0 && mArgs.size()==0){
|
||||
mPropertyName=mName.substr(2,string::npos);
|
||||
if (!mPropertyName.empty()){
|
||||
mPropertyName[0]=tolower(mPropertyName[0]);
|
||||
mPropertyBehaviour=Read;
|
||||
}
|
||||
}else if (mName.find("enable")==0 && mArgs.size()==1){
|
||||
mPropertyName=mName.substr(6,string::npos);
|
||||
if (!mPropertyName.empty()){
|
||||
mPropertyName[0]=tolower(mPropertyName[0]);
|
||||
mPropertyName+="Enabled";
|
||||
mPropertyBehaviour=Write;
|
||||
}
|
||||
}else if (mName.find("set")==0 && mArgs.size()==1){
|
||||
mPropertyName=mName.substr(3,string::npos);
|
||||
if (!mPropertyName.empty()){
|
||||
mPropertyName[0]=tolower(mPropertyName[0]);
|
||||
mPropertyBehaviour=Write;
|
||||
}
|
||||
}else if ((enabled_pos=mName.rfind("Enabled"))!=string::npos && mArgs.size()==0){
|
||||
size_t goodpos=mName.size()-7;
|
||||
if (enabled_pos==goodpos){
|
||||
mPropertyName=mName.substr(0,goodpos);
|
||||
if (!mPropertyName.empty()){
|
||||
mPropertyName+="Enabled";
|
||||
mPropertyBehaviour=Read;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mPropertyBehaviour==None) {
|
||||
mPropertyName="";
|
||||
if (mName.find("create")==0) {
|
||||
mName="new"+mName.substr(6,string::npos);
|
||||
}
|
||||
}
|
||||
}
|
||||
string mUid;
|
||||
Argument *mReturn;
|
||||
string mName;
|
||||
list<Argument*> mArgs;
|
||||
string mHelp;
|
||||
string mPropertyName; /*if it can be a property*/
|
||||
PropertyBehaviour mPropertyBehaviour;
|
||||
bool mConst;
|
||||
bool mStatic;
|
||||
bool mIsCallback;
|
||||
};
|
||||
|
||||
class Property{
|
||||
public:
|
||||
enum Attribute{
|
||||
ReadOnly,
|
||||
ReadWrite
|
||||
};
|
||||
Property(Attribute attr, const string &name, Type *type, const string &help) : mAttr(attr), mName(name), mType(type), mHelp(help){
|
||||
}
|
||||
const string &getName()const{
|
||||
return mName;
|
||||
}
|
||||
const string &getHelp()const{
|
||||
return mHelp;
|
||||
}
|
||||
void setHelp(const string &help){
|
||||
mHelp=help;
|
||||
}
|
||||
Attribute getAttribute()const{
|
||||
return mAttr;
|
||||
}
|
||||
void setAttribute(Attribute attr){
|
||||
mAttr=attr;
|
||||
}
|
||||
Type* getType()const{
|
||||
return mType;
|
||||
}
|
||||
private:
|
||||
Attribute mAttr;
|
||||
string mName;
|
||||
Type *mType;
|
||||
string mHelp;
|
||||
};
|
||||
|
||||
class ConstField{
|
||||
public:
|
||||
ConstField(Type *type, const string &name, int value) : mType(type), mName(name), mValue(value){
|
||||
}
|
||||
void setHelp(const string & help){
|
||||
mHelp=help;
|
||||
}
|
||||
const string &getHelp()const{
|
||||
return mHelp;
|
||||
}
|
||||
const string & getName()const{
|
||||
return mName;
|
||||
}
|
||||
Type *getType()const{
|
||||
return mType;
|
||||
}
|
||||
int getValue()const{
|
||||
return mValue;
|
||||
}
|
||||
static string getCommonPrefix(list<ConstField *> fields){
|
||||
if (fields.size()<2) return "";
|
||||
list<ConstField*>::iterator it;
|
||||
string prefix=fields.front()->getName();
|
||||
int prefixsize;
|
||||
|
||||
for (prefixsize=prefix.size();prefixsize>0;prefixsize--){
|
||||
bool isMatching=true;
|
||||
prefix=prefix.substr(0,prefixsize);
|
||||
|
||||
for(it=fields.begin();it!=fields.end();++it){
|
||||
ConstField *cf=*it;
|
||||
if (prefix != cf->getName().substr(0,prefixsize)){
|
||||
isMatching=false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isMatching){
|
||||
//cout<<"enum prefix: "<<prefix<<endl;
|
||||
return prefix;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
private:
|
||||
Type *mType;
|
||||
string mName;
|
||||
int mValue;
|
||||
string mHelp;
|
||||
};
|
||||
|
||||
template <typename _type>
|
||||
struct name_matcher{
|
||||
name_matcher(const string &name) : mName(name){}
|
||||
bool operator()(_type *cf){
|
||||
return cf->getName()==mName;
|
||||
}
|
||||
string mName;
|
||||
};
|
||||
|
||||
/*actually a class or an enum*/
|
||||
class Class{
|
||||
public:
|
||||
Class(const std::string &name): mName(name){
|
||||
}
|
||||
Type::BasicType getType(){
|
||||
return Type::getType(mName)->getBasicType();
|
||||
}
|
||||
void addMethod(Method *method){
|
||||
if (mMethods.find(method->getName())==mMethods.end())
|
||||
mMethods.insert(make_pair(method->getName(),method));
|
||||
}
|
||||
void addConstField(ConstField *field){
|
||||
list<ConstField*>::iterator it=find_if(mConstFields.begin(),mConstFields.end(),name_matcher<ConstField>(field->getName()));
|
||||
if (it==mConstFields.end())
|
||||
mConstFields.push_back(field);
|
||||
}
|
||||
void setHelp(const std::string &help){
|
||||
mHelp=help;
|
||||
}
|
||||
list<Method*> getMethods()const{
|
||||
list<Method*> ret;
|
||||
map<string,Method*>::const_iterator it;
|
||||
for(it=mMethods.begin();it!=mMethods.end();++it){
|
||||
ret.push_back((*it).second);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
const list<ConstField*> &getConstFields()const{
|
||||
return mConstFields;
|
||||
}
|
||||
const string &getName()const{
|
||||
return mName;
|
||||
}
|
||||
const string &getHelp()const{
|
||||
return mHelp;
|
||||
}
|
||||
list<Property*> getProperties(){
|
||||
list<Property*> ret;
|
||||
map<string,Property*>::const_iterator it;
|
||||
for(it=mProperties.begin();it!=mProperties.end();++it){
|
||||
ret.push_back((*it).second);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void computeProperties(){
|
||||
map<string,Method*>::const_iterator it;
|
||||
Property *prop;
|
||||
for (it=mMethods.begin();it!=mMethods.end();++it){
|
||||
Method *m=(*it).second;
|
||||
if (m->getPropertyBehaviour()==Method::Read){
|
||||
prop=mProperties[m->getPropertyName()];
|
||||
if (prop==NULL){
|
||||
prop=new Property(Property::ReadOnly,m->getPropertyName(),m->getReturnArg()->getType(), m->getHelp());
|
||||
mProperties[m->getPropertyName()]=prop;
|
||||
}
|
||||
}else if (m->getPropertyBehaviour()==Method::Write){
|
||||
prop=mProperties[m->getPropertyName()];
|
||||
if (prop==NULL){
|
||||
prop=new Property(Property::ReadWrite,m->getPropertyName(),m->getArgs().front()->getType(), m->getHelp());
|
||||
mProperties[m->getPropertyName()]=prop;
|
||||
}else{
|
||||
prop->setHelp(m->getHelp());
|
||||
prop->setAttribute(Property::ReadWrite);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private:
|
||||
map<string,Method*> mMethods;
|
||||
map<string,Property*> mProperties;
|
||||
list<ConstField*> mConstFields;
|
||||
string mName;
|
||||
string mHelp;
|
||||
};
|
||||
|
||||
class Project{
|
||||
public:
|
||||
Project(const string &name="wrapper") : mName(name){
|
||||
}
|
||||
Class *getClass(const std::string &name){
|
||||
Class *ret;
|
||||
if ((ret=mClasses[name])==NULL){
|
||||
ret=mClasses[name]=new Class(name);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
list<Class*> getClasses()const{
|
||||
list<Class*> ret;
|
||||
map<string,Class*>::const_iterator it;
|
||||
for(it=mClasses.begin();it!=mClasses.end();++it){
|
||||
ret.push_back((*it).second);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
const string &getName()const{
|
||||
return mName;
|
||||
}
|
||||
void analyse(){
|
||||
list<Class*> classes=getClasses();
|
||||
for_each(classes.begin(),classes.end(),mem_fun(&Class::computeProperties));
|
||||
}
|
||||
void addCallback(Method *callback){
|
||||
list<Method*>::iterator it=find_if(mCallbacks.begin(),mCallbacks.end(),name_matcher<Method>(callback->getName()));
|
||||
if (it==mCallbacks.end())
|
||||
mCallbacks.push_back(callback);
|
||||
}
|
||||
const list<Method*> &getCallbacks()const{
|
||||
return mCallbacks;
|
||||
}
|
||||
private:
|
||||
map<string,Class*> mClasses;
|
||||
list<Method*> mCallbacks;
|
||||
string mName;
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Reference in a new issue