diff --git a/build/rpm/liblinphone.spec.cmake b/build/rpm/liblinphone.spec.cmake index b0f030334..f2512146c 100755 --- a/build/rpm/liblinphone.spec.cmake +++ b/build/rpm/liblinphone.spec.cmake @@ -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.* diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 9e50dc709..590bfb90e 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -42,8 +42,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include #include #include -#include -#include +#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 @@ -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) { diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 310444f69..5da325464 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -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; } diff --git a/src/c-wrapper/internal/c-sal.h b/src/c-wrapper/internal/c-sal.h index c7bd15ef6..77cd79a01 100644 --- a/src/c-wrapper/internal/c-sal.h +++ b/src/c-wrapper/internal/c-sal.h @@ -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) diff --git a/src/sal/op.cpp b/src/sal/op.cpp index b53be38c3..b8df469df 100644 --- a/src/sal/op.cpp +++ b/src/sal/op.cpp @@ -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."); diff --git a/src/sal/sal.cpp b/src/sal/sal.cpp index 71c2e169e..193588ca4 100644 --- a/src/sal/sal.cpp +++ b/src/sal/sal.cpp @@ -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); diff --git a/src/sal/sal.h b/src/sal/sal.h index 082929f1b..1de178d5f 100644 --- a/src/sal/sal.h +++ b/src/sal/sal.h @@ -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 mSupportedContentTypes; std::string mLinphoneSpecs; + belle_tls_crypto_config_postcheck_callback_t mTlsPostcheckCb; + void *mTlsPostcheckCbData; // Cache values mutable std::string mDnsUserHostsFile; diff --git a/tester/CMakeLists.txt b/tester/CMakeLists.txt index 5ac5eeddc..0872bb63b 100644 --- a/tester/CMakeLists.txt +++ b/tester/CMakeLists.txt @@ -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 ) diff --git a/tester/certificates/client/cert3.pem b/tester/certificates/client/cert3.pem new file mode 100644 index 000000000..1bffa265d --- /dev/null +++ b/tester/certificates/client/cert3.pem @@ -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----- diff --git a/tester/certificates/client/key3.pem b/tester/certificates/client/key3.pem new file mode 100644 index 000000000..144fdee61 --- /dev/null +++ b/tester/certificates/client/key3.pem @@ -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----- diff --git a/tester/certificates/client/openssl-altname.cnf b/tester/certificates/client/openssl-altname.cnf new file mode 100644 index 000000000..f97c844d7 --- /dev/null +++ b/tester/certificates/client/openssl-altname.cnf @@ -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) diff --git a/tester/flexisip/README b/tester/flexisip/README index b2799db77..c4dce60b8 100644 --- a/tester/flexisip/README +++ b/tester/flexisip/README @@ -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. + diff --git a/tester/flexisip/flexisip-generic.conf b/tester/flexisip/flexisip-generic.conf index 2795738cd..a25e61166 100644 --- a/tester/flexisip/flexisip-generic.conf +++ b/tester/flexisip/flexisip-generic.conf @@ -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. diff --git a/tester/flexisip/flexisip.conf b/tester/flexisip/flexisip.conf index 682f3180b..e5b800fe2 100644 --- a/tester/flexisip/flexisip.conf +++ b/tester/flexisip/flexisip.conf @@ -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. diff --git a/tester/flexisip_tester.c b/tester/flexisip_tester.c index 1a178fb7e..bc85fd026 100644 --- a/tester/flexisip_tester.c +++ b/tester/flexisip_tester.c @@ -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),*/ diff --git a/tester/register_tester.c b/tester/register_tester.c index e69ec1937..9865b5845 100644 --- a/tester/register_tester.c +++ b/tester/register_tester.c @@ -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), diff --git a/tester/setup_tester.c b/tester/setup_tester.c index f213e044e..590a3c41b 100644 --- a/tester/setup_tester.c +++ b/tester/setup_tester.c @@ -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) { diff --git a/tools/generator.cc b/tools/generator.cc deleted file mode 100644 index de8528a9e..000000000 --- a/tools/generator.cc +++ /dev/null @@ -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 - -#include "generator.hh" - -#ifdef _WIN32 -#include - -#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 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<getName()<<"="<getValue(); - if (!isLast) mOutfile<<","; - if (!cf->getHelp().empty()) mOutfile<<"\t/**< "<getHelp()<<" */"; - mOutfile<getName()<<"/"<getName()<<".hh"; - mOutfile.open(filename.str().c_str()); - if (!mOutfile.is_open()){ - cerr<<"Could not write into "< methods=klass->getMethods(); - list constFields=klass->getConstFields(); - mCurClass=klass; - mOutfile<<"/* Wrapper generated by lp-gen-wrappers, do not edit*/"<"<getName().empty()) - mOutfile<<"namespace "<getName()<<"{"<getType()==Type::Enum){ - mOutfile<<"enum "<getName()<<"{"<::iterator cfit,next; - for (cfit=constFields.begin();cfit!=constFields.end();){ - ConstField *cf=*cfit; - writeEnumMember(cf,++cfit==constFields.end()); - } - }else{ - mOutfile<<"class "<getName()<<"{"<getName().empty()) - mOutfile<<"} //end of namespace "<getName()<getType(); - - if (type->getBasicType()==Type::Class){ - if (arg->isConst()){ - mOutfile<<"const "; - } - mOutfile<getName(); - if (arg->isPointer()) - mOutfile<<"*"; - }else if (type->getBasicType()==Type::Integer){ - mOutfile<<"int"; - }else if (type->getBasicType()==Type::Enum){ - mOutfile<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<<" "<getName(); -} - -void CplusplusGenerator::writeTabs(int ntabs){ - int i; - for(i=0;i100 && comment[i]==' ')){ - mOutfile<isCallback()) return; - - Argument *retarg=method->getReturnArg(); - const list &args=method->getArgs(); - list::const_iterator it; - - writeTabs(1); - mOutfile<<"/**"<getHelp(),1); - mOutfile<getName()<<"("; - - for(it=args.begin();it!=args.end();++it){ - if (it!=args.begin()) mOutfile<<", "; - writeArgument(*it); - } - mOutfile<<")"; - if (method->isConst()) mOutfile<<"const"; - mOutfile<<";"< 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<getName())<<"/"<getName())<<".js"; - mOutfile.open(filename.str().c_str()); - if (!mOutfile.is_open()){ - cerr<<"Could not write into "<getName()<getName()<<" = {};"<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 members=klass->getConstFields(); - list::iterator it; - string enum_name=getEnumName(klass); - - filename<getName())<<"/"<getName()<<" = "<getName()<<" || {};"<getHelp(),0); - mOutfile<getName()<<"."<getHelp().empty()){ - writeTabs(1); - mOutfile<<"/**"<getHelp(),1); - mOutfile<getName().substr(prefix_size,string::npos)<<" : "<getValue(); - if (++it!=members.end()) mOutfile<<","; - mOutfile<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 &methods=klass->getMethods(); - if (methods.empty()) return;//skip empty classes - - filename<getName())<<"/"<getName())<<".js"; - mOutfile.open(filename.str().c_str()); - if (!mOutfile.is_open()){ - cerr<<"Could not write into "<getName().empty()) - // mOutfile<<"namespace "<getName()<<"{"<getHelp()<getName()< properties=klass->getProperties(); - for_each(properties.begin(),properties.end(),bind1st(mem_fun(&JavascriptGenerator::writeProperty),this)); - mOutfile<getName().empty()) - // mOutfile<<"} //end of namespace "<getName()<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:"<getName(); - break; - case Type::Enum: - mOutfile<getName()<<"."<getClass(type->getName())); - break; - case Type::Void: - mOutfile<<"void"; - break; - case Type::Callback: - break; - case Type::Array: - mOutfile<<"Array."; - break; - } -} - -void JavascriptGenerator::writeArgument(Argument *arg, ArgKind kind){ - switch(kind){ - case Normal: - mOutfile<<" * @param {"; - writeType(arg->getType()); - mOutfile<<"} "<getName()<<" - "<getHelp()<getType()); - mOutfile<<"} "<getHelp()<getType()); - mOutfile<<"} "<getName()<<" - "<getHelp()<100 && comment[i]==' ')){ - mOutfile<getName()=="userData" || prop->getName()=="userPointer") return; - mOutfile<<"/**"<getHelp(),0); - mOutfile<getType()); - mOutfile<<"} external:"<getName()<<"#"<getName()<getAttribute()==Property::ReadOnly) - mOutfile<<" * @readonly"<getReturnArg(); - const list &args=method->getArgs(); - list::const_iterator it; - - if (method->isCallback()) return; - if (method->getPropertyBehaviour()!=Method::None) return; - if (method->getName()=="ref" || method->getName()=="unref") return; - - mOutfile<<"/**"<getHelp(),0); - mOutfile<getName()<<"#"<getName()< &args=event->getArgs(); - list::const_iterator it; - - if (!event->isCallback()) return; - mOutfile<<"/**"<getHelp()),0); - mOutfile<getName()<<"#"<getName()< - -#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 diff --git a/tools/genwrappers.cc b/tools/genwrappers.cc deleted file mode 100644 index d55f3dc6f..000000000 --- a/tools/genwrappers.cc +++ /dev/null @@ -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 -#include -#include -#include -#include -#include - - - -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 getChildren(const string &name)const{ - xmlNode *it; - list 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 "<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 "<getBasicType()<<" "<getName()<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< args; - string help; - XmlNode funcnode(node); - XmlNode parameterlist; - list params; - list paramsHelp; - list::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"<setHelp(item.getChild("parameterdescription").getChild("para").getText()); - }else cerr<<"Undocumented parameter "<getName()<<" in function "<getType()->getBasicType()!=Type::Class) return; - className=first_arg->getType()->getName(); - methodName=extractMethodName(name,className); - if (!methodName.empty() && methodName!="destroy"){ - //cout<<"Found "<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 params=node.getChildRecursive("parameterlist").getChildren("parameteritem"); - list::iterator it=params.begin(); - string rettype=node.getChild("type").getText(); - argsstring=argsstring.substr(argsstring.find('(')+1,string::npos); - bool cont=true; - list 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 && commasetHelp((*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 "<getName()<<" with "<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 enumValues=node.getChildren("enumvalue"); - list::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"<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."< files; - list::iterator it; - - LIBXML_TEST_VERSION - - for(i=1;i 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 !"<analyse(); - gen->generate(proj); - return 0; -} diff --git a/tools/software-desc.cc b/tools/software-desc.cc deleted file mode 100644 index 7cf96ac48..000000000 --- a/tools/software-desc.cc +++ /dev/null @@ -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 Type::mTypes; -const char *Type::sBasicTypeNames[]={ - "Void", - "Boolean", - "Integer", - "Float", - "String", - "Enum", - "Class", - "Callback", - "Array", - "undef", - "undef" -}; diff --git a/tools/software-desc.hh b/tools/software-desc.hh deleted file mode 100644 index c20b22425..000000000 --- a/tools/software-desc.hh +++ /dev/null @@ -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 -#include -#include -#include -#include -#include - -#include -#include -#include - - - -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 "<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"< 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 &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 &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 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 fields){ - if (fields.size()<2) return ""; - list::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: "< -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::iterator it=find_if(mConstFields.begin(),mConstFields.end(),name_matcher(field->getName())); - if (it==mConstFields.end()) - mConstFields.push_back(field); - } - void setHelp(const std::string &help){ - mHelp=help; - } - list getMethods()const{ - list ret; - map::const_iterator it; - for(it=mMethods.begin();it!=mMethods.end();++it){ - ret.push_back((*it).second); - } - return ret; - } - const list &getConstFields()const{ - return mConstFields; - } - const string &getName()const{ - return mName; - } - const string &getHelp()const{ - return mHelp; - } - list getProperties(){ - list ret; - map::const_iterator it; - for(it=mProperties.begin();it!=mProperties.end();++it){ - ret.push_back((*it).second); - } - return ret; - } - void computeProperties(){ - map::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 mMethods; - map mProperties; - list 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 getClasses()const{ - list ret; - map::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 classes=getClasses(); - for_each(classes.begin(),classes.end(),mem_fun(&Class::computeProperties)); - } - void addCallback(Method *callback){ - list::iterator it=find_if(mCallbacks.begin(),mCallbacks.end(),name_matcher(callback->getName())); - if (it==mCallbacks.end()) - mCallbacks.push_back(callback); - } - const list &getCallbacks()const{ - return mCallbacks; - } -private: - map mClasses; - list mCallbacks; - string mName; -}; - -#endif