jami-libclient issueshttps://git.jami.net/savoirfairelinux/jami-libclient/-/issues2018-12-05T18:17:03Zhttps://git.jami.net/savoirfairelinux/jami-libclient/-/issues/368Database: AccountID should be used to identify account instead URI2018-12-05T18:17:03ZSébastien BlinDatabase: AccountID should be used to identify account instead URIFor example if the device certificate is removed the account ID is the only thing accessible to identify an account. So we should use this.For example if the device certificate is removed the account ID is the only thing accessible to identify an account. So we should use this.Swarm-chatKateryna KostiukKateryna Kostiukhttps://git.jami.net/savoirfairelinux/jami-libclient/-/issues/357Make unit tests independent from each others2020-12-22T20:19:23ZSébastien BlinMake unit tests independent from each othersSee `/tests`See `/tests`Sébastien BlinSébastien Blinhttps://git.jami.net/savoirfairelinux/jami-libclient/-/issues/313video conference behaves weird if calls are joined in early state2018-05-25T12:53:43ZJami Botvideo conference behaves weird if calls are joined in early stateIssue generated from Tuleap's migration script.
**Originally submitted by: Stepan Salenikovich (ssalenik)**
<p>Several weird things happen in the daemon if a video conference is created from calls which are not yet in &quot;current&quot...Issue generated from Tuleap's migration script.
**Originally submitted by: Stepan Salenikovich (ssalenik)**
<p>Several weird things happen in the daemon if a video conference is created from calls which are not yet in "current" (talking) state, eg: if 2 calls are still ringing and they are joined in a conference:</p>
<ul>
<li>On the conf master side, the daemon log starts continiously printing "[1467910558.018|19087|video\_mixer.cpp:135 ] VideoFrame::allocBuffer() failed"</li>
<li>All participants receive no (or back) video, including master (this is probably related to the error above)</li>
</ul>
<p>This daemon behaviour can be reproduced by either manually creating 2 ringing calls and joining them, or by using the 'createConfFromParticipantList' function (via d-feet, since its not used in LRC).<br />
<br />
Related to this, using createConfFromParticipantList() causes a crash in LRC.<br />
<br />
This bug is blocking for the feature or being able to immediately add participants to a conference, without waiting for each call to start first.</p>Guillaume RoguezGuillaume Roguezhttps://git.jami.net/savoirfairelinux/jami-libclient/-/issues/315LRC: segfault due to assumption that a profile exists2018-05-25T13:31:48ZJami BotLRC: segfault due to assumption that a profile existsIssue generated from Tuleap's migration script.
**Originally submitted by: Stepan Salenikovich (ssalenik)**
<p>GNOME client has not integrated profiles yet:</p>
<p>&nbsp;</p>
<pre>
Program received signal SIGSEGV, Segmentati...Issue generated from Tuleap's migration script.
**Originally submitted by: Stepan Salenikovich (ssalenik)**
<p>GNOME client has not integrated profiles yet:</p>
<p> </p>
<pre>
Program received signal SIGSEGV, Segmentation fault.
Profile::person (this=0x0) at /home/ssalenikovich/projects/ring-lrc/src/profile.cpp:67
67 return d\_ptr->m\_Person;
(gdb) bt
\#0 0x0000000000587a50 in Profile::person() const (this=0x0) at /home/ssalenikovich/projects/ring-lrc/src/profile.cpp:67
\#1 0x0000000000587a6c in Profile::id() const (this=) at /home/ssalenikovich/projects/ring-lrc/src/profile.cpp:57
\#2 0x00000000005f31ca in ProfileModelPrivate::slotAccountAdded(Account\*) (this=this@entry=0x2b26420, acc=0x2202140) at /home/ssalenikovich/projects/ring-lrc/src/profilemodel.cpp:114
\#3 0x00000000005f58a5 in ProfileModelPrivate::slotDelayedInit() (this=0x2b26420) at /home/ssalenikovich/projects/ring-lrc/src/profilemodel.cpp:181
\#4 0x00000000005f59f5 in ProfileModelPrivate::qt\_static\_metacall(QObject\*, QMetaObject::Call, int, void\*\*) (\_o=, \_c=, \_id=, \_a=)
at /home/ssalenikovich/projects/ring-lrc/build/profilemodel.moc:97
\#5 0x00007ffff4757651 in QObject::event(QEvent\*) (this=0x2b26420, e=) at kernel/qobject.cpp:1245
\#6 0x00007ffff4725efc in QCoreApplication::notifyInternal(QObject\*, QEvent\*) (event=0x26ddd70, receiver=0x2b26420, this=0xb01cc0) at kernel/qcoreapplication.cpp:997
\#7 0x00007ffff4725efc in QCoreApplication::notifyInternal(QObject\*, QEvent\*) (this=0xb01cc0, receiver=0x2b26420, event=event@entry=0x26ddd70) at kernel/qcoreapplication.cpp:935
\#8 0x00007ffff4728057 in QCoreApplicationPrivate::sendPostedEvents(QObject\*, int, QThreadData\*) (event=0x26ddd70, receiver=)
at ../../include/QtCore/../../src/corelib/kernel/qcoreapplication.h:228
\#9 0x00007ffff4728057 in QCoreApplicationPrivate::sendPostedEvents(QObject\*, int, QThreadData\*) (receiver=receiver@entry=0x0, event\_type=event\_type@entry=0, data=0xa65b10) at kernel/qcoreapplication.cpp:1552
\#10 0x00007ffff4728588 in QCoreApplication::sendPostedEvents(QObject\*, int) (receiver=receiver@entry=0x0, event\_type=event\_type@entry=0) at kernel/qcoreapplication.cpp:1410
\#11 0x00007ffff477ce73 in postEventSourceDispatch(GSource\*, GSourceFunc, gpointer) (s=0xb01c00) at kernel/qeventdispatcher\_glib.cpp:271
\#12 0x00007ffff61f8ff7 in g\_main\_context\_dispatch (context=0xa72190) at /build/glib2.0-meArNm/glib2.0-2.46.2/./glib/gmain.c:3154
\#13 0x00007ffff61f8ff7 in g\_main\_context\_dispatch (context=context@entry=0xa72190) at /build/glib2.0-meArNm/glib2.0-2.46.2/./glib/gmain.c:3769
\#14 0x00007ffff61f9250 in g\_main\_context\_iterate (context=context@entry=0xa72190, block=block@entry=1, dispatch=dispatch@entry=1, self=)
at /build/glib2.0-meArNm/glib2.0-2.46.2/./glib/gmain.c:3840
\#15 0x00007ffff61f92fc in g\_main\_context\_iteration (context=0xa72190, context@entry=0x0, may\_block=may\_block@entry=1) at /build/glib2.0-meArNm/glib2.0-2.46.2/./glib/gmain.c:3901
\#16 0x00007ffff67baafc in g\_application\_run (application=0xa6e140 [RingClient], argc=2, argv=0x7fffffffde38) at /build/glib2.0-meArNm/glib2.0-2.46.2/./gio/gapplication.c:2311
\#17 0x00000000004e7476 in main(int, char\*\*) (argc=2, argv=0x7fffffffde38) at /home/ssalenikovich/projects/ring-client-gnome/src/main.cpp:34
</pre>
<p> </p>https://git.jami.net/savoirfairelinux/jami-libclient/-/issues/316lrc: do not assume ip2ip account exits2018-05-25T13:31:54ZJami Botlrc: do not assume ip2ip account exitsIssue generated from Tuleap's migration script.
**Originally submitted by: Guillaume Roguez (guillaume)**
<p>The daemon has been updated to no longer require nor create an ip2ip account by default; this change has not been reflected in ...Issue generated from Tuleap's migration script.
**Originally submitted by: Guillaume Roguez (guillaume)**
<p>The daemon has been updated to no longer require nor create an ip2ip account by default; this change has not been reflected in lrc which still assumes its existence.</p>
<p>This bug was originally discovered via the following crash in Ring GNOME:</p>
<p>On gnome client, typing "toto" (not existing contact) raising a segmentation fault.</p>
<p>Follow gdb output:</p>
<pre>
(gdb) bt full
\#0 0x00007ffff7954de6 in QSharedPointer<AccountPrivate>::data() const (this=0x18)
at /usr/include/qt5/QtCore/qsharedpointer\_impl.h:301
\#1 0x00007ffff7954578 in QSharedPointer<AccountPrivate>::operator->() const (this=0x18)
at /usr/include/qt5/QtCore/qsharedpointer\_impl.h:306
\#2 0x00007ffff794542e in Account::alias() const (this=0x0) at /home/yomgui/Projects/ring-project/lrc/src/account.cpp:362
\#3 0x00007ffff79fff07 in NumberCompletionModel::data(QModelIndex const&, int) const (this=0x10a8020, index=..., role=0)
at /home/yomgui/Projects/ring-project/lrc/src/numbercompletionmodel.cpp:220
i = {i = 0x12bb730}
n = 0x124aa80
weight = 261
needAcc = false
\#4 0x0000000000456b7d in QModelIndex::data(int) const (this=0x7fffffffd350, arole=0)
at /usr/include/qt5/QtCore/qabstractitemmodel.h:420
\#5 0x000000000045e42f in autocompletion\_account\_render(GtkCellLayout\*, GtkCellRenderer\*, GtkTreeModel\*, GtkTreeIter\*, gpointer) (cell\_layout=0x10f2a10, cell=0xf89860, model=0x10f2d90, iter=0xb948c0, user\_data=0x0)
at /home/yomgui/Projects/ring-project/client-gnome/src/ringmainwindow.cpp:740
alias =
{d = {data = {c = 16 '\\020', uc = 16 '\\020', s = 16, sc = 16 '\\020', us = 16, i = 16, u = 16, l = 16, ul = 16, b = 16, d = 7,9050503334599447e-323, f = 2,24207754e-44, real = 7,9050503334599447e-323, ll = 16, ull = 16, o = 0x10, ptr = 0x10, shared = 0x10}, type = 16291952, is\_shared = 0, is\_null = 0}}
text = 0x110bb00 "\\b"
idx = {r = 1, c = 0, i = 0, m = 0x10a8020}
\#6 0x00007ffff6ee635b in apply\_cell\_attributes () at /lib64/libgtk-3.so.0
\#7 0x00007ffff5447590 in g\_hash\_table\_foreach () at /lib64/libglib-2.0.so.0
\#8 0x00007ffff6ee61eb in gtk\_cell\_area\_real\_apply\_attributes () at /lib64/libgtk-3.so.0
\#9 0x00007ffff6eeb7e9 in gtk\_cell\_area\_box\_apply\_attributes () at /lib64/libgtk-3.so.0
\#10 0x00007ffff6fd6983 in \_gtk\_marshal\_VOID\_\_OBJECT\_BOXED\_BOOLEAN\_BOOLEANv () at /lib64/libgtk-3.so.0
\#11 0x00007ffff57569d4 in \_g\_closure\_invoke\_va () at /lib64/libgobject-2.0.so.0
\#12 0x00007ffff57712bd in g\_signal\_emit\_valist () at /lib64/libgobject-2.0.so.0
\#13 0x00007ffff57718ff in g\_signal\_emit () at /lib64/libgobject-2.0.so.0
\#14 0x00007ffff6ee7d56 in gtk\_cell\_area\_apply\_attributes () at /lib64/libgtk-3.so.0
\#15 0x00007ffff70ed0cd in validate\_row () at /lib64/libgtk-3.so.0
\#16 0x00007ffff70f4d92 in do\_validate\_rows () at /lib64/libgtk-3.so.0
\#17 0x00007ffff70f54b1 in gtk\_tree\_view\_get\_preferred\_width () at /lib64/libgtk-3.so.0
\#18 0x00007ffff7065154 in gtk\_widget\_query\_size\_for\_orientation () at /lib64/libgtk-3.so.0
\#19 0x00007ffff706539f in gtk\_widget\_compute\_size\_for\_orientation () at /lib64/libgtk-3.so.0
\#20 0x00007ffff70654f2 in gtk\_widget\_get\_preferred\_width\_for\_height () at /lib64/libgtk-3.so.0
\#21 0x00007ffff7065741 in \_gtk\_widget\_get\_preferred\_size\_and\_baseline () at /lib64/libgtk-3.so.0
\#22 0x00007ffff6f57933 in \_gtk\_entry\_completion\_resize\_popup () at /lib64/libgtk-3.so.0
\#23 0x00007ffff6f58c14 in gtk\_entry\_completion\_timeout () at /lib64/libgtk-3.so.0
\#24 0x00007ffff6b145a8 in gdk\_threads\_dispatch () at /lib64/libgdk-3.so.0
---Type <return> to continue, or q <return> to quit---
\#25 0x00007ffff5458893 in g\_timeout\_dispatch () at /lib64/libglib-2.0.so.0
\#26 0x00007ffff5457e3a in g\_main\_context\_dispatch () at /lib64/libglib-2.0.so.0
\#27 0x00007ffff54581d0 in g\_main\_context\_iterate.isra () at /lib64/libglib-2.0.so.0
\#28 0x00007ffff545827c in g\_main\_context\_iteration () at /lib64/libglib-2.0.so.0
\#29 0x00007ffff5a42a0c in g\_application\_run () at /lib64/libgio-2.0.so.0
\#30 0x0000000000453767 in main(int, char\*\*) (argc=1, argv=0x7fffffffe018)
at /home/yomgui/Projects/ring-project/client-gnome/src/main.cpp:34
client = 0x7d6140
</pre>https://git.jami.net/savoirfairelinux/jami-libclient/-/issues/320daemon/lrc: SIP calls fail due to impropery formated URI from lrc2018-05-25T13:32:51ZJami Botdaemon/lrc: SIP calls fail due to impropery formated URI from lrcIssue generated from Tuleap's migration script.
**Originally submitted by: Stepan Salenikovich (ssalenik)**
<p>Initially reported as possibly an ICE/STUN/pjsip problem; however the issue is that lrc passes the SIP uri without the hostna...Issue generated from Tuleap's migration script.
**Originally submitted by: Stepan Salenikovich (ssalenik)**
<p>Initially reported as possibly an ICE/STUN/pjsip problem; however the issue is that lrc passes the SIP uri without the hostname, eg: "sip:1234" instead of "sip:1234@hostname". Several other users have reported this issue.</p>
<p>This is a regression due to this fix in lrc, the purpose of which is to prevent leaking the ring ID via DNS:<br />
https://gerrit-ring.savoirfairelinux.com/\#/c/3804/</p>
<p>User reported that SIP calls always immediately fail. The SIP account is registered and he has a STUN server configured. The config works with linphone and sflphone.</p>
<p> </p>
<p>dring log</p>
<p> </p>
<pre>
14:42:47.520 stuntp0x2630f2 !.STUN mapped address found/changed: 108.x.x.x:21137
14:42:47.521 16798201754592 .Comp 1: Binding discovery complete, srflx address is 108.x.x.x:21137
14:42:47.521 stuntp0x2622c3 .STUN mapped address found/changed: 108.x.x.x:39003
14:42:47.521 16798201754592 .Comp 2: Binding discovery complete, srflx address is 108.x.x.x:39003
14:42:47.523 stuntp0x262cff .STUN mapped address found/changed: 108.x.x.x:32024
14:42:47.523 16798201754592 .Comp 4: Binding discovery complete, srflx address is 108.x.x.x:32024
14:42:47.523 stuntp0x261782 .STUN mapped address found/changed: 108.x.x.x:17967
14:42:47.523 16798201754592 .Comp 3: Binding discovery complete, srflx address is 108.x.x.x:17967
[1461940967.523| 4999|ice\_transport.cpp:259 ] ICE initialization success
[1461940967.524| 4999|ice\_transport.cpp:320 ] ICE as master
14:42:47.524 16798201754592 .ICE session created, comp\_cnt=4, role is Controlling agent
14:42:47.524 16798201754592 .ICE nomination type set to regular
14:42:47.524 16798201754592 .Candidate 0 added: comp\_id=1, type=srflx, foundation=Sc0a8013d, addr=108.x.x.x:21137, base=192.168.1.61:51697, prio=0x6effffff (1862270975)
14:42:47.524 16798201754592 .Candidate 1 added: comp\_id=1, type=host, foundation=Hc0a8013d, addr=192.168.1.61:51697, base=192.168.1.61:51697, prio=0x64ffffff (1694498815)
14:42:47.524 16798201754592 .Candidate 2 added: comp\_id=2, type=srflx, foundation=Sc0a8013d, addr=108.x.x.x:39003, base=192.168.1.61:39426, prio=0x6efffffe (1862270974)
14:42:47.524 16798201754592 .Candidate 3 added: comp\_id=2, type=host, foundation=Hc0a8013d, addr=192.168.1.61:39426, base=192.168.1.61:39426, prio=0x64fffffe (1694498814)
14:42:47.524 16798201754592 .Candidate 4 added: comp\_id=3, type=srflx, foundation=Sc0a8013d, addr=108.x.x.x:17967, base=192.168.1.61:60836, prio=0x6efffffd (1862270973)
14:42:47.524 16798201754592 .Candidate 5 added: comp\_id=3, type=host, foundation=Hc0a8013d, addr=192.168.1.61:60836, base=192.168.1.61:60836, prio=0x64fffffd (1694498813)
14:42:47.524 16798201754592 .Candidate 6 added: comp\_id=4, type=srflx, foundation=Sc0a8013d, addr=108.x.x.x:32024, base=192.168.1.61:42707, prio=0x6efffffc (1862270972)
14:42:47.524 16798201754592 .Candidate 7 added: comp\_id=4, type=host, foundation=Hc0a8013d, addr=192.168.1.61:42707, base=192.168.1.61:42707, prio=0x64fffffc (1694498812)
[1461940967.524| 4999|ice\_transport.cpp:313 ] ICE [local] ufrag=787b0db8, pwd=4430533a
[1461940967.524| 4898|sipvoiplink.cpp:1357 ] STUN mapping of '192.168.1.61:5060'
[1461940967.570| 4898|sipvoiplink.cpp:1383 ] STUN server stun.ideasip.com replied '108.x.x.x:10030'
[1461940967.570| 4898|sipaccountbase.cpp:352 ] [Account d41616a7b6ffc1fe] Using public address 108.x.x.x
[1461940967.570| 4898|sipaccount.cpp:340 ] contact header: / -> sip:301
[1461940967.570| 4898|sip\_utils.cpp:87 ] Adding route proxy.sipthor.net
14:42:47.570 resolver.c !...Transmitting 45 bytes to NS 0 (127.0.0.1:53): DNS SRV query for \_sip.\_udp.proxy.sipthor.net: Success
[1461940967.571| 4898|sipvoiplink.cpp:821 ] [call:16798201754592209698] INVITE@0x2717a88 state changed to 1 (CALLING): cause=0, tsx@0x271e188 status 0 (Default status message)
[1461940967.571| 4898|sipvoiplink.cpp:1141 ] [INVITE:0x2717a88] tsx\_role=0, tsx\_state=1, ev\_type=5, tsx\_state\_type=2
[1461940967.571| 4898|call.cpp:144 ] [call:16798201754592209698] state change 0/1, cnx 0/2, code 0
[1461940967.571| 4898|call.cpp:163 ] [call:16798201754592209698] emit client call state change CONNECTING, code 0
[1461940967.571| 4898|manager.cpp:409 ] ----- Switch current call id to '16798201754592209698' -----
[1461940967.573| 4898|videomanager.cpp:80 ] Setting default device to Integrated Camera
14:42:47.603 \_sip.\_udp.prox DNS SRV resolution failed for \_sip.\_udp.proxy.sipthor.net: DNS "Name Error" (PJLIB\_UTIL\_EDNS\_NXDOMAIN)
14:42:47.604 \_sip.\_udp.prox DNS SRV resolution failed for \_sip.\_udp.proxy.sipthor.net, trying resolving A record for proxy.sipthor.net
14:42:47.604 resolver.c Transmitting 35 bytes to NS 0 (127.0.0.1:53): DNS A query for proxy.sipthor.net: Success
14:42:47.614 tsx0x271e188 Temporary failure in sending Request msg INVITE/cseq=5960 (tdta0x271b170), will try next server: Unsuitable transport selected (PJSIP\_ETPNOTSUITABLE)
14:42:47.614 tsx0x271e188 Temporary failure in sending Request msg INVITE/cseq=5960 (tdta0x271b170), will try next server: Unsuitable transport selected (PJSIP\_ETPNOTSUITABLE)
14:42:47.614 tsx0x271e188 Temporary failure in sending Request msg INVITE/cseq=5960 (tdta0x271b170), will try next server: Unsuitable transport selected (PJSIP\_ETPNOTSUITABLE)
[1461940967.755| 4898|sipvoiplink.cpp:1141 ] [INVITE:0x2717a88] tsx\_role=0, tsx\_state=3, ev\_type=5, tsx\_state\_type=3
[1461940967.765| 4898|sipvoiplink.cpp:1141 ] [INVITE:0x2717a88] tsx\_role=0, tsx\_state=1, ev\_type=5, tsx\_state\_type=2
[1461940967.765| 4898|sipvoiplink.cpp:1141 ] [INVITE:0x2717a88] tsx\_role=0, tsx\_state=4, ev\_type=5, tsx\_state\_type=3
[1461940967.907| 4898|sipvoiplink.cpp:1141 ] [INVITE:0x2717a88] tsx\_role=0, tsx\_state=3, ev\_type=5, tsx\_state\_type=3
[1461940967.938| 4898|sipvoiplink.cpp:821 ] [call:16798201754592209698] INVITE@0x2717a88 state changed to 6 (DISCONNCTD): cause=500, tsx@0x2725038 status 500 (Internal Server Error)
[1461940967.938| 4898|call.cpp:144 ] [call:16798201754592209698] state change 1/4, cnx 2/0, code 500
[1461940967.938| 4898|call.cpp:163 ] [call:16798201754592209698] emit client call state change FAILURE, code 500
[1461940967.938| 4898|manager.cpp:1770 ] [call:16798201754592209698] Failed
[1461940967.938| 4898|call\_factory.cpp:39 ] Removing call 16798201754592209698
[1461940967.938| 4898|call\_factory.cpp:43 ] Remaining 0 SIP call(s)
[1461940967.939| 5004|alsalayer.cpp:176 ] Alsa: Opening default
[1461940967.943| 5004|alsalayer.cpp:376 ] Buffer size range from 2048 to 8192
[1461940967.943| 5004|alsalayer.cpp:377 ] Period size range from 1024 to 1024
[1461940967.943| 5004|alsalayer.cpp:392 ] Was set period\_size = 1024
[1461940967.943| 5004|alsalayer.cpp:393 ] Was set buffer\_size = 2048
[1461940967.943| 5004|alsalayer.cpp:404 ] capture using format {2 channels, 48000Hz}
[1461940967.943| 5004|alsalayer.cpp:176 ] Alsa: Opening default
[1461940967.944| 5004|alsalayer.cpp:376 ] Buffer size range from 2048 to 8192
[1461940967.945| 5004|alsalayer.cpp:377 ] Period size range from 1024 to 1024
[1461940967.945| 5004|alsalayer.cpp:392 ] Was set period\_size = 1024
[1461940967.945| 5004|alsalayer.cpp:393 ] Was set buffer\_size = 2048
[1461940967.945| 5004|alsalayer.cpp:404 ] playback using format {2 channels, 48000Hz}
[1461940967.945| 5004|audiolayer.cpp:55 ] Hardware audio format available : {2 channels, 48000Hz}
[1461940967.945| 5004|manager.cpp:2301 ] Audio format changed: {1 channels, 16000Hz} -> {2 channels, 48000Hz}
14:42:48.327 16798201754592 ....Destroying ICE session 0x7f1900000ba8
14:42:48.327 16798201754592 .....ICE stream transport 0x2620c88 destroyed
14:42:48.327 ice\_session.c .....ICE session 0x7f1900000ba8 destroyed
[1461940968.327| 4898|call.cpp:144 ] [call:16798201754592209698] state change 4/5, cnx 0/0, code 0
[1461940968.327| 4898|call.cpp:163 ] [call:16798201754592209698] emit client call state change OVER, code 0
[1461940968.327| 4898|audiorecord.cpp:214 ] Stop recording /home/programms/sflphone/20160429-144247-sip\_301-ring.wav
[1461940968.327| 4898|manager.cpp:2567 ] Call is NULL
[1461940968.329| 4898|manager.cpp:2567 ] Call is NULL
[1461940972.975| 4898|manager.cpp:548 ] Could not hang up non-existant call 16798201754592209698
</pre>
<p> </p>https://git.jami.net/savoirfairelinux/jami-libclient/-/issues/338New year crash in LRC2018-05-25T13:36:24ZJami BotNew year crash in LRCIssue generated from Tuleap's migration script.
**Originally submitted by: Emmanuel Lepage Vallee (elv13)**
The new time calculation code is broken as each values can be negative and this isn't taken into account.
I will revert to ...Issue generated from Tuleap's migration script.
**Originally submitted by: Emmanuel Lepage Vallee (elv13)**
The new time calculation code is broken as each values can be negative and this isn't taken into account.
I will revert to a version based on my old code but including the changes introduced by the rewrite.
It is faster anyway. In my opinion, it is also clear and simple and don't depend on 3rd party APIs at all, just plain C and decimal logic