summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorme_desk <me_desk@fb96df88-216d-11de-830c-f3023f12d8ac>2012-06-17 21:15:12 +0000
committerme_desk <me_desk@fb96df88-216d-11de-830c-f3023f12d8ac>2012-06-17 21:15:12 +0000
commit8df9466ada46e4b07990b9cb7e8b1cd61b96844a (patch)
tree2cb652e5839bed672133ffa90e7ad477f55cfb6b
downloadpica-pica-8df9466ada46e4b07990b9cb7e8b1cd61b96844a.tar.gz
pica-pica-8df9466ada46e4b07990b9cb7e8b1cd61b96844a.zip
todo list update
git-svn-id: svn://localhost/project_picapica/trunk@128 fb96df88-216d-11de-830c-f3023f12d8ac
-rwxr-xr-xMakefile21
-rwxr-xr-xPICA_client.c1381
-rwxr-xr-xPICA_client.h186
-rw-r--r--PICA_common.h41
-rwxr-xr-xPICA_msgproc.c86
-rwxr-xr-xPICA_msgproc.h24
-rwxr-xr-xPICA_node.c2266
-rwxr-xr-xPICA_node.h159
-rwxr-xr-xPICA_nodeaddrlist.c119
-rwxr-xr-xPICA_nodeaddrlist.h44
-rw-r--r--PICA_nodejoinskynet.c182
-rw-r--r--PICA_nodejoinskynet.h17
-rwxr-xr-xPICA_proto.h100
-rwxr-xr-xbuild.txt10
-rwxr-xr-xdoc/TASKS.txt78
-rwxr-xr-xdoc/bugtraq.txt21
-rwxr-xr-xdoc/concepts.txt1
-rwxr-xr-xdoc/glossary.txt1
-rwxr-xr-xdoc/proto.txt148
-rwxr-xr-xmacros.txt3
-rw-r--r--pica-client/accounts.cpp107
-rw-r--r--pica-client/accounts.h35
-rw-r--r--pica-client/accountswindow.cpp205
-rw-r--r--pica-client/accountswindow.h43
-rw-r--r--pica-client/askpassword.cpp52
-rw-r--r--pica-client/askpassword.h27
-rw-r--r--pica-client/chatwindow.cpp140
-rw-r--r--pica-client/chatwindow.h53
-rw-r--r--pica-client/contactlistwidget.cpp188
-rw-r--r--pica-client/contactlistwidget.h43
-rw-r--r--pica-client/contacts.cpp133
-rw-r--r--pica-client/contacts.h34
-rw-r--r--pica-client/dialogs/addaccountdialog.cpp114
-rw-r--r--pica-client/dialogs/addaccountdialog.h41
-rw-r--r--pica-client/dialogs/forgedcertdialog.cpp53
-rw-r--r--pica-client/dialogs/forgedcertdialog.h34
-rw-r--r--pica-client/dialogs/viewcertdialog.cpp68
-rw-r--r--pica-client/dialogs/viewcertdialog.h28
-rw-r--r--pica-client/globals.h26
-rw-r--r--pica-client/main.cpp161
-rw-r--r--pica-client/mainwindow.cpp110
-rw-r--r--pica-client/mainwindow.h48
-rw-r--r--pica-client/mainwindow.ui40
-rw-r--r--pica-client/msguirouter.cpp93
-rw-r--r--pica-client/msguirouter.h37
-rw-r--r--pica-client/nodes.cpp73
-rw-r--r--pica-client/nodes.h30
-rw-r--r--pica-client/nodethread.cpp94
-rw-r--r--pica-client/nodethread.h40
-rw-r--r--pica-client/pica-client.pro70
-rw-r--r--pica-client/picaactioncenter.cpp37
-rw-r--r--pica-client/picaactioncenter.h28
-rw-r--r--pica-client/picapica-icon-fly.pngbin0 -> 1675 bytes
-rw-r--r--pica-client/picapica-icon-sit.pngbin0 -> 1092 bytes
-rw-r--r--pica-client/picasystray.cpp39
-rw-r--r--pica-client/picasystray.h31
-rw-r--r--pica-client/skynet.cpp437
-rw-r--r--pica-client/skynet.h83
-rwxr-xr-xtests/0337.pem45
-rwxr-xr-xtests/0338.pem45
-rwxr-xr-xtests/0339.pem45
-rw-r--r--tests/033B.pem45
-rw-r--r--tests/033C.pem12
-rw-r--r--tests/033D.pem147
-rw-r--r--tests/033E.pem149
-rw-r--r--tests/033F.pem149
-rwxr-xr-xtests/033a.pem45
-rwxr-xr-xtests/64kb.txt307
-rwxr-xr-xtests/Makefile.win36
-rwxr-xr-xtests/add_newconnbin0 -> 979697 bytes
-rwxr-xr-xtests/build.txt1
-rwxr-xr-xtests/ca/ca_config.txt36
-rw-r--r--tests/ca/newtestca.pem16
-rw-r--r--tests/ca/newtestca_pkey.pem18
-rw-r--r--tests/ca/picapica_test_CA.pem54
-rw-r--r--tests/ca/picapica_test_CA_pkey.pem102
-rwxr-xr-xtests/ca/privkey_testnetCA.pem18
-rwxr-xr-xtests/ca/serial1
-rwxr-xr-xtests/ca/serial.old1
-rwxr-xr-xtests/ca/testnetCA.pem15
-rwxr-xr-xtests/ca/testnet_cert_index_db.txt9
-rwxr-xr-xtests/ca/testnet_cert_index_db.txt.attr1
-rwxr-xr-xtests/ca/testnet_cert_index_db.txt.attr.old1
-rwxr-xr-xtests/ca/testnet_cert_index_db.txt.old7
-rwxr-xr-xtests/cert_pkey.pem35
-rwxr-xr-xtests/cert_pkey_3.pem33
-rwxr-xr-xtests/cert_pkey_n2.pem33
-rw-r--r--tests/create_certs_commands.txt6
-rw-r--r--tests/dh2048.pem8
-rw-r--r--tests/dh512.pem4
-rwxr-xr-xtests/fdssizebin0 -> 6674 bytes
-rwxr-xr-xtests/fdssize.c9
-rwxr-xr-xtests/getidbin0 -> 7320 bytes
-rw-r--r--tests/nodeaddrlist_test.txt5
-rwxr-xr-xtests/privkey_tester4.pem18
-rw-r--r--tests/save_nodelist.txt4
-rwxr-xr-xtests/test_addnewconn.c42
-rw-r--r--tests/test_addrconv.c23
-rwxr-xr-xtests/test_clientbin0 -> 34109 bytes
-rwxr-xr-xtests/test_client.c234
-rwxr-xr-xtests/test_client_win.dev79
-rwxr-xr-xtests/test_client_win.exebin0 -> 1090031 bytes
-rwxr-xr-xtests/test_client_win.layout20
-rwxr-xr-xtests/test_getidfromsubstr.c43
-rwxr-xr-xtests/test_msgprocbin0 -> 7390 bytes
-rwxr-xr-xtests/test_msgproc.c18
-rwxr-xr-xtests/test_nodeaddrbin0 -> 8618 bytes
-rw-r--r--tests/test_nodeaddr.c28
-rwxr-xr-xtests/test_randseed.c14
-rwxr-xr-xtests/test_randseed.dev69
-rwxr-xr-xtests/test_randseed.exebin0 -> 339039 bytes
-rw-r--r--tests/tester10_pkey.pem18
-rw-r--r--tests/tester10cert.pem32
-rw-r--r--tests/tester11_pkey.pem18
-rw-r--r--tests/tester11cert.pem30
-rw-r--r--tests/tester12cert.pem96
-rw-r--r--tests/tester12pkey.pem54
-rw-r--r--tests/tester13cert.pem98
-rw-r--r--tests/tester13cert_forged.pem203
-rw-r--r--tests/tester13pkey.pem54
-rw-r--r--tests/tester13pkey_forged.pem54
-rw-r--r--tests/tester13req_forged.pem28
-rwxr-xr-xtests/tester5_privkey.pem18
-rwxr-xr-xtests/tester5cert.pem63
-rwxr-xr-xtests/tester6_privkey.pem18
-rwxr-xr-xtests/tester6cert.pem63
-rwxr-xr-xtests/tester6req.pem10
-rwxr-xr-xtests/tester8_privkey.pem18
-rwxr-xr-xtests/tester8cert.pem63
-rwxr-xr-xtests/tester8req.pem10
-rwxr-xr-xtests/tester9_privkey.pem18
-rwxr-xr-xtests/tester9cert.pem63
-rwxr-xr-xtests/tester9req.pem10
-rw-r--r--tests/trustedCA/newtestca.pem16
-rw-r--r--tests/trustedCA/picapica_test_CA.pem54
-rw-r--r--tests/trustedCA/picapica_test_CA_pkey.pem102
-rwxr-xr-xtests/trustedCA/testnetCA.pem15
-rwxr-xr-xtests/trusted_CA.pem87
-rwxr-xr-xtests/tst_sizeofbin0 -> 7053 bytes
-rwxr-xr-xtests/tst_sizeof.c16
140 files changed, 11122 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100755
index 0000000..b0293f7
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,21 @@
+OBJS= PICA_msgproc.o PICA_nodeaddrlist.o PICA_node.o PICA_nodejoinskynet.o
+CFLAGS= -pedantic -Wall
+
+all: node;
+
+node: $(OBJS)
+ gcc $(OBJS) -l ssl -l crypto -lsqlite3 -o node # добавить CFLAGS и все такое
+
+PICA_node.o: PICA_node.c PICA_node.h PICA_msgproc.h PICA_nodeaddrlist.h PICA_nodejoinskynet.h
+ gcc $(CFLAGS) -c PICA_node.c
+
+PICA_msgproc.o: PICA_msgproc.c PICA_msgproc.h
+ gcc $(CFLAGS) -c PICA_msgproc.c
+
+PICA_nodeaddrlist.o: PICA_nodeaddrlist.c PICA_nodeaddrlist.h
+ gcc $(CFLAGS) -c PICA_nodeaddrlist.c
+
+PICA_nodejoinskynet.o: PICA_nodejoinskynet.c PICA_nodejoinskynet.h PICA_nodeaddrlist.h
+ gcc $(CFLAGS) -c PICA_nodejoinskynet.c
+
+
diff --git a/PICA_client.c b/PICA_client.c
new file mode 100755
index 0000000..8ac5f6b
--- /dev/null
+++ b/PICA_client.c
@@ -0,0 +1,1381 @@
+#include "PICA_client.h"
+#include "PICA_proto.h"
+#include "PICA_msgproc.h"
+#include "PICA_common.h"
+
+//#ifdef NO_RAND_DEV
+//#include "PICA_rand_seed.h"
+//#endif
+
+#define PICA_DEBUG // debug
+
+#ifdef PICA_DEBUG
+#include <stdio.h>
+#endif
+
+//static SSL_CTX* ctx;
+//unsigned char __hellomsg[4]={0xCA,0xCA,PICA_PROTO_VER_HIGH,PICA_PROTO_VER_LOW};
+
+//char _msgbuf[65536];//
+
+static struct PICA_client_callbacks callbacks;
+
+#ifdef PICA_MULTITHREADED
+
+#ifdef WIN32
+static HANDLE *mt_locks;
+#else
+static pthread_mutex_t *mt_locks;
+#endif
+
+#endif
+static unsigned int procmsg_INITRESP(unsigned char*,unsigned int,void*);
+static unsigned int procmsg_CONNREQINC(unsigned char*,unsigned int,void*);
+static unsigned int procmsg_NOTFOUND(unsigned char*,unsigned int,void*);
+static unsigned int procmsg_FOUND(unsigned char*,unsigned int,void*);
+static unsigned int procmsg_CLNODELIST(unsigned char*,unsigned int,void*);
+static unsigned int procmsg_MSGUTF8(unsigned char*,unsigned int,void*);
+static unsigned int procmsg_MSGOK(unsigned char*,unsigned int,void*);
+
+static struct PICA_proto_msg* c2n_writebuf_push(struct PICA_conninfo *ci, unsigned int msgid, unsigned int size);
+static struct PICA_proto_msg* c2c_writebuf_push(struct PICA_chaninfo *chn, unsigned int msgid, unsigned int size);
+
+#define MSGINFO_MSGSNUM(arr) (sizeof(arr)/sizeof(struct PICA_msginfo))
+const struct PICA_msginfo c2n_messages[] = {
+ {PICA_PROTO_CONNREQINC, PICA_MSG_FIXED_SIZE, PICA_PROTO_CONNREQINC_SIZE, procmsg_CONNREQINC},
+ {PICA_PROTO_NOTFOUND, PICA_MSG_FIXED_SIZE, PICA_PROTO_NOTFOUND_SIZE, procmsg_NOTFOUND},
+ {PICA_PROTO_FOUND, PICA_MSG_FIXED_SIZE, PICA_PROTO_FOUND_SIZE, procmsg_FOUND},
+ {PICA_PROTO_CLNODELIST, PICA_MSG_VAR_SIZE, PICA_MSG_VARSIZE_INT16, procmsg_CLNODELIST}
+};//---!!! PING!!!
+
+const struct PICA_msginfo c2c_messages[] = {
+ {PICA_PROTO_MSGUTF8, PICA_MSG_VAR_SIZE, PICA_MSG_VARSIZE_INT16, procmsg_MSGUTF8},
+ {PICA_PROTO_MSGOK, PICA_MSG_FIXED_SIZE, PICA_PROTO_MSGOK_SIZE, procmsg_MSGOK}
+};
+
+struct PICA_msginfo c2n_init_messages[] = {
+ {PICA_PROTO_INITRESPOK, PICA_MSG_FIXED_SIZE, PICA_PROTO_INITRESPOK_SIZE, procmsg_INITRESP},
+ {PICA_PROTO_VERDIFFER, PICA_MSG_FIXED_SIZE, PICA_PROTO_VERDIFFER_SIZE, procmsg_INITRESP}
+};
+
+
+// static int pem_passwd_cb(char *buf, int size, int rwflag, void *userdata)
+//{
+//strncpy(buf, (char *)(userdata), size);
+//buf[size - 1] = 0;
+//return(strlen(buf));
+//}
+
+//функция возвращает номер клиента id в бинарном виде, извлекая его из строки,
+//которая возвращается функцией X509_NAME_oneline и представляет собой DN из сертификата клиента
+ static int get_id_fromsubjstr(char* DN_str,unsigned int* id)
+{
+char* tmp1;
+char* tmp2;
+
+tmp1=strstr(DN_str,"/CN=");
+
+if (!tmp1)
+ return 0;
+
+tmp2=strchr(tmp1,'#');
+
+if (!tmp2)
+ return 0;
+
+*tmp2=0;
+
+tmp1+=4;
+
+if (tmp1==tmp2)
+ return 0;
+
+*id=(unsigned int)strtol(tmp1,0,10);
+
+*tmp2='#';
+return 1;
+}
+
+ int PICA_get_id_from_cert(const char *cert_file, unsigned int *p_id)
+ {
+ char *DN_str;
+ X509 *x;
+ FILE *f;
+
+ f=fopen(cert_file,"r");
+
+ if (!f)
+ {
+ perror(cert_file);
+ return 0;
+ }
+
+ x=PEM_read_X509(f,0,0,0);
+ fclose(f);
+
+ DN_str=X509_NAME_oneline(X509_get_subject_name(x), 0, 0);
+
+ if (!get_id_fromsubjstr(DN_str,p_id))
+ {
+ OPENSSL_free(DN_str);
+ return 0;
+ }
+ printf("my id is %u\n",*p_id);//debug
+ puts(DN_str);//debug
+
+ OPENSSL_free(DN_str);
+ return 1;
+ }
+
+
+
+static int add_chaninfo(struct PICA_conninfo *ci,struct PICA_chaninfo **chn,unsigned int uid,int is_outgoing)
+{
+struct PICA_chaninfo *chnl,*ipt;
+
+chnl=*chn=(struct PICA_chaninfo*)calloc(sizeof(struct PICA_chaninfo),1);
+
+if (!chnl)
+ return 0;//ERR_CHECK
+
+chnl->conn=ci;
+chnl->outgoing=is_outgoing;
+chnl->peer_id=uid;
+
+chnl->sck_data = -1;//prevent closing stdin when closing not active connection
+
+chnl->read_buf = calloc(PICA_CHANREADBUFSIZE, 1);
+
+if (ci->chan_list_head)
+ {
+ ci->chan_list_end->next=chnl;
+ chnl->prev=ci->chan_list_end;
+ ci->chan_list_end=chnl;
+ }
+else
+ {
+ ci->chan_list_head=chnl;
+ ci->chan_list_end=chnl;
+ }
+
+return 1;
+}
+
+static int establish_data_connection(struct PICA_chaninfo *chnl)
+{
+char* DN_str;
+//unsigned char buf[12];
+int ret,err_ret;
+unsigned int tmp_uid;
+struct PICA_proto_msg *mp;
+
+chnl->sck_data=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+{
+struct sockaddr_in addr=chnl->conn->srv_addr;
+
+addr.sin_port = htons(ntohs(addr.sin_port));
+
+ret=connect( chnl->sck_data, (struct sockaddr*)&addr, sizeof(struct sockaddr_in));
+
+if (ret==SOCKET_ERROR)
+ {
+ err_ret=PICA_ERRSOCK;
+ goto error_ret;
+ }
+}
+
+mp = c2c_writebuf_push( chnl, PICA_PROTO_CONNID, PICA_PROTO_CONNID_SIZE);
+if (mp)
+ {
+ if (chnl->outgoing)
+ {
+ *(unsigned int*)(mp->tail) = chnl->conn->id;//вызвывающий
+ *(unsigned int*)(mp->tail + 4) = chnl->peer_id;//вызываемый
+ }
+ else
+ {
+ puts("callee CONNID");//debug
+ *(unsigned int*)(mp->tail) = chnl->peer_id;//вызвывающий
+ *(unsigned int*)(mp->tail + 4) = chnl->conn->id;//вызываемый
+ }
+ }
+else
+ {
+ err_ret=PICA_ERRNOMEM;
+ goto error_ret;
+ }
+//ret=send(chnl->sck_data,buf,PICA_PROTO_CONNID_SIZE,0);
+do
+ {
+ err_ret = PICA_write_c2c(chnl);
+ if (PICA_OK != err_ret)
+ goto error_ret;
+ }
+while( chnl->write_pos > 0);
+
+chnl->ssl=SSL_new(chnl->conn->ctx);
+
+if (!chnl->ssl)
+ {
+ puts("SSL_new !- 0");//debug
+ err_ret=PICA_ERRSSL;
+ goto error_ret;
+ }
+
+ret=SSL_set_fd(chnl->ssl, chnl->sck_data);
+
+if (!ret)
+ {
+ puts("SSL_set_fd returned 0");//debug
+ err_ret=PICA_ERRSSL;
+ goto error_ret_;
+ }
+
+SSL_set_verify(chnl->ssl, SSL_VERIFY_PEER,0);
+
+if (chnl->outgoing)
+ret=SSL_connect(chnl->ssl);
+else
+ret=SSL_accept(chnl->ssl);
+
+printf("verify result:%i\n",(int)SSL_get_verify_result(chnl->ssl));//debug
+
+if (ret!=1)
+ {
+ printf("ret=%i of SSL_connect or accept\n",ret);//debug
+ printf("SSL_get_error says:%i\n",SSL_get_error(chnl->ssl,ret));//debug
+ err_ret=PICA_ERRSSL;
+ goto error_ret_;
+ }
+
+//проверить сертификат собеседника
+
+chnl->peer_cert=SSL_get_peer_certificate(chnl->ssl);
+
+if (!chnl->peer_cert)
+ {
+ err_ret=PICA_ERRNOPEERCERT;
+ goto error_ret_;
+ }
+
+DN_str=X509_NAME_oneline(X509_get_subject_name(chnl->peer_cert), 0, 0);
+
+if (!DN_str)
+ {
+ puts("no DN_str");//debug
+ err_ret=PICA_ERRSSL;
+ goto error_ret_;
+ }
+
+if (!get_id_fromsubjstr(DN_str,&tmp_uid) || tmp_uid!=chnl->peer_id)
+ {
+ err_ret=PICA_ERRINVPEERCERT;
+ goto error_ret_;
+ }
+puts(DN_str);//debug
+
+OPENSSL_free(DN_str);
+
+{
+BIO *mem = BIO_new(BIO_s_mem());
+char *cert_pem;
+unsigned int cert_pem_nb;
+
+PEM_write_bio_X509(mem, chnl->peer_cert);
+
+cert_pem_nb = (unsigned int)BIO_get_mem_data(mem, &cert_pem);
+
+if (callbacks.peer_cert_verify_cb(chnl->peer_id, (const char*)cert_pem, cert_pem_nb) == 0)
+ {
+ BIO_free(mem);
+ err_ret=PICA_ERRINVPEERCERT;
+ goto error_ret_;
+ }
+
+BIO_free(mem);
+}
+
+chnl->state=PICA_CHANSTATE_ACTIVE;
+
+return PICA_OK;
+
+error_ret_:
+error_ret:
+callbacks.channel_failed(chnl->peer_id);
+PICA_close_channel(chnl);
+return err_ret;
+}
+
+static unsigned int procmsg_INITRESP(unsigned char* buf, unsigned int nb,void* p)
+{
+struct PICA_conninfo *ci=(struct PICA_conninfo *)p;
+
+switch(buf[0])
+ {
+ case PICA_PROTO_INITRESPOK:
+ ci -> init_resp_ok = 1;
+ break;
+
+ case PICA_PROTO_VERDIFFER:
+ ci->init_resp_ok = -1;
+ ci->node_ver_major = buf[2];
+ ci->node_ver_minor = buf[3];
+ break;
+
+ default:
+ return 0;
+ }
+
+return 1;
+}
+
+static unsigned int procmsg_CONNREQINC(unsigned char* buf,unsigned int nb,void* p)
+{
+int ret;
+struct PICA_conninfo *ci=(struct PICA_conninfo *)p;
+struct PICA_proto_msg *mp;
+
+
+ if (callbacks.accept_cb(*(unsigned int*)(buf+2)))
+ {
+ struct PICA_chaninfo *_chnl;
+
+ mp = c2n_writebuf_push( ci, PICA_PROTO_CONNALLOW, PICA_PROTO_CONNALLOW_SIZE);
+
+ if (mp)
+ {
+ *(unsigned int*)(mp->tail) = *(unsigned int*)(buf+2);
+ }
+ else
+ return 0;
+
+ ret = add_chaninfo( ci, &_chnl, *(unsigned int*)(buf+2), 0);
+
+ if (!ret)
+ return 0; //ERR_CHECK - кончилась память
+
+ do
+ {
+ ret = PICA_write_c2n(ci);
+ if (PICA_OK != ret)
+ return 0;
+ }
+ while( ci->write_pos > 0);
+
+ ret = establish_data_connection(_chnl);
+
+ if (ret!=PICA_OK)
+ return 1;
+
+ callbacks.channel_established_cb(_chnl->peer_id);//*nchn=_chnl;
+
+ return 1;
+ }
+ else
+ {
+ mp = c2n_writebuf_push( ci, PICA_PROTO_CONNDENY, PICA_PROTO_CONNDENY_SIZE);
+
+ if (mp)
+ {
+ *(unsigned int*)(mp->tail) = *(unsigned int*)(buf+2);
+ }
+ else
+ return 0;//ERR_CHECK
+ }
+return 1;
+}
+
+static unsigned int procmsg_NOTFOUND(unsigned char* buf,unsigned int nb,void* p)
+{
+struct PICA_conninfo *ci=(struct PICA_conninfo *)p;
+struct PICA_chaninfo *ipt,*rq=0;
+
+puts("procmsg_NOTFOUND");//debug
+
+ipt=ci->chan_list_end;
+while(ipt)
+ {
+ if (ipt->peer_id==*(unsigned int*)(buf+2))
+ {
+ rq=ipt;
+ break;
+ }
+ ipt=ipt->prev;
+ }
+
+if (!rq)
+ return 0;// ERR_CHECK -левое сообщение, такой запрос не посылался
+
+puts("procmsg_NOTFOUND_chkp0");//debug
+PICA_close_channel(rq);
+
+puts("procmsg_NOTFOUND_chkp1");//debug
+
+callbacks.notfound_cb(*(unsigned int*)(buf+2));
+return 1;
+}
+
+static unsigned int procmsg_FOUND(unsigned char* buf,unsigned int nb,void* p)
+{
+struct PICA_conninfo *ci=(struct PICA_conninfo *)p;
+struct PICA_chaninfo *ipt,*rq=0;
+int ret;
+
+ipt=ci->chan_list_end;
+while(ipt)
+ {
+ if (ipt->peer_id==*(unsigned int*)(buf+2))
+ {
+ rq=ipt;
+ break;
+ }
+ ipt=ipt->prev;
+ }
+
+if (!rq)
+ return 0;// ERR_CHECK -левое сообщение, такой запрос не посылался
+
+ret=establish_data_connection(rq);
+
+if (ret!=PICA_OK)
+ return 0;
+
+callbacks.channel_established_cb(rq->peer_id);//*nchn=_chnl;
+
+return 1;
+}
+
+static unsigned int procmsg_CLNODELIST(unsigned char* buf, unsigned int nb, void* p)
+{
+unsigned int pos = 4;
+char ipaddr_string[INET6_ADDRSTRLEN];
+char temp_zeroswap;
+unsigned int port;
+
+puts("CLNODELIST");//debug
+
+//callbacks.nodelist_cb(buf + 4, nb - 4);
+do
+ {
+ switch(buf[pos])
+ {
+ case PICA_PROTO_NEWNODE_IPV4:
+
+ callbacks.nodelist_cb(
+ PICA_PROTO_NEWNODE_IPV4,
+ buf + pos +1,
+ inet_ntop(AF_INET, buf + pos +1, ipaddr_string, INET6_ADDRSTRLEN),
+ ntohs(*(uint16_t*)(buf + pos + 5))
+ );
+
+ pos +=7;
+ break;
+
+ case PICA_PROTO_NEWNODE_IPV6:
+
+ callbacks.nodelist_cb(
+ PICA_PROTO_NEWNODE_IPV6,
+ buf + pos +1,
+ inet_ntop(AF_INET6, buf + pos +1, ipaddr_string, INET6_ADDRSTRLEN),
+ ntohs(*(uint16_t*)(buf + pos + 17))
+ );
+
+ pos += 19;
+ break;
+
+ case PICA_PROTO_NEWNODE_DNS:
+
+ port = ntohs(*(uint16_t*)(buf + pos + buf[pos+1] + 2));
+
+ temp_zeroswap = buf[pos + 2 + buf[pos+1]]; //save byte after hostname string
+ buf[pos + 2 + buf[pos+1]] = '\0';// and replace it with string terminating zero
+
+ callbacks.nodelist_cb(
+ PICA_PROTO_NEWNODE_DNS,
+ buf + pos +2,
+ buf + pos +2,
+ port
+ );
+
+ buf[pos + 2 + buf[pos+1]] = temp_zeroswap;
+
+ pos += 4 + buf[pos+1];
+ break;
+
+ default:
+ puts("unknown node address type");//debug
+ return 0;
+ }
+ }
+while(pos < nb);
+
+return 1;
+}
+
+static unsigned int procmsg_MSGUTF8(unsigned char* buf, unsigned int nb, void* p)
+{
+struct PICA_chaninfo *chan = (struct PICA_chaninfo *)p;
+struct PICA_proto_msg *mp;
+
+callbacks.newmsg_cb(chan->peer_id, buf + 4, nb - 4, buf[0]);
+
+//send PICA_PROTO_MSGOK
+mp = c2c_writebuf_push( chan, PICA_PROTO_MSGOK, PICA_PROTO_MSGOK_SIZE);
+
+if (mp)
+ {
+ RAND_bytes( mp->tail, 2);
+ }
+else
+ return 0;
+
+return 1;
+}
+
+static unsigned int procmsg_MSGOK(unsigned char* buf, unsigned int nb, void* p)
+{
+struct PICA_chaninfo *chan = (struct PICA_chaninfo *)p;
+
+callbacks.msgok_cb(chan -> peer_id);
+return 1;
+}
+
+
+#ifdef PICA_MULTITHREADED
+
+#ifdef WIN32
+void locking_cb(int mode, int type, char *file, int line)
+{
+ if (mode & CRYPTO_LOCK)
+ WaitForSingleObject(mt_locks[type],INFINITE);
+ else
+ ReleaseMutex(mt_locks[type]);
+}
+#else
+unsigned long thread_id_cb(void)
+{
+ return (unsigned long)pthread_self();
+}
+
+void locking_cb(int mode, int type, char *file, int line)
+{
+ if (mode & CRYPTO_LOCK)
+ pthread_mutex_lock(&mt_locks[type]);
+ else
+ pthread_mutex_unlock(&mt_locks[type]);
+}
+#endif
+
+#endif
+
+
+int PICA_client_init(struct PICA_client_callbacks *clcbs)
+{
+struct sockaddr_in sd;
+
+if (!clcbs)
+ return 0;
+
+callbacks=*clcbs;
+
+SSL_load_error_strings();
+SSL_library_init();
+
+#ifdef WIN32
+WSADATA wsd;
+WSAStartup(MAKEWORD(2,2),&wsd);
+#endif
+
+#ifdef NO_RAND_DEV
+PICA_rand_seed();
+#endif
+
+#ifdef PICA_MULTITHREADED
+{
+ int i;
+#ifdef WIN32
+mt_locks = malloc(CRYPTO_num_locks()*sizeof(HANDLE));
+
+for (i=0; i<CRYPTO_num_locks(); i++)
+ mt_locks[i] = CreateMutex(NULL,FALSE,NULL);
+
+CRYPTO_set_locking_callback(locking_cb);
+#else
+mt_locks = malloc(CRYPTO_num_locks()*sizeof(pthread_mutex_t));
+
+memset(mt_locks,0,CRYPTO_num_locks()*sizeof(pthread_mutex_t));
+
+for (i=0; i<CRYPTO_num_locks(); i++)
+ pthread_mutex_init(&mt_locks[i],NULL);
+
+CRYPTO_set_id_callback(thread_id_cb);
+CRYPTO_set_locking_callback(locking_cb);
+#endif
+}
+#endif
+
+//ctx=SSL_CTX_new(TLSv1_method());
+//if (!ctx)
+//;//ERR_CHECK
+
+return 1;
+}
+
+/*адрес сервера, сертификат клиента и закрытый ключ*/
+
+//Осуществляет подключение к серверу по указанному адресу.
+//Аргументы:
+//nodeaddr - адрес узла
+//port - номер порта, в нативном для хост-машины порядке байт
+//CA_file - имя файла, содержащего корневой сертификат в формате PEM
+//cert_file - имя файла, содержащего сертфикат клиента в формате PEM
+//pkey_file - имя файла, содержащего приватный ключ в формате PEM
+//password_cb - callback-функция, запрашивающая пароль к приватному ключу, см. man 3 SSL_CTX_set_default_passwd_cb;в userdata передается указатель на id
+//ci -указатель на указатель на структуру PICA_conninfo, который будет проинициализирован при успешном выполнении функции
+//Память, выделенная под структуру, освобождается при вызове PICA_close_connection
+//Возвращаемое значение - код завершения
+//PICA_OK - успешное завершение функции
+//....
+
+int PICA_new_connection(const char *nodeaddr,
+ unsigned int port,
+ const char *CA_file,
+ const char *cert_file,
+ const char *pkey_file,
+ int (*password_cb)(char *buf, int size, int rwflag, void *userdata),
+ struct PICA_conninfo **ci)
+{
+int ret,ret_err;
+struct PICA_conninfo *cid;
+struct sockaddr_in a;
+
+if (!(nodeaddr && CA_file && cert_file && pkey_file && ci))
+ return PICA_ERRINVARG;
+
+
+cid=*ci=(struct PICA_conninfo*)calloc(sizeof(struct PICA_conninfo),1);//(1)
+if (!cid)
+ return PICA_ERRNOMEM;
+
+cid->read_buf = calloc(PICA_CONNREADBUFSIZE, 1);//(2)
+if (!cid->read_buf)
+ {
+ ret_err = PICA_ERRNOMEM;
+ goto error_ret_1;
+ }
+
+cid->ctx=SSL_CTX_new(TLSv1_method());//(3)
+
+if (!cid->ctx)
+ {
+ ret_err=PICA_ERRSSL;
+ goto error_ret_2;
+ }
+
+if (!PICA_get_id_from_cert(cert_file, &cid->id))
+ {
+ ret_err=PICA_ERRINVCERT;
+ goto error_ret_3;
+ }
+////<<
+if (password_cb)
+ {
+ SSL_CTX_set_default_passwd_cb(cid->ctx, password_cb);
+ SSL_CTX_set_default_passwd_cb_userdata(cid->ctx,&cid->id);
+ }
+
+ret=SSL_CTX_use_certificate_file(cid->ctx,cert_file,SSL_FILETYPE_PEM);
+if (ret!=1)
+ {//ERR_CHECK
+ ret_err=PICA_ERRSSL;
+ goto error_ret_3;
+ }
+ret=SSL_CTX_use_PrivateKey_file(cid->ctx,pkey_file,SSL_FILETYPE_PEM);
+if (ret!=1)
+ {//ERR_CHECK
+ ret_err=PICA_ERRSSL;
+ goto error_ret_3;
+ }
+////<<
+
+ret=SSL_CTX_load_verify_locations(cid->ctx,CA_file,0/*"trustedCA/"*/);
+printf("loadverifylocations ret=%i\n",ret);//debug
+SSL_CTX_set_client_CA_list(cid->ctx,SSL_load_client_CA_file(CA_file));
+
+
+cid->ssl_comm=SSL_new(cid->ctx);//(4)
+
+if (!cid->ssl_comm)
+ {
+ ret_err=PICA_ERRSSL;
+ goto error_ret_3;
+ }
+
+
+//resolving name
+{
+ struct addrinfo h,*r;
+
+#ifdef AI_IDN
+#define H_AI_FLAGS (AI_ADDRCONFIG | AI_IDN)
+#else
+#define H_AI_FLAGS AI_ADDRCONFIG
+#endif
+
+ memset(&h,0,sizeof(struct addrinfo));
+
+ h.ai_flags=H_AI_FLAGS;
+ h.ai_family=AF_INET;//CONF
+ h.ai_socktype=SOCK_STREAM;
+ h.ai_protocol=IPPROTO_TCP;
+
+ if (0!=getaddrinfo(nodeaddr,NULL,&h,&r))
+ {
+ ret_err=PICA_ERRDNSRESOLVE;
+ goto error_ret_4;
+ }
+
+ memset(&a,0,sizeof(a));
+ memcpy(&a,r->ai_addr,r->ai_addrlen);
+ a.sin_family = AF_INET;
+ //a.sin_addr.s_addr = inet_addr (nodeaddr);
+ a.sin_port = htons(port);
+
+ freeaddrinfo(r);
+}
+
+cid->srv_addr=a;
+
+cid->sck_comm=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//(5)
+if (cid->sck_comm==SOCKET_ERROR)
+ {
+ ret_err=PICA_ERRSOCK;
+ goto error_ret_4;
+ }
+
+cid->state = PICA_CONNSTATE_CONNECTING;
+
+ret=connect(cid->sck_comm,(struct sockaddr*)&a,sizeof(struct sockaddr_in));
+
+
+if (ret==SOCKET_ERROR)
+ {
+ ret_err=PICA_ERRSOCK;
+ goto error_ret_5;
+ }
+
+
+{
+struct PICA_proto_msg *mp;
+
+mp = c2n_writebuf_push( cid, PICA_PROTO_INITREQ, PICA_PROTO_INITREQ_SIZE);
+
+if (mp)
+ {
+ mp->tail[0] = PICA_PROTO_VER_HIGH;
+ mp->tail[1] = PICA_PROTO_VER_LOW;
+ }
+else
+ {
+ ret_err = PICA_ERRNOMEM;
+ goto error_ret_5;
+ }
+}
+
+do
+ {
+ ret = PICA_write_c2n(cid);
+ if (ret != PICA_OK)
+ {
+ ret_err = ret;
+ break;
+ }
+ ret = PICA_read_c2n(cid);
+ if (ret != PICA_OK)
+ {
+ ret_err = ret;
+ break;
+ }
+ //TODO Timeout
+ }
+while (cid->init_resp_ok == 0);
+
+if (cid->init_resp_ok != 1)
+ {
+ if (cid->init_resp_ok == -1)
+ ret_err = PICA_ERRVERDIFFER;
+
+ goto error_ret_5;
+ }
+
+printf("6\n");//debug
+
+ret=SSL_set_fd(cid->ssl_comm,cid->sck_comm);
+
+printf("SSL_set_fd=%i\n",ret);
+
+ret=SSL_accept(cid->ssl_comm);
+
+if (ret!=1)
+ {
+ printf("SSL_accept ret=%i\n SSL_get_error=%i\n",ret,SSL_get_error(cid->ssl_comm,ret));//debug
+ //ERR_CHECK
+ ret_err=PICA_ERRSSL;
+ goto error_ret_5;
+ }
+cid->state = PICA_CONNSTATE_CONNECTED;//<<<!!!
+
+{
+struct PICA_proto_msg *mp;
+
+mp = c2n_writebuf_push( cid, PICA_PROTO_CLNODELISTREQ, PICA_PROTO_CLNODELISTREQ_SIZE);
+
+if (mp)
+ {
+ RAND_bytes(mp->tail, 2);
+ }
+}
+
+return PICA_OK;
+
+
+error_ret_5: //(5)
+ SHUTDOWN(cid->sck_comm);
+ CLOSE(cid->sck_comm);
+
+error_ret_4: //(4)
+ SSL_free(cid->ssl_comm);
+
+error_ret_3: //(3)
+ SSL_CTX_free(cid->ctx);
+
+error_ret_2: //(2)
+ free(cid->read_buf);
+
+error_ret_1: //(1)
+ free(cid);
+ *ci=0;
+
+return ret_err;
+}
+
+
+
+//создает ИСХОДЯЩИЙ логический зашифрованный канал связи с указанным собеседником, если тот доступен
+// в данный момент.
+
+int PICA_create_channel(struct PICA_conninfo *ci,unsigned int uid,struct PICA_chaninfo **chn)
+{
+struct PICA_proto_msg *mp;
+struct PICA_chaninfo *chnl;
+char* DN_str;
+unsigned char buf[12];
+int ret,err_ret;
+
+if (!(ci && chn))
+ return PICA_ERRINVARG;
+
+
+if (!add_chaninfo(ci,chn,uid,1))
+ return PICA_ERRNOMEM;
+
+chnl=*chn;
+
+mp = c2n_writebuf_push( ci, PICA_PROTO_CONNREQOUTG, PICA_PROTO_CONNREQOUTG_SIZE);
+
+ if (mp)
+ {
+ *(unsigned int*)(mp->tail)=uid;
+ }
+ else
+ return PICA_ERRNOMEM;
+
+
+return PICA_OK;
+
+//сделать нормальную обработку ошибок - со стековой последовательностью деинициализирующих процедур (!!??)
+// в этой функции и в предыдущей
+
+}
+
+int PICA_read(struct PICA_conninfo *ci,int timeout)
+{
+fd_set fds;
+struct timeval tv={0,1000};
+struct PICA_chaninfo *ipt, *kill_ptr = 0;
+int ret,nfds;
+
+//puts("PICA_read<<");//debug
+
+tv.tv_usec=timeout;
+
+FD_ZERO(&fds);
+
+nfds=ci->sck_comm;
+FD_SET(ci->sck_comm,&fds);
+
+ipt=ci->chan_list_head;
+
+while(ipt)
+ {
+ if (ipt->state==PICA_CHANSTATE_ACTIVE)
+ {
+ FD_SET(ipt->sck_data,&fds);
+
+ if (ipt->sck_data>nfds)
+ nfds=ipt->sck_data;
+ }
+
+ ipt=ipt->next;
+ }
+
+#warning "make sockets non-blocking!!!!"
+
+ret=select(nfds+1,&fds,0,0,&tv);
+
+if (ret>0)
+ {
+
+ if (FD_ISSET(ci->sck_comm,&fds))
+ {
+ ret=PICA_read_c2n(ci);
+ if (ret!=PICA_OK)
+ return ret;
+ }
+
+ ipt=ci->chan_list_head;
+ while(ipt)
+ {
+ if (FD_ISSET(ipt->sck_data, &fds))
+ {
+ if (PICA_OK != (ret = PICA_read_c2c(ipt)) )
+ {
+ callbacks.channel_closed_cb(ipt->peer_id, ret);
+ kill_ptr = ipt;
+ }
+
+ }
+
+ ipt=ipt->next;
+
+ if (kill_ptr)
+ {
+ PICA_close_channel(kill_ptr);
+ kill_ptr = 0;
+ }
+ }
+
+ }
+//puts("PICA_read>>");//debug
+
+return PICA_OK;
+}
+
+int PICA_write(struct PICA_conninfo *ci)
+{
+struct PICA_chaninfo *ipt, *kill_ptr = 0;
+int ret = PICA_OK;
+
+//puts("PICA_write>>");//debug
+
+if (ci->write_pos)
+ ret = PICA_write_c2n(ci);
+
+if (PICA_OK != ret)
+ return ret;
+
+ipt=ci->chan_list_head;
+
+while(ipt)
+ {
+ if (ipt->write_pos)
+ {
+ if (PICA_OK != (ret = PICA_write_c2c(ipt)))
+ {
+ callbacks.channel_closed_cb(ipt->peer_id, ret);
+ kill_ptr = ipt;
+ }
+ }
+ ipt = ipt->next;
+
+ if (kill_ptr)
+ {
+ PICA_close_channel(kill_ptr);
+ kill_ptr = 0;
+ }
+ }
+
+//puts("PICA_write<<");//debug
+return PICA_OK;
+}
+
+int PICA_write_socket(SOCKET s, unsigned char *buf, unsigned int *ppos)
+{
+int ret;
+
+if (*ppos > 0 )
+ {
+ ret = send( s, buf, *ppos, MSG_NOSIGNAL);
+
+ if (ret > 0)
+ {
+ if (ret < *ppos)
+ memmove( buf, buf + ret, *ppos - ret);
+ *ppos -= ret;
+ }
+
+ if (ret < 0)
+ {
+#ifdef WIN32
+ ret = WSAGetLastError();
+ if (! (ret == WSAEWOULDBLOCK || ret == WSAENOBUFS))
+#else
+ ret = errno;
+ if (! (ret == EAGAIN || ret == ENOBUFS || ret == EINTR))
+#endif
+ return PICA_ERRSOCK;
+ }
+ if (ret == 0)
+ return PICA_ERRSERV;
+
+ }
+return PICA_OK;
+}
+
+int PICA_write_ssl(SSL *ssl, unsigned char *buf, unsigned int *ppos)
+{
+int ret;
+
+if (*ppos > 0 )
+ {
+ ret = SSL_write( ssl, buf, *ppos);
+
+ if (ret > 0)
+ {
+ if (ret < *ppos)
+ memmove( buf, buf + ret, *ppos - ret);
+ *ppos -= ret;
+ }
+
+ if (ret < 0)
+ {
+ ret = SSL_get_error( ssl, ret);
+ if (! (ret == SSL_ERROR_WANT_WRITE || ret == SSL_ERROR_WANT_READ))
+ return PICA_ERRSSL;
+ }
+ if (ret == 0)
+ return PICA_ERRSERV;
+
+ }
+return PICA_OK;
+}
+
+//int PICA_read_socket()
+//int PICA_read_ssl()
+
+int PICA_write_c2n(struct PICA_conninfo *ci)
+{
+int ret = PICA_ERRINVARG;
+
+if (PICA_CONNSTATE_CONNECTING == ci->state)
+ ret = PICA_write_socket( ci->sck_comm, ci->write_buf, &ci->write_pos);
+else if (PICA_CONNSTATE_CONNECTED == ci->state)
+ ret = PICA_write_ssl( ci->ssl_comm, ci->write_buf, &ci->write_pos);
+
+return ret;
+}
+
+int PICA_write_c2c(struct PICA_chaninfo *chn)
+{
+int ret = PICA_ERRINVARG;
+
+if (PICA_CHANSTATE_ACTIVE != chn->state)
+ ret = PICA_write_socket( chn->sck_data, chn->write_buf, &chn->write_pos);
+else
+ ret = PICA_write_ssl( chn->ssl, chn->write_buf, &chn->write_pos);
+
+return ret;
+}
+
+int PICA_read_socket(SOCKET s, unsigned char *buf, unsigned int *ppos, unsigned int size)
+{
+int ret;
+
+ret = recv(s, buf + *ppos, size - *ppos, 0);
+
+if (ret == 0)
+ return PICA_ERRDISCONNECT;
+
+if (ret < 0)
+ {
+#ifdef WIN32
+ switch(WSAGetLastError())
+ {
+ case WSAEINTR:
+ case WSAEINPROGRESS:
+ case WSAEWOULDBLOCK:
+ break;
+ default:
+ return PICA_ERRSOCK;
+ }
+#else
+ switch(errno)
+ {
+ case EAGAIN :
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ case EINTR:
+ break;
+
+ default:
+ return PICA_ERRSOCK;//ERR_CHECK
+ }
+#endif
+ }
+
+if (ret > 0)
+ {
+ *ppos += ret;
+ }
+
+return PICA_OK;
+}
+
+int PICA_read_ssl(SSL *ssl, unsigned char *buf, unsigned int *ppos, unsigned int size)
+{
+int ret;
+
+ret = SSL_read(ssl, buf + *ppos, size - *ppos);
+
+if (!ret)
+ return PICA_ERRSSL;
+
+if (ret < 0)
+ switch(SSL_get_error(ssl, ret))
+ {
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_READ:
+ break;
+ default:
+ return PICA_ERRSSL;
+ };
+
+if (ret > 0)
+ {
+ *ppos += ret;
+ }
+
+return PICA_OK;
+}
+
+int PICA_read_c2c(struct PICA_chaninfo *chn)
+{
+int ret;
+
+if (chn->state != PICA_CHANSTATE_ACTIVE)
+ return PICA_OK;// ???
+
+//ret = SSL_read(chn->ssl , chn->read_buf + chn->read_pos, PICA_CHANREADBUFSIZE - chn->read_pos);
+ret = PICA_read_ssl(chn->ssl, chn->read_buf, &chn->read_pos, PICA_CHANREADBUFSIZE);
+
+if (ret == PICA_OK)
+ {
+ if(!PICA_processdatastream(chn->read_buf, &(chn->read_pos ), chn, c2c_messages, MSGINFO_MSGSNUM(c2c_messages)))
+ return PICA_ERRSERV;
+ }
+
+return ret;
+}
+
+//функция читает и обрабатывает данные, приходящие от сервера по управляющему соединению.
+int PICA_read_c2n(struct PICA_conninfo *ci)
+{
+int ret;
+
+//ret=SSL_read(ci->ssl_comm, ci->read_buf + ci->read_pos, PICA_CONNREADBUFSIZE - ci->read_pos);
+if (PICA_CONNSTATE_CONNECTING == ci->state)
+ ret = PICA_read_socket(ci->sck_comm, ci->read_buf, &ci->read_pos, PICA_CONNREADBUFSIZE);
+else if (PICA_CONNSTATE_CONNECTED == ci->state)
+ ret = PICA_read_ssl(ci->ssl_comm, ci->read_buf, &ci->read_pos, PICA_CONNREADBUFSIZE);
+
+
+
+if (ret == PICA_OK)
+ {
+ if (PICA_CONNSTATE_CONNECTING == ci->state)
+ if(!PICA_processdatastream( ci->read_buf, &(ci->read_pos), ci, c2n_init_messages, MSGINFO_MSGSNUM(c2n_init_messages)))
+ return PICA_ERRSERV;
+
+ if (PICA_CONNSTATE_CONNECTED == ci->state)
+ if(!PICA_processdatastream( ci->read_buf, &(ci->read_pos), ci, c2n_messages, MSGINFO_MSGSNUM(c2n_messages)))
+ return PICA_ERRSERV;
+ }
+
+return ret;
+}
+
+struct PICA_proto_msg* c2n_writebuf_push(struct PICA_conninfo *ci, unsigned int msgid, unsigned int size)
+{
+struct PICA_proto_msg *mp;
+
+if (! ci->write_buf)
+ {
+ ci->write_buf = calloc(PICA_CONNWRITEBUFSIZE, 1);
+ if (!ci->write_buf)
+ return 0;
+ ci->write_buflen = PICA_CONNWRITEBUFSIZE;
+ }
+
+if (ci->write_buflen - ci->write_pos < size)
+ {
+ unsigned char*p;
+ p = realloc(ci->write_buf, size + ci->write_pos);
+
+ if (p)
+ {
+ ci->write_buf = p;
+ ci->write_buflen = size + ci->write_pos;
+ }
+ else
+ return 0;
+ }
+
+mp = (struct PICA_proto_msg *)(ci->write_buf + ci->write_pos);
+mp->head[0] = mp->head[1] = msgid;
+ci->write_pos += size;
+
+return mp;
+}
+
+struct PICA_proto_msg* c2c_writebuf_push(struct PICA_chaninfo *chn, unsigned int msgid, unsigned int size)
+{
+struct PICA_proto_msg *mp;
+
+if (! chn->write_buf)
+ {
+ chn->write_buf = calloc(PICA_CHANWRITEBUFSIZE, 1);
+ if (!chn->write_buf)
+ return 0;
+ chn->write_buflen = PICA_CHANWRITEBUFSIZE;
+ }
+
+if (chn->write_buflen - chn->write_pos < size)
+ {
+ unsigned char*p;
+ p = realloc(chn->write_buf, size + chn->write_pos);
+
+ if (p)
+ {
+ chn->write_buf = p;
+ chn->write_buflen = size + chn->write_pos;
+ }
+ else
+ return 0;
+ }
+
+mp = (struct PICA_proto_msg *)(chn->write_buf + chn->write_pos);
+mp->head[0] = mp->head[1] = msgid;
+chn->write_pos += size;
+
+return mp;
+}
+
+int PICA_send_msg(struct PICA_chaninfo *chn, char *buf,unsigned int len)
+{
+struct PICA_proto_msg *mp;
+
+if (len > PICA_PROTO_C2CMSG_MAXDATASIZE)
+ return PICA_ERRMSGSIZE;
+
+if ((mp = c2c_writebuf_push(chn, PICA_PROTO_MSGUTF8, len + 4)))
+ {
+ *((uint16_t*)mp->tail) = len;
+ memcpy(mp->tail + 2, buf, len);
+ }
+else
+ return PICA_ERRNOMEM;
+
+return PICA_OK;
+}
+
+void PICA_close_channel(struct PICA_chaninfo *chn)
+{
+struct PICA_chaninfo *ipt;
+
+puts("PICA_close_channel");//debug
+
+ipt=chn->conn->chan_list_head;
+puts("PICA_close_channel_chkp0");//debug
+while(ipt)
+ {
+ if (ipt==chn)
+ {
+ if (ipt->next)
+ ipt->next->prev=ipt->prev;
+ else
+ chn->conn->chan_list_end=ipt->prev;
+
+ if (ipt->prev)
+ ipt->prev->next=ipt->next;
+ else
+ chn->conn->chan_list_head=ipt->next;
+
+ break;
+ }
+ ipt=ipt->next;
+ }
+
+puts("PICA_close_channel_chkp1");//debug
+
+if (chn -> read_buf)
+ free(chn -> read_buf);
+
+if (chn -> write_buf)
+ free(chn -> write_buf);
+
+if (chn->peer_cert)
+ X509_free(chn->peer_cert);
+
+puts("PICA_close_channel_chkp2");//debug
+
+if (chn->ssl)
+ SSL_shutdown(chn->ssl);
+ puts("PICA_close_channel_chkp3");//debug
+
+if (chn->ssl)
+ {
+ printf("3 SSL_free(%X)\n",chn->ssl);//debug
+ SSL_free(chn->ssl);
+ }
+
+puts("PICA_close_channel_chkp4");//debug
+SHUTDOWN(chn->sck_data);
+puts("PICA_close_channel_chkp5");//debug
+CLOSE(chn->sck_data);
+puts("PICA_close_channel_chkp6");//debug
+free(chn);
+puts("PICA_close_channel_chkp7");//debug
+}
+
+void PICA_close_connection(struct PICA_conninfo *cid)
+{
+struct PICA_chaninfo *ipt;
+
+while((ipt = cid->chan_list_head))
+ PICA_close_channel(ipt);
+
+SSL_shutdown(cid->ssl_comm);
+printf("4 SSL_free(%X)\n",cid->ssl_comm);//debug
+SSL_free(cid->ssl_comm);
+SHUTDOWN(cid->sck_comm);
+CLOSE(cid->sck_comm);
+SSL_CTX_free(cid->ctx);
+free(cid->read_buf);
+
+if (cid->write_buf)
+ free(cid->write_buf);
+
+free(cid);
+}
diff --git a/PICA_client.h b/PICA_client.h
new file mode 100755
index 0000000..94b4004
--- /dev/null
+++ b/PICA_client.h
@@ -0,0 +1,186 @@
+#ifndef PICA_CLIENT_H
+#define PICA_CLIENT_H
+
+#include <openssl/crypto.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/bio.h>
+
+
+#ifdef PICA_MULTITHREADED
+
+#define OPENSSL_THREAD_DEFINES
+#include <openssl/opensslconf.h>
+#if defined(OPENSSL_THREADS)
+ // thread support enabled
+#else
+#error "THREAD SUPPORT IS NOT ENABLED IN OPENSSL!"
+#endif
+
+#endif
+
+
+#ifdef WIN32
+
+#include <windows.h>
+#include <winsock2.h>
+
+#else
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#ifdef PICA_MULTITHREADED
+#include <pthread.h>
+#endif
+
+#define SOCKET int
+#define SOCKET_ERROR -1
+
+#endif
+
+//#define PICA_MSGOK 3
+//#define PICA_MSGUTF8 2
+#define PICA_NEWCHAN 1
+#define PICA_OK 0
+#define PICA_ERRINVARG -1
+#define PICA_ERRNOMEM -2
+#define PICA_ERRSSL -3
+#define PICA_ERRSOCK -4
+#define PICA_ERRSERV -5
+#define PICA_ERRVERDIFFER -6
+#define PICA_ERRNOTFOUND -7
+#define PICA_ERRNOPEERCERT -8
+#define PICA_ERRINVPEERCERT -9
+#define PICA_ERRDISCONNECT -10
+#define PICA_ERRINVCERT -11
+#define PICA_ERRMSGSIZE -12
+#define PICA_ERRDNSRESOLVE -13
+
+//#define PICA_CHNMSGBUFLEN 104
+
+#define PICA_CHANREADBUFSIZE 65536
+#define PICA_CHANWRITEBUFSIZE 4096
+#define PICA_CONNREADBUFSIZE 65536
+#define PICA_CONNWRITEBUFSIZE 4096
+
+#define PICA_CHANSTATE_ACTIVE 13
+#define PICA_CONNSTATE_CONNECTING 17
+#define PICA_CONNSTATE_CONNECTED 19
+
+struct PICA_conninfo;
+struct PICA_chaninfo;
+
+struct PICA_conninfo
+{
+#warning "sockaddr!"
+struct sockaddr_in srv_addr; // sockaddr !!
+unsigned int id;
+SSL_CTX* ctx;
+SOCKET sck_comm;
+SSL* ssl_comm;
+//сертификат клиента
+// unsigned char r_buf[128];
+// unsigned int r_pos;
+
+int state;
+
+unsigned char *read_buf;
+unsigned char *write_buf;
+unsigned int read_pos;
+unsigned int write_pos;
+unsigned int write_buflen;
+
+struct PICA_chaninfo *chan_list_head;
+struct PICA_chaninfo *chan_list_end;
+
+int init_resp_ok;
+unsigned char node_ver_major, node_ver_minor;
+};
+
+struct PICA_chaninfo
+{
+struct PICA_conninfo *conn;//соединение с сервером, через которое установлен данный логический канал связи
+unsigned int peer_id;//номер собеседника
+SOCKET sck_data;
+SSL *ssl;
+int outgoing;//1 если создание канала инициировано локальным клиентом, 0 - если собеседником
+X509 *peer_cert;
+//unsigned char msgbuf[PICA_CHNMSGBUFLEN];
+
+unsigned char *read_buf;
+unsigned char *write_buf;
+unsigned int read_pos;
+unsigned int write_pos;
+unsigned int write_buflen;
+
+struct PICA_chaninfo *next;
+struct PICA_chaninfo *prev;
+int state;
+};
+
+struct PICA_client_callbacks
+{
+//получение сообщения.
+void (*newmsg_cb)(unsigned int peer_id,char* msgbuf,unsigned int nb,int type);
+//получение подтверждения о доставке сообщения
+void (*msgok_cb)(unsigned int peer_id);
+//создание канала с собеседником
+void (*channel_established_cb)(unsigned int peer_id);
+//создать канал не удалось
+void (*channel_failed)(unsigned int peer_id);
+//входящий запрос на создание канала от пользователя с номером caller_id
+//возвращаемое значение: 0 - отклонить запрос, ненулевое значение - принять запрос
+int (*accept_cb)(unsigned int caller_id);
+//запрошенный пользователь не найден, в оффлайне или отказался от общения
+void (*notfound_cb)(unsigned int callee_id);
+//
+void (*channel_closed_cb)(unsigned int peer_id, int reason);
+
+void (*nodelist_cb)(int type, void *addr_bin, const char *addr_str, unsigned int port);
+//сертификат собеседника в формате PEM. Функция должна сравнить предъявленный сертификат с сохранённым (если есть) и вернуть 1 при успешной проверке, 0 - при неуспешной
+int (*peer_cert_verify_cb)(unsigned int peer_id, const char *cert_pem, unsigned int nb);
+};
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int PICA_get_id_from_cert(const char *cert_file, unsigned int *p_id);
+int PICA_client_init(struct PICA_client_callbacks *clcbs);
+
+int PICA_new_connection
+ ( const char *nodeaddr,
+ unsigned int port,
+ const char *CA_file,
+ const char *cert_file,
+ const char *pkey_file,
+ //const char* password,
+ int (*password_cb)(char *buf, int size, int rwflag, void *userdata),
+ struct PICA_conninfo **ci);
+
+int PICA_create_channel(struct PICA_conninfo *ci,unsigned int uid,struct PICA_chaninfo **chn);
+int PICA_read_c2n(struct PICA_conninfo *ci);
+
+int PICA_read(struct PICA_conninfo *ci,int timeout);
+int PICA_write(struct PICA_conninfo *ci);
+
+int PICA_send_msg(struct PICA_chaninfo *chn, char *buf,unsigned int len);
+int PICA_read_msg(struct PICA_chaninfo *chn,char *buf,unsigned int *n);
+
+void PICA_close_channel(struct PICA_chaninfo *chn);
+void PICA_close_connection(struct PICA_conninfo *cid);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
diff --git a/PICA_common.h b/PICA_common.h
new file mode 100644
index 0000000..bc95be1
--- /dev/null
+++ b/PICA_common.h
@@ -0,0 +1,41 @@
+#ifndef PICA_COMMON_H
+#define PICA_COMMON_H
+
+#ifdef WIN32
+#define CLOSE closesocket
+#define SHUTDOWN(s) shutdown(s,SD_BOTH)
+#else
+#define CLOSE close
+#define SHUTDOWN(s) shutdown(s,SHUT_RDWR)
+#endif
+
+#ifdef WIN32
+#define MSG_NOSIGNAL 0
+#endif
+
+
+//call func for each linked list item
+#define LISTMAP(listname,type,func,arg) \
+{struct type *p;\
+p= listname ## _list_head;\
+while(p)\
+{func(p,arg);p=p->next;}\
+}
+
+#define LISTSEARCH(listname,type, item, value,itemptr) \
+{\
+struct type *p;\
+p= listname ## _list_head;\
+while(p)\
+ {\
+ if (p-> item == value)\
+ {\
+ itemptr=p;\
+ break;\
+ }\
+ p=p->next;\
+ }\
+}
+
+
+#endif
diff --git a/PICA_msgproc.c b/PICA_msgproc.c
new file mode 100755
index 0000000..78987c6
--- /dev/null
+++ b/PICA_msgproc.c
@@ -0,0 +1,86 @@
+#include <limits.h>
+#include <string.h>
+#include "PICA_proto.h"
+#include "PICA_msgproc.h"
+
+
+//возвращает размер сообщения либо UINT_MAX,если количество прочитанных байт недостаточно для определения размера
+//либо 0 при ошибочных данных
+unsigned int PICA_msggetsize(unsigned char *buf,unsigned int nb,struct PICA_msginfo *msgs,unsigned int nmsg)
+{
+unsigned int i;
+
+if (nb<2)
+ return UINT_MAX;
+
+if (buf[0]!=buf[1])
+ return 0;
+
+
+for (i=0;i<nmsg;i++)
+ if (buf[0]==(unsigned char)msgs[i].msgid)
+ {
+ if (msgs[i].fixedsz==PICA_MSG_FIXED_SIZE)
+ return msgs[i].sz;
+ else if (msgs[i].fixedsz==PICA_MSG_VAR_SIZE)
+ {
+ if (nb>=(msgs[i].sz+2))
+ {
+ unsigned int tail_len=0;
+ int j;
+#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ for (j=0;j<msgs[i].sz;j++)
+ ((char*)&tail_len)[sizeof(int)-1-j]=buf[2+j];
+#else
+ for (j=0;j<msgs[i].sz;j++)
+ ((char*)&tail_len)[j]=buf[2+j];
+#endif
+ return 2+msgs[i].sz+tail_len;
+ }
+ else
+ return UINT_MAX;
+ }
+ }
+
+return 0;
+}
+
+//Определяет тип сообщения размера size, находящегося в буфере buf, вызывает соответствующий обработчик и передает обработчику аргумент arg. Возвращает значение, которое вернула функция-обработчик сообщения. Если тип сообщения неизвестен, возвращает 0
+unsigned int PICA_processmsg(unsigned char *buf,unsigned int size,void* arg,struct PICA_msginfo *msgs,unsigned int nmsg)
+{
+int i;
+
+if (buf[0]!=buf[1])
+ return 0;
+
+for (i=0;i<nmsg;i++)
+ if (buf[0]==(unsigned char)msgs[i].msgid)
+ return msgs[i].msgprocfn(buf,size,arg);
+
+return 0;
+}
+
+unsigned int PICA_processdatastream(unsigned char *buf,unsigned int *read_pos,void* arg,struct PICA_msginfo *msgs,unsigned int nmsg)
+{
+ while(*read_pos)
+ {
+ unsigned int uret;
+ uret=PICA_msggetsize(buf,*read_pos,msgs,nmsg);
+
+ if (!uret)
+ return 0;
+
+ if (uret > *read_pos)
+ break;
+
+ if (!PICA_processmsg(buf,uret,arg,msgs,nmsg))
+ return 0;
+
+
+ *read_pos-=uret;
+
+ memmove(buf,buf+uret,*read_pos);//сдвиг содержимого буфера к началу
+ }
+
+return 1;
+}
diff --git a/PICA_msgproc.h b/PICA_msgproc.h
new file mode 100755
index 0000000..5bcab20
--- /dev/null
+++ b/PICA_msgproc.h
@@ -0,0 +1,24 @@
+// структуры и функции для обработки сообщений протокола
+//
+#ifndef PICA_MSGPROC_H
+#define PICA_MSGPROC_H
+
+#define PICA_MSG_FIXED_SIZE 0
+#define PICA_MSG_VAR_SIZE 1
+#define PICA_MSG_VARSIZE_INT8 1
+#define PICA_MSG_VARSIZE_INT16 2
+
+struct PICA_msginfo
+{
+unsigned int msgid;
+unsigned int fixedsz;//фиксированный или переменный размер
+unsigned int sz;//фиксированный размер всего пакета либо кол-во байт, определяющих размер хвостовой части пакета
+unsigned int (*msgprocfn)(unsigned char*,unsigned int,void*);//функция-обработчик данного типа сообщений
+};
+
+unsigned int PICA_msggetsize(unsigned char *buf,unsigned int nb,struct PICA_msginfo *msgs,unsigned int nmsg);
+
+unsigned int PICA_processmsg(unsigned char *buf,unsigned int size,void* arg,struct PICA_msginfo *msgs,unsigned int nmsg);
+
+unsigned int PICA_processdatastream(unsigned char *buf,unsigned int *read_pos,void* arg,struct PICA_msginfo *msgs,unsigned int nmsg);
+#endif
diff --git a/PICA_node.c b/PICA_node.c
new file mode 100755
index 0000000..72c30cc
--- /dev/null
+++ b/PICA_node.c
@@ -0,0 +1,2266 @@
+//Программа является узлом сети
+//
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include "PICA_node.h"
+#include "PICA_proto.h"
+#include "PICA_msgproc.h"
+#include "PICA_nodeaddrlist.h"
+#include "PICA_common.h"
+#include "PICA_nodejoinskynet.h"
+
+#ifdef NO_RAND_DEV
+#include "PICA_rand_seed.h"
+#endif
+
+#define MAX_NEWCONNS 64
+
+#define NEWCONN_TIMEOUT 10 //sec
+#define JOINSKYNET_TIMEOUT 15
+#define SELECT_TIMEOUT 1
+
+clock_t TMO_CCLINK_WAITACTIVE=15;//CONF //таймаут ожидания перехода соединения в активное состояние
+
+char *my_addr;//TEMP CONF FIXME собственный ip-адрес узла, на котором он ожидает соединения
+
+SOCKET listen_comm_sck;//*
+
+SSL_CTX* ctx;
+struct newconn newconns[MAX_NEWCONNS];
+unsigned int newconns_pos=0;
+
+
+struct client *client_tree_root;//корень дерева клиентов
+struct client *client_list_head;//указатель на первый элемент списка клиентов или 0, если список пуст
+struct client *client_list_end;//указатель на последний элемент списка клиентов или 0, если список пуст
+
+struct cclink *cclink_list_head;
+struct cclink *cclink_list_end;
+
+unsigned int cclink_list_count;
+
+struct nodelink *nodelink_list_head;
+struct nodelink *nodelink_list_end;
+unsigned int nodelink_list_count;
+
+struct PICA_nodeaddr *nodeaddr_list_head;
+struct PICA_nodeaddr *nodeaddr_list_end;
+unsigned int nodeaddr_list_count;
+
+
+char msg_VERDIFFER[4]={PICA_PROTO_VERDIFFER,PICA_PROTO_VERDIFFER,PICA_PROTO_VER_HIGH,PICA_PROTO_VER_LOW};
+const unsigned int msg_VERDIFFER_len=4;
+
+unsigned int procmsg_INITREQ(unsigned char* buf,unsigned int size,void* ptr);
+unsigned int procmsg_CONNID(unsigned char* buf,unsigned int size,void* ptr);
+unsigned int procmsg_NODECONNREQ(unsigned char* buf,unsigned int size,void* ptr);
+
+unsigned int procmsg_CONNREQOUTG(unsigned char* buf,unsigned int size,void* ptr);
+unsigned int procmsg_CONNALLOW(unsigned char* buf,unsigned int size,void* ptr);
+unsigned int procmsg_CONNDENY(unsigned char* buf,unsigned int size,void* ptr);
+unsigned int procmsg_NODERESP(unsigned char* buf,unsigned int size,void* ptr);
+unsigned int procmsg_NEWNODE(unsigned char* buf,unsigned int size,void* ptr);
+unsigned int procmsg_NODELISTREQ(unsigned char* buf,unsigned int size,void* ptr);
+unsigned int procmsg_NODELIST(unsigned char* buf,unsigned int size,void* ptr);
+unsigned int procmsg_N2NFOUND(unsigned char* buf,unsigned int size,void* ptr);
+unsigned int procmsg_SEARCH(unsigned char* buf,unsigned int size,void* ptr);
+unsigned int procmsg_N2NCONNREQOUTG(unsigned char* buf,unsigned int size,void* ptr);
+unsigned int procmsg_N2NALLOW(unsigned char* buf,unsigned int size,void* ptr);
+unsigned int procmsg_N2NNOTFOUND(unsigned char* buf,unsigned int size,void* ptr);
+unsigned int procmsg_N2NMSG(unsigned char* buf,unsigned int size,void* ptr);
+
+struct cclink* cclink_list_add(struct client *clr,struct client *cle);
+struct cclink* cclink_list_search(unsigned int caller_id, unsigned int callee_id);
+struct cclink* cclink_list_findwaitsearch(unsigned int callee_id);
+void cclink_list_addlocal(struct client *clr,struct client *cle);
+void cclink_list_delete(struct cclink *l);
+void cclink_list_addn2nclr(struct client *clr,unsigned int callee_id);
+void cclink_setwaitconn(struct cclink *ccl);
+void cclink_list_addn2ncle(unsigned int caller_id, struct nodelink *caller_node, struct client *cle);
+void cclink_activate(struct cclink *ccl,SOCKET s);
+void cclink_attach_remotecle_node(struct cclink *ccl, struct nodelink *callee_node);
+
+struct client* client_tree_search(unsigned int id);//
+struct client *client_list_addnew(struct newconn *nc);
+struct PICA_proto_msg* client_wbuf_push(struct client *c,unsigned int msgid,unsigned int size);
+
+struct PICA_proto_msg* nodelink_wbuf_push(struct nodelink *nl,unsigned int msgid,unsigned int size);
+int nodelink_rbuf_grow(struct nodelink *nl);
+int nodelink_attach_nodeaddr(struct nodelink *nl, unsigned int addr_type, void* nodeaddr, unsigned int nodeaddr_size);
+struct nodelink *nodelink_list_addnew(struct newconn *nc);
+void nodelink_list_delete(struct nodelink *n);
+
+void newconn_close(struct newconn* nc);
+void newconn_free(struct newconn* nc);
+struct newconn* newconn_add(struct newconn *ncs,int *pos);
+
+//описание формата сообщений, которые могут приходить _ОТ_ клиентов по управляющему защищенному соединению
+#define MSGINFO_MSGSNUM(arr) (sizeof(arr)/sizeof(struct PICA_msginfo))
+
+struct PICA_msginfo _msginfo_comm[]={
+{PICA_PROTO_CONNREQOUTG, PICA_MSG_FIXED_SIZE, PICA_PROTO_CONNREQOUTG_SIZE, procmsg_CONNREQOUTG},
+{PICA_PROTO_CONNALLOW, PICA_MSG_FIXED_SIZE, PICA_PROTO_CONNALLOW_SIZE, procmsg_CONNALLOW},
+{PICA_PROTO_CONNDENY, PICA_MSG_FIXED_SIZE, PICA_PROTO_CONNDENY_SIZE, procmsg_CONNDENY},
+{PICA_PROTO_CLNODELISTREQ, PICA_MSG_FIXED_SIZE, PICA_PROTO_CLNODELISTREQ_SIZE, procmsg_NODELISTREQ}
+};//---!!! PING!!!
+
+struct PICA_msginfo _msginfo_node[]={
+{PICA_PROTO_INITRESPOK,PICA_MSG_FIXED_SIZE,PICA_PROTO_INITRESPOK_SIZE,procmsg_NODERESP},
+{PICA_PROTO_VERDIFFER ,PICA_MSG_FIXED_SIZE,PICA_PROTO_VERDIFFER_SIZE,procmsg_NODERESP},
+{PICA_PROTO_NEWNODE_IPV4 ,PICA_MSG_FIXED_SIZE,PICA_PROTO_NEWNODE_IPV4_SIZE,procmsg_NEWNODE},
+{PICA_PROTO_NODELISTREQ,PICA_MSG_FIXED_SIZE,PICA_PROTO_NODELISTREQ_SIZE,procmsg_NODELISTREQ},
+{PICA_PROTO_NODELIST,PICA_MSG_VAR_SIZE,PICA_MSG_VARSIZE_INT16,procmsg_NODELIST},
+
+{PICA_PROTO_SEARCH,PICA_MSG_FIXED_SIZE,PICA_PROTO_SEARCH_SIZE,procmsg_SEARCH},
+{PICA_PROTO_N2NFOUND,PICA_MSG_FIXED_SIZE,PICA_PROTO_N2NFOUND_SIZE,procmsg_N2NFOUND},
+{PICA_PROTO_N2NFOUNDCACHE,PICA_MSG_VAR_SIZE,PICA_MSG_VARSIZE_INT16,procmsg_N2NFOUND},
+{PICA_PROTO_N2NCONNREQOUTG,PICA_MSG_FIXED_SIZE,PICA_PROTO_N2NCONNREQOUTG_SIZE,procmsg_N2NCONNREQOUTG},
+{PICA_PROTO_N2NALLOW,PICA_MSG_FIXED_SIZE,PICA_PROTO_N2NALLOW_SIZE,procmsg_N2NALLOW},
+{PICA_PROTO_N2NNOTFOUND,PICA_MSG_FIXED_SIZE,PICA_PROTO_N2NNOTFOUND_SIZE,procmsg_N2NNOTFOUND},
+{PICA_PROTO_N2NMSG,PICA_MSG_VAR_SIZE,PICA_MSG_VARSIZE_INT16,procmsg_N2NMSG}
+};
+
+struct PICA_msginfo _msginfo_newconn[]={
+{PICA_PROTO_INITREQ,PICA_MSG_FIXED_SIZE,PICA_PROTO_INITREQ_SIZE,procmsg_INITREQ},
+{PICA_PROTO_CONNID,PICA_MSG_FIXED_SIZE,PICA_PROTO_CONNID_SIZE,procmsg_CONNID},
+{PICA_PROTO_NODECONNREQ,PICA_MSG_FIXED_SIZE,PICA_PROTO_NODECONNREQ_SIZE,procmsg_NODECONNREQ}
+};
+
+
+
+unsigned int procmsg_INITREQ(unsigned char* buf,unsigned int size,void* ptr)
+{
+struct newconn *nc=(struct newconn*)ptr;
+struct PICA_proto_msg *mp;
+
+if (buf[2]==PICA_PROTO_VER_HIGH && buf[3]==PICA_PROTO_VER_LOW)
+ {
+ struct client *c;
+ c=client_list_addnew(nc);
+ if (!c)
+ return 0;
+
+ newconn_free(nc);
+
+ if (mp=client_wbuf_push(c,PICA_PROTO_INITRESPOK,PICA_PROTO_INITRESPOK_SIZE))
+ {
+ mp->tail[0]='O';
+ mp->tail[1]='K';
+ }
+
+ c->state=PICA_CLSTATE_SENDINGRESP;
+ printf("got PICA_PROTO_INITREQ from client\n");//debug
+ }///////---------<<<<<<<<<<< new client
+else
+ {
+ //// PICA_PROTO_VERDIFFER
+ send(nc->sck,msg_VERDIFFER,msg_VERDIFFER_len,0);
+ return 0;
+ }
+
+return 1;
+}
+
+unsigned int procmsg_CONNID(unsigned char* buf,unsigned int size,void* ptr)
+{
+struct PICA_proto_msg *mp;
+struct newconn *nc=(struct newconn*)ptr;
+unsigned int caller_id,callee_id;
+struct cclink *link;
+
+puts("received CONNID");//debug
+
+caller_id=*((unsigned int*)(buf+2));
+callee_id=*((unsigned int*)(buf+6));
+
+printf("CONNID: caller_id=%u callee_id=%u\n",caller_id,callee_id);//debug
+
+link=cclink_list_search(caller_id,callee_id);
+
+if (!link)
+ return 0;
+
+puts("\ncclink found");//debug
+
+printf("link->state = %i\n", link->state);//debug
+
+if (link->state!=PICA_CCLINK_LOCAL_WAITCONNCLE && link->state!=PICA_CCLINK_LOCAL_WAITCONNCLR
+ && link->state!=PICA_CCLINK_N2NCLR_WAITCONNCLR && link->state!=PICA_CCLINK_N2NCLE_WAITCONNCLE)
+ return 0;
+
+{
+struct client *c;
+//проверка IP адреса.
+//IP адрес должен совпадать с адресом клиента в управляющем соединении
+
+if (link->state==PICA_CCLINK_LOCAL_WAITCONNCLE || link->state==PICA_CCLINK_N2NCLE_WAITCONNCLE)
+ c = link->p2;
+else if (link->state==PICA_CCLINK_LOCAL_WAITCONNCLR || link->state==PICA_CCLINK_N2NCLR_WAITCONNCLR)
+ c = link->p1;
+
+if (nc->addr.sin_addr.s_addr!=c->addr.sin_addr.s_addr)
+ {
+ puts("IP check failed");//debug
+ return 0;
+ }
+}
+
+if (link->state==PICA_CCLINK_LOCAL_WAITCONNCLE)
+ {
+ if(mp=client_wbuf_push(link->p1,PICA_PROTO_FOUND,PICA_PROTO_FOUND_SIZE))
+ {
+ *((unsigned int*)mp->tail)=callee_id;
+ }
+ else
+ {
+ puts("unable to send FOUND to caller");//debug
+ cclink_list_delete(link);
+ return 0;
+ }
+ }
+
+if (link->state==PICA_CCLINK_N2NCLE_WAITCONNCLE)
+ {
+ if(mp=nodelink_wbuf_push(link->caller_node,PICA_PROTO_N2NALLOW,PICA_PROTO_N2NALLOW_SIZE))
+ {
+ *((unsigned int*)mp->tail)=caller_id;
+ *((unsigned int*)(mp->tail+4))=callee_id;
+ }
+ else
+ {
+ puts("unable to send N2NALLOW to caller node");//debug
+ cclink_list_delete(link);
+ return 0;
+ }
+ }
+
+cclink_activate(link,nc->sck);
+newconn_free(nc);
+return 1;
+}
+
+unsigned int procmsg_NODECONNREQ(unsigned char* buf,unsigned int size,void* ptr)
+{
+struct newconn *nc=(struct newconn*)ptr;
+struct PICA_proto_msg *mp;
+
+if (buf[2]==PICA_PROTO_VER_HIGH &&
+ buf[3]==PICA_PROTO_VER_LOW)
+ {
+ ////-прием входящего подключения
+ //--------------------------
+ struct nodelink *nl;
+ nl=nodelink_list_addnew(nc);
+ newconn_free(nc);
+ if (!nl)
+ return 0;
+
+ if (mp=nodelink_wbuf_push(nl,PICA_PROTO_INITRESPOK,PICA_PROTO_INITRESPOK_SIZE))
+ {
+ mp->tail[0]='O';
+ mp->tail[1]='K';
+ }
+ }
+else
+ {
+ //// PICA_PROTO_VERDIFFER
+ send(nc->sck,msg_VERDIFFER,msg_VERDIFFER_len,0);
+ return 0;
+ }
+
+return 1;
+}
+
+
+void mapfunc_sendsearchreq(struct nodelink *p,unsigned int id)
+{
+struct PICA_proto_msg *mp;
+
+mp=nodelink_wbuf_push(p,PICA_PROTO_SEARCH,PICA_PROTO_SEARCH_SIZE);
+
+if (mp)
+ *((unsigned int*)mp->tail) = id;
+
+}
+
+int nodelink_send_searchreq(unsigned int id)
+{
+if (!nodelink_list_count)
+ return 0;
+
+LISTMAP(nodelink,nodelink,mapfunc_sendsearchreq,id);
+
+return 1;
+}
+
+unsigned int procmsg_CONNREQOUTG(unsigned char* buf,unsigned int size,void* ptr)
+{
+struct client *i,*o;//i - указатель на вызывающего клиента, o - вызываемого(если найден)
+unsigned int callee_id;
+struct PICA_proto_msg *mp;
+
+puts("procmsg_CONNREQOUTGOING");//debug
+
+i=(struct client *)ptr;
+
+callee_id=*(unsigned int*)(buf+2);
+
+if (o=client_tree_search(callee_id))
+ {
+ #warning "duplicated connections" // сделать проверку на наличие уже имеющегося соединения между клиентами
+ // и если оно есть, то разрывать его, либо... ???? надо подумать
+
+ #warning "self" // а еще - если клиент вызывает сам себя?
+
+ //отправить o _CONNREQINC
+ if (mp=client_wbuf_push(o,PICA_PROTO_CONNREQINC,PICA_PROTO_CONNREQINC_SIZE))
+ {
+ *((unsigned int*)mp->tail)=i->id;
+ cclink_list_addlocal(i,o);
+ return 1;
+ }
+
+ return 1;//сообщение отправить не удалось, но все равно возвращаем 1
+ }
+
+if (cclink_list_findwaitsearch(callee_id)/*search request for this id is already in progress*/
+ || nodelink_send_searchreq(callee_id) /*make new search*/)
+ {
+ cclink_list_addn2nclr(i,callee_id);
+ return 1;
+ }
+
+//отправить i _NOTFOUND
+if (mp=client_wbuf_push(i,PICA_PROTO_NOTFOUND,PICA_PROTO_NOTFOUND_SIZE))
+ {
+ *((unsigned int*)mp->tail)=callee_id;
+ puts("sending NOTFOUND... put to senq q...");//debug
+ }
+
+return 1;
+}
+
+unsigned int procmsg_CONNALLOW(unsigned char* buf,unsigned int size,void* ptr)
+{
+struct client *caller,*callee;
+struct cclink *link;
+unsigned int caller_id;
+
+puts("procmsg_CONNALLOW");//debug
+
+callee=(struct client *)ptr;
+caller_id=*(unsigned int*)(buf+2);
+
+puts("procmsg_CONNALLOW_");//debug
+printf("ptr=%X\n",ptr);//debug
+
+puts("procmsg_CONNALLOW__");//debug
+
+
+link=cclink_list_search(caller_id,callee->id);
+
+if (!link)
+ return 0;
+
+if (link->state!=PICA_CCLINK_LOCAL_WAITREP &&
+ link->state!=PICA_CCLINK_N2NCLE_WAITREP)
+ return 0;
+
+cclink_setwaitconn(link);
+
+puts("procmsg_CONNALLOW - return 1");//debug
+return 1;
+}
+
+unsigned int procmsg_CONNDENY(unsigned char* buf,unsigned int size,void* ptr)
+{
+struct client *caller,*callee;
+struct cclink *link;
+unsigned int caller_id;
+struct PICA_proto_msg *mp;
+
+puts("procmsg_procmsg_CONNDENY");//debug
+
+callee=(struct client *)ptr;
+caller_id=*(unsigned int*)(buf+2);
+
+link=cclink_list_search(caller_id,callee->id);
+
+if (!link)
+ return 0;
+
+caller = link -> p1;//check for NULL if not LOCAL
+
+if (link->state!=PICA_CCLINK_LOCAL_WAITREP &&
+ link->state!=PICA_CCLINK_N2NCLE_WAITREP)
+ {
+ cclink_list_delete(link);
+ return 0;
+ }
+
+if (link->state==PICA_CCLINK_LOCAL_WAITREP)
+ {
+ //отправить caller _NOTFOUND
+ if (mp=client_wbuf_push(caller,PICA_PROTO_NOTFOUND,PICA_PROTO_NOTFOUND_SIZE))
+ {
+ *((unsigned int*)mp->tail)=callee->id;
+ }
+ }
+else if (link->state==PICA_CCLINK_N2NCLE_WAITREP)
+ {
+ //отправить caller N2NNOTFOUND
+ if (mp=nodelink_wbuf_push(link->caller_node,PICA_PROTO_N2NNOTFOUND,PICA_PROTO_N2NNOTFOUND_SIZE))
+ {
+ *((unsigned int*)mp->tail)=caller_id;
+ *( ((unsigned int*)mp->tail)+1)=callee->id;
+ }
+ }
+
+cclink_list_delete(link);
+
+return 1;
+}
+
+unsigned int procmsg_NODERESP(unsigned char* buf,unsigned int size,void* ptr)
+{
+switch(buf[0])
+ {
+ case PICA_PROTO_INITRESPOK:
+ if (buf[2]!='O' || buf[3]!='K')
+ return 0;
+
+ *((int*)ptr)=0;
+ break;
+ case PICA_PROTO_VERDIFFER:
+ ((char*)ptr)[0]=buf[2];
+ ((char*)ptr)[1]=buf[3];
+ break;
+ }
+
+return 1;
+}
+
+unsigned int procmsg_NEWNODE(unsigned char* buf,unsigned int size,void* ptr)
+{
+struct nodelink *n=(struct nodelink *)ptr;
+
+switch (buf[0])
+ {
+ case PICA_PROTO_NEWNODE_IPV4:
+ printf("NEWNODE_IPV4: ip %.16s port %hu\n",inet_ntoa(*(struct in_addr*)(buf+2)),ntohs(*(uint16_t*)(buf+6)));//debug
+ {
+ struct PICA_nodeaddr_ipv4 na_ipv4;
+
+ na_ipv4.magick=PICA_PROTO_NEWNODE_IPV4;
+ na_ipv4.addr=*(in_addr_t*)(buf+2);
+ na_ipv4.port=*(in_port_t*)(buf+6);
+ //int nodelink_attach_nodeaddr(struct nodelink *nl, unsigned int addr_type, void* nodeaddr, unsigned int nodeaddr_size)
+ nodelink_attach_nodeaddr(n,na_ipv4.magick,&na_ipv4,sizeof(struct PICA_nodeaddr_ipv4));
+ }
+ break;
+ case PICA_PROTO_NEWNODE_IPV6:
+ #warning "TO DO"
+ break;
+ case PICA_PROTO_NEWNODE_DNS:
+ #warning "TODO"
+ break;
+ }
+
+
+
+return 1;
+}
+
+unsigned int procmsg_NODELISTREQ(unsigned char* buf,unsigned int size,void* ptr)
+{
+struct PICA_proto_msg *mp;
+struct nodelink *nlp;
+unsigned int nl_size=0;
+struct nodelink *n=0;
+struct client *c=0;
+
+switch(buf[0])
+ {
+ case PICA_PROTO_CLNODELISTREQ:
+ c = (struct client *)ptr;
+ break;
+ case PICA_PROTO_NODELISTREQ:
+ n = (struct nodelink *)ptr;
+ break;
+ }
+
+unsigned char *nl_buf=malloc(65536);
+
+if (!nl_buf)
+ return 0;//ERR
+
+puts("NODELISTREQ");//debug
+
+{//TEMP FIXME CONF
+//собственный адрес узла
+nl_buf[nl_size]=PICA_PROTO_NEWNODE_IPV4;
+*((in_addr_t*)(nl_buf+nl_size+1))=inet_addr(my_addr);
+*((uint16_t*)(nl_buf+nl_size+5))=htons(PICA_COMM_PORT);
+nl_size+=PICA_PROTO_NODELIST_ITEM_IPV4_SIZE;
+}
+
+nlp=nodelink_list_head;
+while(nlp && nl_size < 65532)//MAP
+ {
+ switch(nlp->addr_type)
+ {
+ case PICA_PROTO_NEWNODE_IPV4:
+ if ( PICA_PROTO_NODELISTREQ == buf[0] &&
+ ((struct PICA_nodeaddr_ipv4*)nlp->node_addr)->addr==((struct PICA_nodeaddr_ipv4*)n->node_addr)->addr &&
+ ((struct PICA_nodeaddr_ipv4*)nlp->node_addr)->port==((struct PICA_nodeaddr_ipv4*)n->node_addr)->port )
+ break;//exclude requester's address
+
+ nl_buf[nl_size]=PICA_PROTO_NEWNODE_IPV4;
+ *((in_addr_t*)(nl_buf+nl_size+1)) = ((struct PICA_nodeaddr_ipv4*)nlp->node_addr)->addr;
+ *((uint16_t*)(nl_buf+nl_size+5)) = ((struct PICA_nodeaddr_ipv4*)nlp->node_addr)->port;
+ nl_size += PICA_PROTO_NODELIST_ITEM_IPV4_SIZE;//CONF
+ break;
+ }
+//#warning "set limit on list size, <65532 bytes!!!"
+ nlp = nlp->next;
+ }
+
+switch(buf[0])
+ {
+ case PICA_PROTO_CLNODELISTREQ:
+ mp = client_wbuf_push( c, PICA_PROTO_CLNODELIST, nl_size+4);
+ break;
+ case PICA_PROTO_NODELISTREQ:
+ mp = nodelink_wbuf_push( n, PICA_PROTO_NODELIST, nl_size+4);
+ break;
+ }
+
+if (mp)
+ {
+ *((uint16_t*)mp->tail)=nl_size;
+ memcpy(mp->tail+2,nl_buf,nl_size);
+ }
+
+free(nl_buf);
+return 1;
+}
+
+unsigned int procmsg_NODELIST(unsigned char* buf,unsigned int size,void* ptr)
+{
+struct PICA_nodeaddr_ipv4 na_ipv4;
+
+struct nodelink *n=(struct nodelink *)ptr;
+unsigned int listsize=size-2-PICA_MSG_VARSIZE_INT16;
+unsigned int listleft=listsize;
+
+puts("NODELIST");//debug
+printf("NODELIST_SIZE:%u\n",listsize);
+
+while(listleft)
+ {
+#warning "strict error checking!!! -- listleft overflow, unknown list items, check listleft >= possible item size"
+ if (listleft==listsize) //first entry, sender node's address
+ {
+ switch(buf[size-listleft])
+ {
+ case PICA_PROTO_NEWNODE_IPV4:
+ na_ipv4.magick=PICA_PROTO_NEWNODE_IPV4;
+ na_ipv4.addr=*(in_addr_t*)(buf+size-listleft+1);
+ na_ipv4.port=*(in_port_t*)(buf+size-listleft+5);
+ nodelink_attach_nodeaddr(n,na_ipv4.magick,&na_ipv4,sizeof(struct PICA_nodeaddr_ipv4));
+
+ listleft-=PICA_PROTO_NODELIST_ITEM_IPV4_SIZE;
+ break;
+ }
+ continue;
+ }
+ else
+ {
+ char addr[256];
+ struct newconn nc;
+ struct nodelink *nlp;
+
+ memset(addr,0,256);
+ memset(&nc,0,sizeof(struct newconn));
+
+ switch(buf[size-listleft])
+ {
+ case PICA_PROTO_NEWNODE_IPV4:
+ na_ipv4.magick=PICA_PROTO_NEWNODE_IPV4;
+ na_ipv4.addr=*(in_addr_t*)(buf+size-listleft+1);
+ na_ipv4.port=*(in_port_t*)(buf+size-listleft+5);
+
+ sprintf(addr,"%.16s",inet_ntoa(*(struct in_addr*)&na_ipv4.addr));
+ if (try_connect_to_node(addr,ntohs(na_ipv4.port),&nc))
+ if (try_get_reply(&nc))
+ {
+ nlp=nodelink_list_addnew(&nc);
+ send_newnode(nlp,my_addr);
+ nodelink_attach_nodeaddr(nlp,na_ipv4.magick,&na_ipv4,sizeof(struct PICA_nodeaddr_ipv4));
+ printf("LINKED NODE:%s\n",inet_ntoa(*(struct in_addr*)&na_ipv4.addr));//debug
+ }
+
+ listleft-=PICA_PROTO_NODELIST_ITEM_IPV4_SIZE;
+ break;
+ }
+ }
+ }
+
+return 1;
+}
+
+unsigned int procmsg_N2NFOUND(unsigned char* buf,unsigned int size,void* ptr)
+{
+struct cclink *cc;
+struct nodelink *n=(struct nodelink *)ptr;
+unsigned int callee_id=*(unsigned int*)(buf+2);
+struct PICA_proto_msg *mp;
+
+while(cc=cclink_list_findwaitsearch(callee_id))
+ {
+ cclink_attach_remotecle_node(cc,n);
+ if(mp=nodelink_wbuf_push(n,PICA_PROTO_N2NCONNREQOUTG,PICA_PROTO_N2NCONNREQOUTG_SIZE))
+ {
+ *((unsigned int*)mp->tail) = cc->caller_id;
+ *((unsigned int*)(mp->tail+4)) = callee_id;
+ }
+ }
+return 1;
+}
+
+unsigned int procmsg_SEARCH(unsigned char* buf,unsigned int size,void* ptr)
+{
+struct nodelink *n=(struct nodelink *)ptr;
+unsigned int callee_id=*(unsigned int*)(buf+2);
+struct PICA_proto_msg *mp;
+struct client *callee;
+
+if (callee=client_tree_search(callee_id))
+ {
+ if(mp=nodelink_wbuf_push(n,PICA_PROTO_N2NFOUND,PICA_PROTO_N2NFOUND_SIZE))
+ {
+ *((unsigned int*)mp->tail) = callee_id;
+ }
+ }
+return 1;
+}
+
+unsigned int procmsg_N2NCONNREQOUTG(unsigned char* buf,unsigned int size,void* ptr)
+{
+struct nodelink *n=(struct nodelink *)ptr;
+unsigned int caller_id,callee_id;
+struct PICA_proto_msg *mp;
+struct client *callee;
+
+caller_id=*(unsigned int*)(buf+2);
+callee_id=*(unsigned int*)(buf+6);
+
+if (callee=client_tree_search(callee_id))
+ {
+ if (mp=client_wbuf_push(callee,PICA_PROTO_CONNREQINC,PICA_PROTO_CONNREQINC_SIZE))
+ {
+ *((unsigned int*)mp->tail)=caller_id;
+ cclink_list_addn2ncle(caller_id, n, callee);
+ }
+ }
+else
+ {
+ if(mp=nodelink_wbuf_push(n,PICA_PROTO_N2NNOTFOUND,PICA_PROTO_N2NNOTFOUND_SIZE))
+ {
+ *((unsigned int*)mp->tail) = caller_id;
+ *((unsigned int*)(mp->tail+4)) = callee_id;
+ }
+ }
+return 1;
+}
+
+unsigned int procmsg_N2NALLOW(unsigned char* buf,unsigned int size,void* ptr)
+{
+struct nodelink *n=(struct nodelink *)ptr;
+unsigned int caller_id,callee_id;
+struct PICA_proto_msg *mp;
+struct cclink *cc;
+
+caller_id=*(unsigned int*)(buf+2);
+callee_id=*(unsigned int*)(buf+6);
+
+cc=cclink_list_search(caller_id,callee_id);
+
+if (cc)
+ {
+ if (mp=client_wbuf_push(cc->p1,PICA_PROTO_FOUND,PICA_PROTO_FOUND_SIZE))
+ {
+ *((unsigned int*)mp->tail)=callee_id;
+
+ cclink_setwaitconn(cc);
+ }
+ }
+return 1;
+}
+
+unsigned int procmsg_N2NNOTFOUND(unsigned char* buf,unsigned int size,void* ptr)
+{
+struct nodelink *n=(struct nodelink *)ptr;
+unsigned int caller_id,callee_id;
+struct PICA_proto_msg *mp;
+struct cclink *cc;
+
+caller_id=*(unsigned int*)(buf+2);
+callee_id=*(unsigned int*)(buf+6);
+
+cc=cclink_list_search(caller_id,callee_id);
+
+if (cc)
+ {
+ if (mp=client_wbuf_push(cc->p1,PICA_PROTO_NOTFOUND,PICA_PROTO_NOTFOUND_SIZE))
+ {
+ *((unsigned int*)mp->tail)=callee_id;
+ }
+ cclink_list_delete(cc);
+ }
+
+return 1;
+}
+
+unsigned int procmsg_N2NMSG(unsigned char* buf,unsigned int size,void* ptr)
+{
+struct nodelink *n=(struct nodelink *)ptr;
+struct PICA_proto_msg *mp;
+unsigned int sender_id,receiver_id;
+struct cclink *cc;
+unsigned int len,datalen;
+
+len=*(unsigned short*)(buf+2);
+sender_id=*(unsigned int*)(buf+4);
+receiver_id=*(unsigned int*)(buf+8);
+
+if (len<8)
+ return 0;
+datalen=len-8;
+
+cc=cclink_list_search(sender_id,receiver_id);
+
+if (cc && cc->state==PICA_CCLINK_N2NCLE_ACTIVE)
+ {
+ if (datalen + cc->bufpos_p1p2 > cc->buflen_p1p2)
+ {
+ cclink_list_delete(cc);//CLOSING CCLINK DUE TO BUFER OVERRUN --------- FIXME FIX FIX FIX
+ return 1;
+ }
+
+ memcpy(cc->buf_p1p2 + cc->bufpos_p1p2, buf+12,datalen);
+ cc->bufpos_p1p2 += datalen;
+ return 1;
+ }
+///////////////
+cc=cclink_list_search(receiver_id,sender_id);
+
+if (cc && cc->state==PICA_CCLINK_N2NCLR_ACTIVE)
+ {
+ if (datalen + cc->bufpos_p2p1 > cc->buflen_p2p1)
+ {
+ cclink_list_delete(cc);//CLOSING CCLINK DUE TO BUFER OVERRUN --------- FIXME FIX FIX FIX
+ return 1;
+ }
+
+ memcpy(cc->buf_p2p1 + cc->bufpos_p2p1, buf+12,datalen);
+ cc->bufpos_p2p1 += datalen;
+ return 1;
+ }
+
+if(mp=nodelink_wbuf_push(n,PICA_PROTO_N2NNOTFOUND,PICA_PROTO_N2NNOTFOUND_SIZE))
+ {
+ *((unsigned int*)mp->tail) = sender_id;
+ *((unsigned int*)(mp->tail+4)) = receiver_id;
+ }
+
+return 1;
+}
+
+
+struct cclink* cclink_list_findwaitsearch(unsigned int callee_id)
+{
+struct cclink *c;
+c = cclink_list_head;
+while(c)
+ {
+ if (c->callee_id == callee_id && c->state==PICA_CCLINK_N2NCLR_WAITSEARCH)
+ return c;
+ c=c->next;
+ }
+return 0;
+}
+
+struct cclink* cclink_list_search(unsigned int caller_id, unsigned int callee_id)
+{
+struct cclink *c;
+c= cclink_list_head;
+
+while(c)
+ {
+ if (c->caller_id == caller_id && c->callee_id == callee_id)
+ return c;
+ c=c->next;
+ }
+return 0;
+}
+
+void cclink_list_delete(struct cclink *l)
+{
+//удаление из глобального списка
+if (l->prev)
+ l->prev->next=l->next;
+else
+ cclink_list_head=l->next;
+
+if (l->next)
+ l->next->prev=l->prev;
+else
+ cclink_list_end=l->prev;
+
+if (l->p1)
+ if (l->sck_p1)
+ {
+ SHUTDOWN(l->sck_p1);
+ CLOSE(l->sck_p1);
+ }
+
+if (l->p2)
+ if (l->sck_p2)
+ {
+ SHUTDOWN(l->sck_p2);
+ CLOSE(l->sck_p2);
+ }
+
+
+if (l->buf_p1p2)
+ free(l->buf_p1p2);
+
+if (l->buf_p2p1)
+ free(l->buf_p2p1);
+
+free(l);
+cclink_list_count--;
+}
+
+
+//clr - caller
+//cle - callee
+// null value of clr or cle means that corresponding client is on remote node, is not local client
+struct cclink* cclink_list_add(struct client *clr,struct client *cle)
+{
+struct cclink *l;
+
+puts("cclink_list_add");//debug
+
+l=(struct cclink*)calloc(sizeof(struct cclink),1);
+
+if (!l)
+ return 0;
+
+//l->state=PICA_LINKSTATE_WAITREP;
+
+l->p1=clr;
+l->p2=cle;
+
+//добавление в глобальный список
+if (cclink_list_head)
+ {
+ cclink_list_end->next=l;
+ l->prev=cclink_list_end;
+ cclink_list_end=l;
+ }
+else
+ {
+ cclink_list_head=l;
+ cclink_list_end=l;
+ }
+
+//timestamp
+l->tmst=time(0);
+
+cclink_list_count++;
+
+return l;
+}
+
+
+void cclink_list_addlocal(struct client *clr,struct client *cle)
+{
+struct cclink *l;
+
+l=cclink_list_add(clr,cle);
+
+if (l)
+ {
+ l->state=PICA_CCLINK_LOCAL_WAITREP;
+ l->caller_id = l->p1->id;
+ l->callee_id = l->p2->id;
+ }
+
+}
+
+void cclink_list_addn2nclr(struct client *clr,unsigned int callee_id)
+{
+struct cclink *l;
+
+l=cclink_list_add(clr,0);
+
+if (l)
+ {
+ l->state=PICA_CCLINK_N2NCLR_WAITSEARCH;
+ l->caller_id = l->p1->id;
+ l->callee_id = callee_id;
+ }
+}
+
+void cclink_attach_remotecle_node(struct cclink *ccl, struct nodelink *callee_node)
+{
+if (ccl && ccl->state==PICA_CCLINK_N2NCLR_WAITSEARCH)
+ {
+ ccl->state=PICA_CCLINK_N2NCLR_WAITREP;
+ ccl->callee_node=callee_node;
+ }
+}
+
+void cclink_list_addn2ncle(unsigned int caller_id, struct nodelink *caller_node, struct client *cle)
+{
+struct cclink *l;
+
+l=cclink_list_add(0,cle);
+
+if (l)
+ {
+ l->state=PICA_CCLINK_N2NCLE_WAITREP;
+ l->caller_id = caller_id;
+ l->callee_id = l->p2->id;
+ l->caller_node = caller_node;
+ }
+
+}
+
+void cclink_setwaitconn(struct cclink *ccl)
+{
+switch (ccl->state)
+ {
+ case PICA_CCLINK_LOCAL_WAITREP:
+ ccl->state=PICA_CCLINK_LOCAL_WAITCONNCLE;
+ //
+ break;
+ case PICA_CCLINK_N2NCLR_WAITREP:
+ ccl->state=PICA_CCLINK_N2NCLR_WAITCONNCLR;
+ break;
+ case PICA_CCLINK_N2NCLE_WAITREP:
+ ccl->state=PICA_CCLINK_N2NCLE_WAITCONNCLE;
+ break;
+ default:
+ ;
+ }
+}
+
+int cclink_allocbufs(struct cclink *ccl)
+{
+ccl->buf_p1p2 = malloc(DEFAULT_BUF_SIZE);
+ccl->buf_p2p1 = malloc(DEFAULT_BUF_SIZE);
+if (!ccl->buf_p1p2 || !ccl->buf_p2p1)
+ {
+ if (ccl->buf_p1p2)
+ free(ccl->buf_p1p2);
+ if (ccl->buf_p2p1)
+ free(ccl->buf_p2p1);
+ return 0;
+ }
+ccl->buflen_p1p2 = DEFAULT_BUF_SIZE;
+ccl->buflen_p2p1 = DEFAULT_BUF_SIZE;
+return 1;
+}
+
+void cclink_activate(struct cclink *ccl,SOCKET s)
+{
+switch(ccl->state)
+ {
+ case PICA_CCLINK_LOCAL_WAITCONNCLE:
+ ccl->sck_p2 = s;
+ ccl->state = PICA_CCLINK_LOCAL_WAITCONNCLR;
+ break;
+ case PICA_CCLINK_LOCAL_WAITCONNCLR:
+ ccl->sck_p1 = s;
+ if (!cclink_allocbufs(ccl))
+ {
+ cclink_list_delete(ccl);
+ return;
+ }
+ ccl->state = PICA_CCLINK_LOCAL_ACTIVE;
+ puts("caller cclink established");//debug
+ break;
+ case PICA_CCLINK_N2NCLR_WAITCONNCLR:
+ ccl->sck_p1 = s;
+ if (!cclink_allocbufs(ccl))
+ {
+ cclink_list_delete(ccl);
+ return;
+ }
+ ccl->state = PICA_CCLINK_N2NCLR_ACTIVE;
+ break;
+ case PICA_CCLINK_N2NCLE_WAITCONNCLE:
+ ccl->sck_p2 = s;
+ if (!cclink_allocbufs(ccl))
+ {
+ cclink_list_delete(ccl);
+ return;
+ }
+ ccl->state = PICA_CCLINK_N2NCLE_ACTIVE;
+ break;
+ }
+}
+
+
+int PICA_node_init()
+{
+struct sockaddr_in sd;
+
+SSL_load_error_strings();
+SSL_library_init();
+
+puts("This product includes software developed by the OpenSSL Project\n\
+for use in the OpenSSL Toolkit (http://www.openssl.org/\n");
+puts("This product includes cryptographic software written by\n\
+Eric Young (eay@cryptsoft.com)\n");
+puts(SSLeay_version(SSLEAY_VERSION));
+
+
+#ifdef WIN32
+WSADATA wsd;
+WSAStartup(MAKEWORD(2,2),&wsd);
+#endif
+
+//CONF -AF_INET6 ????
+listen_comm_sck=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+//ERR_CHECK
+
+memset(&sd,0,sizeof(sd));
+
+sd.sin_family=AF_INET;
+sd.sin_addr.s_addr=INADDR_ANY;//CONF
+sd.sin_port=htons(PICA_COMM_PORT);//CONF
+
+bind(listen_comm_sck,(struct sockaddr*)&sd,sizeof(sd));
+listen(listen_comm_sck,20);
+//ERR_CHECK
+
+{
+int ret_comm;
+unsigned long arg=1;
+#ifdef WIN32
+ret_comm=ioctlsocket(listen_comm_sck,FIONBIO,&arg);
+// ret_data=ioctlsocket(listen_data_sck,FIONBIO,&arg);
+// ret_data=ioctlsocket(listen_node_sck,FIONBIO,&arg);
+#else
+ret_comm=ioctl(listen_comm_sck,FIONBIO,(int*)&arg);
+// ret_data=ioctl(listen_data_sck,FIONBIO,(int*)&arg);
+// ret_data=ioctl(listen_node_sck,FIONBIO,(int*)&arg);
+#endif
+printf("ioctl ret_comm=%i\n",ret_comm);//debug
+}
+
+#ifdef NO_RAND_DEV
+PICA_rand_seed();
+#endif
+
+ctx=SSL_CTX_new(TLSv1_method());
+
+if (!ctx)
+;//ERR_CHECK
+
+return 1;
+}
+
+//функция возвращает номер клиента id в бинарном виде, извлекая его из строки,
+//которая возвращается функцией X509_NAME_oneline и представляет собой DN из сертификата клиента
+int get_id_fromsubjstr(char* DN_str,unsigned int* id)
+{
+char* tmp1;
+char* tmp2;
+
+tmp1=strstr(DN_str,"/CN=");
+
+if (!tmp1)
+ return 0;
+
+tmp2=strchr(tmp1,'#');
+
+if (!tmp2)
+ return 0;
+
+*tmp2=0;
+
+tmp1+=4;
+
+if (tmp1==tmp2)
+ return 0;
+
+*id=(unsigned int)strtol(tmp1,0,10);
+
+*tmp2='#';
+return 1;
+}
+
+
+void newconn_close(struct newconn* nc)
+{
+CLOSE(nc->sck);
+
+
+nc->sck=0;
+nc->pos=0; printf("newconn_close: closing connection\n");//debug
+}
+
+//функция ищет свободный элемент в массиве структур newconn и возвращает на него указатель
+//В случае, если свободный элемент не найден, возвращается элемент с наиболее поздней отметкой времени,
+//при этом соединение закрывается
+struct newconn* newconn_add(struct newconn *ncs,int *pos)
+{
+time_t min;
+int min_pos=0;
+if (*pos<MAX_NEWCONNS && !ncs[*pos].sck)
+ {
+ goto ret;
+ }
+else
+ {
+ *pos=0;
+ min=ncs->tmst;
+ while(ncs[*pos].sck && *pos<MAX_NEWCONNS)
+ {
+ if (ncs[*pos].tmst<min)
+ {
+ min=ncs[*pos].tmst;
+ min_pos=*pos;
+ }
+ (*pos)++;
+ }
+
+ if ( (*pos)==MAX_NEWCONNS)
+ {
+ *pos=min_pos;
+ SHUTDOWN(ncs[min_pos].sck);
+ CLOSE(ncs[min_pos].sck);
+ }
+ }
+
+ret:
+ncs[*pos].pos=0;
+ncs[*pos].tmst=time(0);
+return &ncs[(*pos)++];
+}
+
+void newconn_free(struct newconn* nc)
+{
+nc->sck=0;
+//nc->pos=0; - pos value is being used in processmsgdatastream
+}
+
+int client_tree_add(struct client *ci)
+{
+struct client *i;
+if (client_tree_root)
+ {
+ i=client_tree_root;
+
+ do
+ {
+ if (i->id==ci->id)
+ return 0;
+
+ if (ci->id < i->id)
+ {
+ if (i->left)
+ i=i->left;
+ else
+ {
+ i->left=ci;
+ ci->up=i;
+ return 1;
+ }
+ }
+ else
+ {
+ if (i->right)
+ i=i->right;
+ else
+ {
+ i->right=ci;
+ ci->up=i;
+ return 1;
+ }
+ }
+ }
+ while(1);
+ }
+else
+ client_tree_root=ci;
+
+return 1;
+}
+
+struct client* client_tree_search(unsigned int id)//надо бы потестить
+{
+struct client* i_ptr;
+i_ptr=client_tree_root;
+
+printf("client_tree_search: searching for %u...\n",id);//debug
+
+while(i_ptr)
+ {
+ if (i_ptr->id==id)
+ return i_ptr;
+
+ if (id < i_ptr->id)
+ i_ptr=i_ptr->left;
+ else
+ i_ptr=i_ptr->right;
+ }
+
+puts("client_tree_search- id not found");//debug
+return 0;
+}
+
+void client_tree_print(struct client *c)
+{
+if (!c)
+ {
+ puts("NULL");
+ return;
+ }
+
+printf("%p: %u LEFT: %p, RIGHT: %p\n", c, c->id, c->left, c->right);
+
+if (c->left)
+ client_tree_print(c->left);
+
+if (c->right)
+ client_tree_print(c->right);
+}
+
+//удаление структуры из дерева
+void client_tree_remove(struct client* ci)
+{
+struct client** p_link;//указывает на left или right в родительском узле
+
+if (!ci->up)
+ {
+ p_link = &client_tree_root;
+ }
+else
+ {
+ if (ci==ci->up->left)
+ p_link=&(ci->up->left);
+ else
+ p_link=&(ci->up->right);
+ }
+
+
+if (!ci->left && !ci->right)
+ {
+ *p_link=0;
+ return;
+ }
+
+if (ci->left && !ci->right)
+ {
+ ci->left->up=ci->up;
+ *p_link=ci->left;
+ return;
+ }
+
+if (!ci->left && ci->right)
+ {
+ ci->right->up=ci->up;
+ *p_link=ci->right;
+ return;
+ }
+
+
+ {
+ struct client *lm;
+ lm=ci->right;
+ while(lm->left) lm=lm->left;//поиск самого левого узла правого поддерева
+
+ if (lm!=ci->right)
+ {
+ lm->up->left=lm->right;
+ lm->right=ci->right;
+ }
+ lm->left=ci->left;
+ lm->up=ci->up;
+ *p_link=lm;
+ }
+}
+
+struct client *client_list_addnew(struct newconn *nc)
+{
+struct client* ci;
+
+ci=(struct client *)calloc(1,sizeof(struct client));
+
+if (!ci) return 0;
+
+ci->ssl_comm =SSL_new (ctx);
+
+if (!ci->ssl_comm)
+ {
+ free(ci);
+ return 0;
+ }
+
+if (client_list_end)
+ {
+ client_list_end->next=ci;
+ ci->prev=client_list_end;
+ client_list_end=ci;
+ }
+else
+ {
+ client_list_head=ci;
+ client_list_end=ci;
+ }
+
+ci->sck_comm=nc->sck;
+ci->addr=nc->addr;
+
+
+SSL_set_fd(ci->ssl_comm,ci->sck_comm);
+
+//ci->state=PICA_CLSTATE_SENDINGRESP;
+
+printf("client_list_addnew: returning %X\n",ci);//debug
+return ci;
+}
+
+void client_list_delete(struct client* ci)
+{
+printf("client_list_delete: ci=%X\n",ci);//debug
+
+SSL_free(ci->ssl_comm);
+//удаление из списка
+if (ci->prev)
+ ci->prev->next=ci->next;
+else
+ client_list_head=ci->next;
+
+if (ci->next)
+ ci->next->prev=ci->prev;
+else
+ client_list_end=ci->prev;
+
+//удаление из дерева
+if (client_tree_search(ci->id) == ci)
+ {
+ client_tree_remove(ci);
+ client_tree_print(client_tree_root);//debug
+ }
+
+
+CLOSE(ci->sck_comm);
+
+free(ci);
+}
+
+struct PICA_proto_msg* client_wbuf_push
+ (struct client *c,unsigned int msgid,unsigned int size)
+{
+//Дальнейший вариант - размер буфера w_buf может увеличиться в
+//этой функции путем вызова realloc, если размер сообщения превышает половину
+//текущего размера буфера
+//Если буфер занят меньше, чем на половину, его размер может уменьшаться где-нибудь
+//в другом месте, для экономии памяти
+ if (CL_WBUFSIZE - c->w_pos >= size)
+ {
+ struct PICA_proto_msg *mp=(struct PICA_proto_msg *)(c->w_buf + c->w_pos);
+ mp->head[0]=mp->head[1]=msgid;
+ c->w_pos+=size;
+ return mp;
+ }
+ else
+ return 0;
+}
+
+struct nodelink *nodelink_list_addnew(struct newconn *nc)
+{
+struct nodelink *nl;
+
+nl=(struct nodelink*)calloc(1,sizeof(struct nodelink));
+
+if (!nl)
+ return 0;
+
+if (nodelink_list_end)
+ {
+ nodelink_list_end->next=nl;
+ nl->prev=nodelink_list_end;
+ nodelink_list_end=nl;
+ }
+else
+ {
+ nodelink_list_head=nl;
+ nodelink_list_end=nl;
+ }
+
+nl->sck=nc->sck;
+nl->addr=nc->addr;
+
+nl->r_buf=calloc(1,DEFAULT_BUF_SIZE);
+if (!nl->r_buf)
+ {
+ free(nl);
+ return 0;
+ }
+nl->buflen_r=DEFAULT_BUF_SIZE;
+
+printf("nodelink_list_addnew: returning %X\n",nl);//debug
+nodelink_list_count++;
+return nl;
+}
+
+void nodelink_list_delete(struct nodelink *n)
+{
+if (n->r_buf)
+ free(n->r_buf);
+
+if (n->w_buf)
+ free(n->w_buf);
+
+if (n->node_addr)
+ free(n->node_addr);
+
+CLOSE(n->sck);
+
+if (n->prev)
+ n->prev->next=n->next;
+else
+ nodelink_list_head=n->next;
+
+if (n->next)
+ n->next->prev=n->prev;
+else
+ nodelink_list_end=n->prev;
+
+nodelink_list_count--;
+free(n);
+}
+
+int nodelink_attach_nodeaddr(struct nodelink *nl, unsigned int addr_type, void* nodeaddr, unsigned int nodeaddr_size)
+{
+if (nl && nodeaddr)
+ {
+ if (nl->node_addr)
+ free(nl->node_addr);
+
+ nl->node_addr=malloc(nodeaddr_size);
+ if (!nl->node_addr)
+ return 0;//ERR_CHECK
+ memcpy(nl->node_addr,nodeaddr,nodeaddr_size);
+
+ nl->addr_type=addr_type;
+ return 1;
+ }
+return 0;
+}
+
+int nodelink_rbuf_grow(struct nodelink *nl)
+{
+unsigned char *p;
+
+puts(__FUNCTION__);//debug
+
+p=realloc(nl->r_buf,nl->buflen_r + DEFAULT_BUF_SIZE);
+
+if (!p)
+ {
+ puts("out of memory");//debug
+ return 0;
+ }
+nl->r_buf = p;
+nl->buflen_r +=DEFAULT_BUF_SIZE;
+
+return 1;
+}
+
+struct PICA_proto_msg* nodelink_wbuf_push
+ (struct nodelink *nl,unsigned int msgid,unsigned int size)
+{
+struct PICA_proto_msg *mp;
+
+if (!nl->w_buf)
+ {
+ nl->w_buf=calloc(1,DEFAULT_BUF_SIZE );
+ if (!nl->w_buf)
+ {
+ puts("out of memory");//debug
+ return 0;
+ }
+ nl->buflen_w=DEFAULT_BUF_SIZE;
+ }
+
+if ((nl->buflen_w - nl->w_pos) < size)
+ {
+ unsigned char *p;
+ p=realloc(nl->w_buf,size + nl->w_pos);
+
+ if (p)
+ {
+ nl->w_buf=p;
+ nl->buflen_w=size + nl->w_pos;
+ }
+ else
+ return 0;
+ }
+
+mp=(struct PICA_proto_msg *)(nl->w_buf + nl->w_pos);
+mp->head[0]=mp->head[1]=msgid;
+nl->w_pos+=size;
+return mp;
+}
+
+
+
+// обработка различных таймаутов. Отправка пингов
+
+void process_timeouts_newconn()
+{
+clock_t t;
+int i;
+
+t=time(0);
+
+for (i=0;i<MAX_NEWCONNS;i++)
+ {
+ if (newconns[i].sck)
+ {
+ if (newconns[i].tmst<t)
+ {
+ if (t-newconns[i].tmst>NEWCONN_TIMEOUT)
+ {
+ newconn_close(newconns+i);
+ puts("closing by timeout(1): ");//debug
+ }
+ }
+ else if ( newconns[i].tmst-t>NEWCONN_TIMEOUT)
+ {
+ newconn_close(newconns+i);
+ puts("closing by timeout(2): ");//debug
+ }
+ }
+ }
+}
+
+void process_timeouts_c2c()
+{
+struct cclink *il;
+
+il=cclink_list_head;
+
+while(il)
+ {
+ switch(il->state)
+ {
+ case PICA_CCLINK_LOCAL_ACTIVE:
+ case PICA_CCLINK_N2NCLR_ACTIVE:
+ case PICA_CCLINK_N2NCLE_ACTIVE:
+
+ //idle timeout (>=60sec), update timestamp at process_c2c
+
+ break;
+ default:
+ if ((time(0)- il->tmst)>TMO_CCLINK_WAITACTIVE)
+ {
+ struct cclink *dl;
+ dl=il;
+ il=il->next;
+ cclink_list_delete(dl);
+ puts("inactive cclink closed by timeout");//debug
+ continue;
+ }
+ }
+ il=il->next;
+ }
+}
+
+int process_timeouts()
+{
+process_timeouts_newconn();
+process_timeouts_c2c();
+//..
+return 1;
+}
+
+void set_select_fd(SOCKET s, fd_set *readfds, int *nfds)
+{
+FD_SET(s,readfds);
+
+if (s > *nfds)
+ *nfds=s;
+
+//printf("set_select_fd([socket %i ])\n",s);//debug
+}
+
+void listen_set_fds(fd_set *readfds,int *nfds)
+{
+set_select_fd(listen_comm_sck,readfds,nfds);
+}
+
+void newconn_set_fds(fd_set *readfds,int *nfds)
+{
+int i;
+
+for (i=0;i<MAX_NEWCONNS;i++)
+ if (newconns[i].sck)
+ set_select_fd(newconns[i].sck,readfds,nfds);
+}
+
+void c2n_set_fds(fd_set *readfds,int *nfds)
+{
+struct client *i_ptr;
+
+i_ptr=client_list_head;
+while(i_ptr)
+ {
+ set_select_fd(i_ptr->sck_comm,readfds,nfds);
+
+ i_ptr=i_ptr->next;
+ }
+}
+
+void c2c_set_fds(fd_set *readfds,int *nfds)
+{
+struct cclink *cc;
+
+cc=cclink_list_head;
+while(cc)
+ {
+ if ( !cc->jam_p1p2 && (cc->state==PICA_CCLINK_LOCAL_ACTIVE || cc->state==PICA_CCLINK_N2NCLR_ACTIVE) )
+ set_select_fd(cc->sck_p1,readfds,nfds);
+
+ if ( !cc->jam_p2p1 && (cc->state==PICA_CCLINK_LOCAL_ACTIVE || cc->state==PICA_CCLINK_N2NCLE_ACTIVE) )
+ set_select_fd(cc->sck_p2,readfds,nfds);
+
+ cc=cc->next;
+ }
+}
+
+void n2n_set_fds(fd_set *readfds,int *nfds)
+{
+struct nodelink *nl;
+nl=nodelink_list_head;
+
+while(nl)
+ {
+ set_select_fd(nl->sck,readfds,nfds);
+ nl=nl->next;
+ }
+}
+
+void process_listen_read(fd_set *readfds)
+{
+struct newconn *nc;
+SOCKET s;
+struct sockaddr_in addr;
+int addrsize=sizeof(struct sockaddr_in);
+if (FD_ISSET(listen_comm_sck,readfds))
+ {
+ puts("accept");//debug
+ s=accept(listen_comm_sck,(struct sockaddr*)&addr,&addrsize);
+
+ if (s>0)
+ {
+ nc=newconn_add(newconns,&newconns_pos);
+
+ nc->sck=s;
+ nc->addr=addr;
+ printf("accepted connection\n");//debug
+ }
+ else
+ {
+ newconn_free(nc);
+
+ perror("accept:");//debug
+ printf("accept error!\n");//debug
+ //ERR_CHECK
+ }
+ }
+
+//IPv6
+}
+
+void process_newconn_read(fd_set *readfds)
+{
+int i,ret;
+
+for (i=0;i<MAX_NEWCONNS;i++)
+ if (newconns[i].sck && FD_ISSET(newconns[i].sck,readfds))
+ {
+ ret=recv(newconns[i].sck,newconns[i].buf+newconns[i].pos,NEWCONN_BUFSIZE - newconns[i].pos,0);
+
+ if (ret<=0)
+ {
+ perror("process_newconn_read - recv:");//debug
+ newconn_close(newconns+i);
+ continue;
+ }
+ newconns[i].pos+=ret;
+
+ if(!PICA_processdatastream(newconns[i].buf,&(newconns[i].pos),newconns+i /*arg*/,_msginfo_newconn, MSGINFO_MSGSNUM(_msginfo_newconn) ))
+ {
+ puts("processdatastream error\n");//debug
+ newconn_close(newconns+i);
+ }
+ //FD_CLR(newconns[i].sck,readfds);
+ }
+}
+
+
+
+void process_c2n_read(fd_set *readfds)
+{
+struct client *i_ptr,*kill_ptr;
+int ret;
+
+i_ptr=client_list_head;
+kill_ptr=0;
+
+while (i_ptr)
+ {
+ if (FD_ISSET(i_ptr->sck_comm,readfds))
+ switch(i_ptr->state)
+ {
+ case PICA_CLSTATE_CONNECTED:
+ ret=SSL_read(i_ptr->ssl_comm,i_ptr->r_buf+i_ptr->r_pos,CL_RBUFSIZE-i_ptr->r_pos);
+
+ if (!ret)
+ kill_ptr=i_ptr;
+
+ if (ret<0)
+ switch(SSL_get_error(i_ptr->ssl_comm,ret))
+ {
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_READ:
+ break;
+ default:
+ kill_ptr=i_ptr;
+ }
+
+ if (ret>0)
+ {
+ i_ptr->r_pos+=ret;
+ if(!PICA_processdatastream(i_ptr->r_buf,&(i_ptr->r_pos),i_ptr,_msginfo_comm, MSGINFO_MSGSNUM(_msginfo_comm) ))
+ kill_ptr=i_ptr;
+
+ }
+ break;
+ case PICA_CLSTATE_TLSNEGOTIATION:
+ i_ptr->w_pos=1;//????
+ //-------------- <<<<<<<<<<<<<<<<<<<<<<<<----------------------------------------
+ break;
+ };
+
+ i_ptr=i_ptr->next;
+
+ if (kill_ptr)
+ {
+ client_list_delete(kill_ptr);
+ kill_ptr=0;
+ }
+ }
+
+}
+
+void process_c2c_read(fd_set *readfds)
+{
+int i,ret;
+struct cclink *cc,*kill_ptr;
+
+cc=cclink_list_head;
+kill_ptr=0;
+
+while(cc)
+ {
+ if ((cc->state==PICA_CCLINK_LOCAL_ACTIVE || cc->state==PICA_CCLINK_N2NCLR_ACTIVE)
+ && FD_ISSET(cc->sck_p1,readfds) && !cc->jam_p1p2 )
+ {
+ ret=recv(cc->sck_p1,cc->buf_p1p2 + cc->bufpos_p1p2,cc->buflen_p1p2 - cc->bufpos_p1p2,0);
+ printf("DATA recv p1p2: ret=%i\n",ret);//debug
+ if (ret>0)
+ {
+ cc->bufpos_p1p2+=ret;
+ }
+ else
+ kill_ptr=cc;
+ }
+
+ if ((cc->state==PICA_CCLINK_LOCAL_ACTIVE || cc->state==PICA_CCLINK_N2NCLE_ACTIVE)
+ && FD_ISSET(cc->sck_p2,readfds) && !cc->jam_p2p1 )
+ {
+ ret=recv(cc->sck_p2,cc->buf_p2p1 + cc->bufpos_p2p1,cc->buflen_p2p1 - cc->bufpos_p2p1,0);
+ printf("DATA recv p2p1: ret=%i\n",ret);//debug
+ if (ret>0)
+ {
+ cc->bufpos_p2p1+=ret;
+ }
+ else
+ kill_ptr=cc;
+ }
+
+
+ cc=cc->next;
+
+ if (kill_ptr)
+ {
+ cclink_list_delete(kill_ptr);
+ kill_ptr=0;
+ }
+ }
+}
+
+void process_n2n_read(fd_set *readfds)
+{
+int i,ret;
+struct nodelink *nl,*kill_ptr;
+
+nl=nodelink_list_head;
+kill_ptr=0;
+
+while(nl)
+ {
+ if (FD_ISSET(nl->sck,readfds))
+ {
+ ret=recv(nl->sck,nl->r_buf+nl->r_pos,nl->buflen_r - nl->r_pos,0);
+
+ if (ret<=0)
+ {
+ perror("n2n_read recv:");//debug
+ kill_ptr=nl;
+ }
+ else
+ {
+ nl->r_pos+=ret;
+
+ if(!PICA_processdatastream(nl->r_buf,&(nl->r_pos),nl,_msginfo_node, MSGINFO_MSGSNUM(_msginfo_node) ))
+ kill_ptr=nl;
+
+ if (nl->buflen_r == nl->r_pos)
+ if (!nodelink_rbuf_grow(nl))
+ kill_ptr=nl;
+ }
+ }
+
+ nl=nl->next;
+
+ if (kill_ptr)
+ {
+ nodelink_list_delete(kill_ptr);puts("process_n2n_read - calling nodelink_list_delete");//debug
+ kill_ptr=0;
+ }
+ }
+}
+
+void process_c2n_write()
+{
+struct client *i_ptr,*kill_ptr;
+int ret;
+
+i_ptr=client_list_head;
+kill_ptr=0;
+
+while(i_ptr)
+ {
+ //---------
+ switch(i_ptr->state)
+ {
+ case PICA_CLSTATE_CONNECTED:
+ if (!i_ptr->w_pos)
+ break;
+
+ if (!i_ptr->btw_ssl)
+ i_ptr->btw_ssl=i_ptr->w_pos;
+
+ ret=SSL_write(i_ptr->ssl_comm,i_ptr->w_buf,i_ptr->btw_ssl);
+
+ printf("process_comm: write: ret of SSL_write=%i\n",ret);//debug
+
+ if (ret==i_ptr->btw_ssl)
+ {
+ i_ptr->w_pos-=i_ptr->btw_ssl;
+ i_ptr->btw_ssl=0;
+ }
+
+ if (!ret)
+ kill_ptr=i_ptr;
+
+ if (ret<0)
+ {
+ ret=SSL_get_error(i_ptr->ssl_comm,ret);
+ if (ret!=SSL_ERROR_WANT_WRITE && ret!=SSL_ERROR_WANT_READ)
+ kill_ptr=i_ptr;
+ }
+ break;
+ case PICA_CLSTATE_SENDINGRESP:
+ ret=send(i_ptr->sck_comm,i_ptr->w_buf,i_ptr->w_pos,0);
+ printf("process_comm: write: sendingresp w_pos=%u ret=%i\n",i_ptr->w_pos,ret);//debug
+ if (!ret)
+ kill_ptr=i_ptr;
+
+ if (ret==SOCKET_ERROR)
+ ;//ERR_CHECK
+
+ if (ret < i_ptr->w_pos)
+ memmove(i_ptr->w_buf,i_ptr->w_buf+ret,i_ptr->w_pos - ret); //ring buffer is better (??)
+
+ i_ptr->w_pos-=ret;
+
+
+ if (!i_ptr->w_pos)
+ i_ptr->state=PICA_CLSTATE_TLSNEGOTIATION;
+
+ puts("SENDINGRESP");//debug
+ break;
+ case PICA_CLSTATE_TLSNEGOTIATION:
+ i_ptr->w_pos=0;
+ ret=SSL_connect(i_ptr->ssl_comm);
+
+ printf("wr SSL_connect=%i\n",ret);//debug
+
+ if (!ret)
+ kill_ptr=i_ptr;
+
+ if (ret<0)
+ {
+ int eret;
+ eret=SSL_get_error(i_ptr->ssl_comm,ret);
+ switch(eret)
+ {
+ case SSL_ERROR_WANT_WRITE:
+ i_ptr->w_pos=1;
+ printf("wr WANT_WRITE\n");//debug
+ break;
+ case SSL_ERROR_WANT_READ:
+ printf("wr WANT_READ\n");//debug
+ break;
+ default:
+ printf("wr SSL_get_error=%i",eret);//debug
+ kill_ptr=i_ptr;
+ }
+ }
+ if (ret==1)
+ //проверить сертификат пользователя
+ {
+ X509* client_cert;
+ char* DN_str;
+
+ printf("SSL connection using %s\n", SSL_get_cipher(i_ptr->ssl_comm));//debug
+
+ client_cert=SSL_get_peer_certificate (i_ptr->ssl_comm);
+ if (!client_cert)
+ {
+ //ERR_CHECK //нет сертификата
+ kill_ptr=i_ptr;
+ }
+ //извлечение идентификационного номера из сертификата
+ DN_str=X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0);
+ if (!DN_str)
+ {
+ //ERR_CHECK
+ kill_ptr=i_ptr;
+ }
+ ret=get_id_fromsubjstr(DN_str,&i_ptr->id);
+ if (!ret)
+ {
+ //ERR_CHECK //ошибка выделения id
+ kill_ptr=i_ptr;
+ }
+ printf("user id=%u\n",i_ptr->id);//debug
+ #warning "see comments below"
+ //сделать проверку названия сети!!!!---------------------------<<<<<<<<<<<<<<<<<<<<<<
+
+ OPENSSL_free(DN_str);
+ X509_free(client_cert);
+ //прицепить структуру к дереву
+ if (client_tree_search(i_ptr->id))
+ ret = 0;//id already exists in tree
+ else
+ {
+ ret=client_tree_add(i_ptr);
+ client_tree_print(client_tree_root);//debug
+ }
+ if (!ret)
+ {
+ //ERR_CHECK //не уникальный id
+ kill_ptr=i_ptr;
+ }
+ i_ptr->state=PICA_CLSTATE_CONNECTED;
+ }
+ break;
+ }
+ //---------
+
+ i_ptr=i_ptr->next;
+
+ if (kill_ptr)
+ {
+ client_list_delete(kill_ptr);
+ kill_ptr=0;
+ }
+ }
+}
+
+void process_c2c_write()
+{
+int i,ret;
+struct cclink *cc,*kill_ptr;
+
+cc=cclink_list_head;
+kill_ptr=0;
+
+while(cc)
+ {
+ if (cc->bufpos_p1p2/* && (cc->state==PICA_CCLINK_LOCAL_ACTIVE || cc->state==PICA_CCLINK_N2NCLE_ACTIVE)*/)
+ {
+ if (cc->state==PICA_CCLINK_N2NCLR_ACTIVE)//receiver is on remote node
+ {
+ int sendlen;
+ struct PICA_proto_msg *mp;
+
+ sendlen = (cc->bufpos_p1p2 > PICA_PROTO_N2NMSG_MAXDATASIZE) ? PICA_PROTO_N2NMSG_MAXDATASIZE : cc->bufpos_p1p2;
+
+ if(mp=nodelink_wbuf_push(cc->callee_node,PICA_PROTO_N2NMSG,sendlen+12))
+ {
+ *((unsigned short*)mp->tail) = 8+sendlen;
+ *((unsigned int*)(mp->tail+2))=cc->caller_id;
+ *((unsigned int*)(mp->tail+6))=cc->callee_id;
+ memcpy(mp->tail+10, cc->buf_p1p2, sendlen);
+ ret=sendlen;
+ }
+ else
+ ret=0;
+ }
+ else
+ ret=send(cc->sck_p2,cc->buf_p1p2,cc->bufpos_p1p2,MSG_NOSIGNAL);
+
+ printf("DATA send p1p2: ret=%i\n",ret);//debug
+ if (ret>0)
+ {
+ if (ret < cc->bufpos_p1p2)
+ memmove(cc->buf_p1p2,cc->buf_p1p2+ret,cc->bufpos_p1p2-ret);//may be it's better to implement ring buffer here
+ cc->bufpos_p1p2-=ret;
+ cc->jam_p1p2=0;
+ }
+
+ if (ret==0)
+ {
+ kill_ptr=cc;
+ }
+ if (ret==-1)
+ {
+ #ifdef WIN32
+ ret=WSAGetLastError();
+ if (ret==WSAEWOULDBLOCK || ret==WSAENOBUFS)
+ cc->jam_p1p2=1;
+ else
+ {
+ kill_ptr=cc;
+ }
+ #else
+ ret=errno;
+ if (ret==EAGAIN || ret==ENOBUFS)
+ cc->jam_p1p2=1;
+ else
+ {
+ kill_ptr=cc;
+ }
+ #endif
+ }
+
+ }
+ if (cc->bufpos_p2p1 /*&& (cc->state==PICA_CCLINK_LOCAL_ACTIVE || cc->state==PICA_CCLINK_N2NCLR_ACTIVE)*/)
+ {
+ if (cc->state==PICA_CCLINK_N2NCLE_ACTIVE)//receiver is on remote node
+ {
+ int sendlen;
+ struct PICA_proto_msg *mp;
+
+ sendlen = (cc->bufpos_p2p1 > PICA_PROTO_N2NMSG_MAXDATASIZE) ? PICA_PROTO_N2NMSG_MAXDATASIZE : cc->bufpos_p2p1;
+
+ if(mp=nodelink_wbuf_push(cc->caller_node,PICA_PROTO_N2NMSG,sendlen+12))
+ {
+ *((unsigned short*)mp->tail) = 8+sendlen;
+ *((unsigned int*)(mp->tail+2))=cc->callee_id;
+ *((unsigned int*)(mp->tail+6))=cc->caller_id;
+ memcpy(mp->tail+10, cc->buf_p2p1, sendlen);
+ ret=sendlen;
+ }
+ else
+ ret=0;
+ }
+ else
+ ret=send(cc->sck_p1,cc->buf_p2p1,cc->bufpos_p2p1,MSG_NOSIGNAL);
+
+ printf("DATA send p2p1: ret=%i\n",ret);//debug
+ if (ret>0)
+ {
+ if (ret < cc->bufpos_p2p1)
+ memmove(cc->buf_p2p1,cc->buf_p2p1+ret,cc->bufpos_p2p1-ret);//may be it's better to implement ring buffer here
+ cc->bufpos_p2p1-=ret;
+ cc->jam_p2p1=0;
+ }
+
+ if (ret==0)
+ {
+ kill_ptr=cc;
+ }
+ if (ret==-1)
+ {
+ #ifdef WIN32
+ ret=WSAGetLastError();
+ if (ret==WSAEWOULDBLOCK || ret==WSAENOBUFS)
+ cc->jam_p2p1=1;
+ else
+ {
+ kill_ptr=cc;
+ }
+ #else
+ ret=errno;
+ if (ret==EAGAIN || ret==ENOBUFS)
+ cc->jam_p2p1=1;
+ else
+ {
+ kill_ptr=cc;
+ }
+ #endif
+
+ }
+
+ }
+
+ cc=cc->next;
+
+ if (kill_ptr)
+ {
+ cclink_list_delete(kill_ptr);
+ kill_ptr=0;
+ }
+ }
+}
+
+void process_n2n_write()
+{
+int ret;
+struct nodelink *nl,*kill_ptr;
+
+nl=nodelink_list_head;
+kill_ptr=0;
+
+while(nl)
+ {
+ if (nl->w_pos>0)
+ {
+ ret=send(nl->sck,nl->w_buf,nl->w_pos,MSG_NOSIGNAL);
+
+ if (ret>0)
+ {
+ if (ret < nl->w_pos)
+ memmove(nl->w_buf,nl->w_buf+ret,nl->w_pos-ret);//may be it's better to implement ring buffer here
+ nl->w_pos-=ret;
+ }
+ if (ret<0)
+ {
+ #ifdef WIN32
+ ret=WSAGetLastError();
+ if (!(ret==WSAEWOULDBLOCK || ret==WSAENOBUFS))
+ kill_ptr=nl;
+ #else
+ ret=errno;
+ if (!(ret==EAGAIN || ret==ENOBUFS || ret == EINTR))
+ kill_ptr=nl;
+ #endif
+ }
+ if (ret==0)
+ kill_ptr=nl;
+ }
+
+ nl=nl->next;
+
+ if (kill_ptr)
+ {
+ nodelink_list_delete(kill_ptr);puts("process_n2n_write - calling nodelink_list_delete");//debug
+ kill_ptr=0;
+ }
+ }
+}
+
+int node_loop()
+{
+fd_set fds;
+struct timeval tv;
+int nfds,ret;
+
+do
+ {
+ FD_ZERO(&fds);
+ tv.tv_sec=SELECT_TIMEOUT;
+ tv.tv_usec=0;
+
+ listen_set_fds(&fds,&nfds);
+ newconn_set_fds(&fds,&nfds);
+ c2n_set_fds(&fds,&nfds);
+ c2c_set_fds(&fds,&nfds);
+ n2n_set_fds(&fds,&nfds);
+
+ ret=select(nfds+1,&fds,0,0,&tv);
+
+ if (ret<0)
+ {
+ perror("node_loop-select error ");
+ //ERR_CHECK
+ return -1;
+ }
+
+ if (ret>0)
+ {
+ process_c2n_read(&fds);
+ process_c2c_read(&fds);
+ process_n2n_read(&fds);
+ process_newconn_read(&fds);
+ process_listen_read(&fds);
+ }
+
+
+ process_c2n_write();
+ process_c2c_write();
+ process_n2n_write();
+
+ process_timeouts();
+ }
+while(1);////<<<<<<<<<<
+return 0;
+}
+
+int main(int argc,char** argv)
+{
+printf("PicaPica node started\n");//debug
+
+if (!PICA_node_init())
+ return -1;
+
+//if (PICA_node_joinskynet("nodelist")<=0)//CONF имя файла
+ /*return -1*/;//вывести предупреждение, что ни к одному другому узлу подключиться не удалось
+
+PICA_node_joinskynet("nodes.sqlite",argv[1]);//CONF-CONF имя файла с адресами узлов, свой адрес
+
+my_addr=argv[1];//TEMP FIXME
+
+return node_loop();
+}
diff --git a/PICA_node.h b/PICA_node.h
new file mode 100755
index 0000000..d0eb04b
--- /dev/null
+++ b/PICA_node.h
@@ -0,0 +1,159 @@
+#ifndef PICA_NODE_H
+#define PICA_NODE_H
+
+#ifdef WIN32
+
+#include <windows.h>
+#include <winsock2.h>
+
+#else
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/select.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <sys/errno.h>
+
+#define SOCKET int
+#define SOCKET_ERROR -1
+
+#endif
+
+#include <openssl/crypto.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+#include <time.h>
+#include "PICA_nodeaddrlist.h"
+
+
+#define PICA_COMM_PORT 51914
+//#define PICA_DATA_PORT 51915
+//#define PICA_NODE_PORT 51916
+
+#define PICA_CLSTATE_SENDINGRESP 1
+#define PICA_CLSTATE_TLSNEGOTIATION 2
+#define PICA_CLSTATE_CONNECTED 3
+
+//LOCAL - оба клиента подсоединены к одному узлу
+//N2NCLR - к узлу подключен вызывающий клиент
+//N2NCLE - к узлу подключен вызываемый клиент
+
+#define PICA_CCLINK_LOCAL_WAITREP 1
+#define PICA_CCLINK_LOCAL_WAITCONNCLE 2
+#define PICA_CCLINK_LOCAL_WAITCONNCLR 3
+#define PICA_CCLINK_LOCAL_ACTIVE 4
+
+#define PICA_CCLINK_N2NCLR_WAITSEARCH 5
+#define PICA_CCLINK_N2NCLR_WAITREP 6
+#define PICA_CCLINK_N2NCLR_WAITCONNCLR 7
+#define PICA_CCLINK_N2NCLR_ACTIVE 8
+
+#define PICA_CCLINK_N2NCLE_WAITREP 9
+#define PICA_CCLINK_N2NCLE_WAITCONNCLE 10
+#define PICA_CCLINK_N2NCLE_ACTIVE 11
+
+
+
+#define PICA_NETWORKNAME "TESTNET" // TEMP //CONF
+
+#define NEWCONN_BUFSIZE 10
+#define CL_RBUFSIZE 32
+#define CL_WBUFSIZE 128
+
+#define DEFAULT_BUF_SIZE 4096
+
+struct nodelink;
+
+struct client
+{
+unsigned int id;
+struct client *up;
+struct client *left;
+struct client *right;
+SOCKET sck_comm;
+struct sockaddr_in addr;
+SSL *ssl_comm;
+unsigned char r_buf[CL_RBUFSIZE];
+unsigned char w_buf[CL_WBUFSIZE];
+unsigned int r_pos;
+unsigned int w_pos;
+
+unsigned int btw_ssl;//кол-во отправляемых байтов, передаваемое функции SSL_write
+//Если требуется повторный вызов SSL_write,то нужно передавать те же значения аргументов,а w_pos может быть увеличен
+//
+int state;
+struct client* next;//структура client может быть одновременно в составе дерева и линейного списка
+struct client* prev;
+
+
+
+};
+
+struct cclink //структура, описывающая соединение двух клиентов между собой через сервер
+{
+//unsigned int cclink_id;
+unsigned int state;
+
+clock_t tmst;
+struct client *p1;//caller
+struct client *p2;//callee
+
+unsigned int caller_id;
+unsigned int callee_id;
+
+struct nodelink *caller_node;
+struct nodelink *callee_node;
+
+SOCKET sck_p1;
+SOCKET sck_p2;
+int jam_p1p2;
+int jam_p2p1;
+char* buf_p1p2;
+char* buf_p2p1;
+unsigned int buflen_p1p2;
+unsigned int buflen_p2p1;
+unsigned int bufpos_p1p2;
+unsigned int bufpos_p2p1;
+struct cclink *next;
+struct cclink *prev;
+
+};
+
+struct newconn //структура, описывающая новое подключение (клиента)
+{
+SOCKET sck;
+struct sockaddr_in addr;
+clock_t tmst;
+int pos;
+unsigned char buf[NEWCONN_BUFSIZE];
+};
+
+struct nodelink
+{
+struct sockaddr_in addr;
+SOCKET sck;
+SSL *ssl;
+
+unsigned int addr_type;//тип адреса узла - PICA_PROTO_NEWNODE_IPV4,PICA_PROTE_NEWNODE_IPV6,...
+void *node_addr;//указатель на структуру адреса {PICA_nodeaddr_ipv4, PICA_nodeaddr_ipv6,PICA_nodeaddr_dns,..}
+
+unsigned char *r_buf;
+unsigned char *w_buf;
+unsigned int buflen_r;
+unsigned int buflen_w;
+unsigned int r_pos;
+unsigned int w_pos;
+
+struct nodelink *next;
+struct nodelink *prev;
+};
+
+#endif
diff --git a/PICA_nodeaddrlist.c b/PICA_nodeaddrlist.c
new file mode 100755
index 0000000..db942b6
--- /dev/null
+++ b/PICA_nodeaddrlist.c
@@ -0,0 +1,119 @@
+#include "PICA_nodeaddrlist.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sqlite3.h>
+
+struct cb_param
+{
+struct PICA_nodeaddr **n_addr;
+int *N;
+};
+
+static int nodeaddr_load_sqlite_cb(void *p, int argc, char **argv, char **ColName)
+{
+struct cb_param *cp = (struct cb_param*)p;
+struct PICA_nodeaddr *na;
+
+if (argc != 4)
+ return 1;
+
+na = (struct PICA_nodeaddr *)malloc(sizeof(struct PICA_nodeaddr));
+
+if (!na)
+ return 1;
+
+printf("NODE: addr: %s port %s\n", argv[0], argv[1]);//debug
+
+strncpy(na->addr, argv[0], 255);
+na->addr[255] = '\0';
+
+na->port = atoi(argv[1]);
+
+na->inactive_count = atoi(argv[2]);
+
+na->last_active = atoi(argv[3]);
+
+na->next = NULL;
+
+*(cp->n_addr) = na;
+(*(cp->N))++;
+
+return 0;
+}
+
+int PICA_nodeaddr_list_load(char* dbfilename,struct PICA_nodeaddr **list_head)
+{
+ int N=0, ret;
+sqlite3 *db;
+char *zErrMsg = 0;
+const char query[] = "select address, port, inactive_count, last_active from nodes order by inactive_count asc, last_active desc";
+struct cb_param p = {list_head, &N};
+
+
+ret = sqlite3_open(dbfilename, &db);
+
+if (ret != SQLITE_OK)
+ {
+ fprintf(stderr, "Can't open nodelist database: %s\n", sqlite3_errmsg(db));
+ sqlite3_close(db);
+ return -1;
+ }
+
+ret = sqlite3_exec(db, query, nodeaddr_load_sqlite_cb, &p, &zErrMsg);
+
+if( ret != SQLITE_OK )
+ {
+ fprintf(stderr, "SQL error: %s\n", zErrMsg);
+ sqlite3_free(zErrMsg);
+ }
+
+sqlite3_close(db);
+return N;
+}
+
+
+
+int PICA_nodeaddr_list_save(char* listfilename,struct PICA_nodeaddr *list_head)
+{
+/*
+FILE *f_list;
+struct PICA_nodeaddr *list_item;
+
+f_list=fopen(listfilename,"w");
+
+if (f_list==0)
+ {
+ puts("error opening node list for save");
+ //ERR_CHECK
+ return -1; // -1 коды ошибок
+ }
+
+list_item=list_head;
+while(list_item)
+ {
+ int ret;
+ ret=fprintf(f_list,"%s %u %u\n",list_item->addr,list_item->port,list_item->unavail_count);
+
+ if (ret<0)
+ return -1;
+
+ list_item=list_item->next;
+ }
+
+
+return fclose(f_list);*/
+}
+
+void PICA_nodeaddr_list_free(struct PICA_nodeaddr *list_head)
+{
+struct PICA_nodeaddr *pp,*pn;
+pp=list_head;
+
+while(pp)
+ {
+ pn=pp->next;
+ free(pp);
+ pp=pn;
+ }
+} \ No newline at end of file
diff --git a/PICA_nodeaddrlist.h b/PICA_nodeaddrlist.h
new file mode 100755
index 0000000..468dfbb
--- /dev/null
+++ b/PICA_nodeaddrlist.h
@@ -0,0 +1,44 @@
+// Описание структур и функций для работы со списком адресов узлов
+//
+#ifndef PICA_NODEADDRLIST_H
+#define PICA_NODEADDRLIST_H
+
+#include <netinet/in.h>
+
+struct PICA_nodeaddr_ipv4
+{
+int magick;
+in_addr_t addr;
+in_port_t port;//network byte order
+};
+struct PICA_nodeaddr_ipv6
+{
+int magick;
+struct in6_addr addr;
+in_port_t port;//network byte order
+};
+struct PICA_nodeaddr_dns
+{
+int magick;
+unsigned int addr_len;
+char addr[256];
+in_port_t port;//network byte order
+};
+
+struct PICA_nodeaddr
+{
+char addr[256];//макс. длина DNS имени 255 символов
+int inactive_count;//кол-во зафиксированных подряд случаев недоступности узла
+time_t last_active;//
+in_port_t port;//номер порта - host byte order
+
+struct PICA_nodeaddr *next;
+};
+
+int PICA_nodeaddr_list_load(char* dbfilename,struct PICA_nodeaddr **list_head);
+int PICA_nodeaddr_list_save(char* dbfilename,struct PICA_nodeaddr *list_head);
+int PICA_nodeaddr_list_add();//
+int PICA_nodeaddr_list_remove();//
+void PICA_nodeaddr_list_free(struct PICA_nodeaddr *list_head);
+
+#endif
diff --git a/PICA_nodejoinskynet.c b/PICA_nodejoinskynet.c
new file mode 100644
index 0000000..10e6206
--- /dev/null
+++ b/PICA_nodejoinskynet.c
@@ -0,0 +1,182 @@
+#define JOINSKYNET
+#include "PICA_node.h"
+#include "PICA_nodejoinskynet.h"
+#include "PICA_proto.h"
+#include "PICA_msgproc.h"
+#include "PICA_common.h"
+#include <time.h>
+#include <sys/time.h>
+
+#warning "USE HEADER VVV"
+struct nodelink *nodelink_list_addnew(struct newconn *nc);
+extern struct PICA_msginfo _msginfo_node[];
+struct PICA_proto_msg* nodelink_wbuf_push
+ (struct nodelink *nl,unsigned int msgid,unsigned int size);
+
+//Делает попытку подключения к указанному узлу, при удачном подключении отправляет узлу NODECONNREQ и возвращает 1. При неудаче возвращает 0
+//static int try_connect_to_node(struct PICA_nodeaddr *nap,struct newconn *nc)
+
+/*static*/ int try_connect_to_node(char *addr, unsigned int port,struct newconn *nc)
+{
+struct addrinfo h,*r,*ap;
+char portbuf[8];
+char msg[PICA_PROTO_NODECONNREQ_SIZE]=
+{PICA_PROTO_NODECONNREQ,PICA_PROTO_NODECONNREQ,
+PICA_PROTO_VER_HIGH,PICA_PROTO_VER_LOW};
+int ret;
+
+memset(&h,0,sizeof(struct addrinfo));
+
+#ifdef AI_IDN
+#define H_AI_FLAGS (AI_ADDRCONFIG | AI_IDN)
+#else
+#define H_AI_FLAGS AI_ADDRCONFIG
+#endif
+
+h.ai_flags=H_AI_FLAGS;
+h.ai_family=AF_INET;//CONF
+h.ai_socktype=SOCK_STREAM;
+h.ai_protocol=IPPROTO_TCP;
+
+sprintf(portbuf,"%u",port);
+
+if (0!=getaddrinfo(addr,portbuf,&h,&r))
+ return 0;
+
+ap=r;
+
+while(ap)
+ {
+ nc->sck=socket(ap->ai_family,ap->ai_socktype,ap->ai_protocol);
+ if (nc->sck==SOCKET_ERROR)
+ {
+ ap=ap->ai_next;
+ continue;
+ }
+
+ if (0!=connect(nc->sck,ap->ai_addr,ap->ai_addrlen))
+ {
+ ap=ap->ai_next;
+
+ CLOSE(nc->sck);
+ continue;
+ }
+
+ if (PICA_PROTO_NODECONNREQ_SIZE!=
+ send(nc->sck,msg,PICA_PROTO_NODECONNREQ_SIZE,0))
+ {
+ ap=ap->ai_next;
+ CLOSE(nc->sck);
+ }
+ else
+ break;
+ }
+
+if (ap)
+ {
+ nc->addr=*((struct sockaddr_in*)(ap->ai_addr));
+ freeaddrinfo(r);
+ return 1;
+ }
+
+freeaddrinfo(r);
+return 0;
+}
+
+/*static*/ int try_get_reply(struct newconn *nc)
+{
+int ret;
+fd_set fds;
+struct timeval tv={10,0};
+union _pv
+ {
+ int i;
+ char c[4];
+ }
+proto_version;
+
+proto_version.c[0]=PICA_PROTO_VER_HIGH;
+proto_version.c[1]=PICA_PROTO_VER_LOW;
+FD_ZERO(&fds);
+FD_SET(nc->sck,&fds);
+ret=select(nc->sck+1,&fds,0,0,&tv);
+if (0==ret)
+ return 0;
+if (-1==ret)
+ return 0;
+
+ret=recv(nc->sck,nc->buf,4,0);
+if (ret<=0)
+ return 0;
+
+nc->pos+=ret;
+if (!PICA_processdatastream(nc->buf,(unsigned int*)&nc->pos,&proto_version,_msginfo_node,2))
+ return 0;
+
+if (!proto_version.i) //server reply was ok
+ return 1;
+else
+ {
+ if (proto_version.c[0]> PICA_PROTO_VER_HIGH || (proto_version.c[1]> PICA_PROTO_VER_LOW && proto_version.c[0]== PICA_PROTO_VER_HIGH))
+ {
+ puts("make update of node software");//debug
+ }
+ else
+ {//peer node has older software version
+ puts("peer node has older protocol version");//debug
+ }
+ }
+return 0;
+}
+
+/*static*/ void send_newnode(struct nodelink *nlp, const char *my_addr)
+{
+struct PICA_proto_msg *mp;
+mp=nodelink_wbuf_push(nlp,PICA_PROTO_NEWNODE_IPV4,PICA_PROTO_NEWNODE_IPV4_SIZE);
+*((in_addr_t*)(mp->tail))=inet_addr(my_addr);//CONF TEMP
+*((uint16_t*)(mp->tail+4))=htons(PICA_COMM_PORT);
+}
+
+static void send_nodelistreq(struct nodelink *nlp)
+{
+struct PICA_proto_msg *mp;
+mp=nodelink_wbuf_push(nlp,PICA_PROTO_NODELISTREQ,PICA_PROTO_NODELISTREQ_SIZE);
+RAND_bytes(mp->tail, 2);
+}
+
+//Загружает из указанного файла addrlistfilename список адресов узлов, делает попытки подключения ко всем узлам, после удачного подключения к какому-либо из них запрашивает у узла актуальный список адресов узлов, после чего возвращает количество активных узлов в сети
+int PICA_node_joinskynet(char* addrlistfilename,const char *my_addr)
+{
+struct PICA_nodeaddr *nap,*addrlist_h=0;
+struct nodelink *nlp;
+struct newconn nc;
+clock_t tmst,t;
+int ret;
+
+ret=PICA_nodeaddr_list_load(addrlistfilename,&addrlist_h);//MEM
+if (ret<=0)
+ return ret;
+nap=addrlist_h;
+memset(&nc,0,sizeof(nc));
+while(nap)
+ {
+ if (try_connect_to_node(nap->addr,nap->port,&nc))
+ if (try_get_reply(&nc))
+ {
+ nlp=nodelink_list_addnew(&nc);
+ send_newnode(nlp,my_addr);
+ send_nodelistreq(nlp);
+ ret=1;
+ break;
+ }
+
+
+ CLOSE(nc.sck);
+ nc.pos=0;
+ nap=nap->next;
+ }
+
+PICA_nodeaddr_list_free(addrlist_h);
+return ret;
+}
+
diff --git a/PICA_nodejoinskynet.h b/PICA_nodejoinskynet.h
new file mode 100644
index 0000000..1ca2578
--- /dev/null
+++ b/PICA_nodejoinskynet.h
@@ -0,0 +1,17 @@
+
+#ifndef PICA_NODEJOINSKYNET_H
+#define PICA_NODEJOINSKYNET_H
+
+int PICA_node_joinskynet(char* addrlistfilename,const char *my_addr);
+int try_connect_to_node(char *addr, unsigned int port,struct newconn *nc);
+int try_get_reply(struct newconn *nc);
+void send_newnode(struct nodelink *nlp, const char *my_addr);
+
+#ifdef JOINSKYNET //include from PICA_nodejoinskynet.c, private data types definition
+#include "PICA_nodeaddrlist.h"
+
+
+#endif
+
+
+#endif
diff --git a/PICA_proto.h b/PICA_proto.h
new file mode 100755
index 0000000..9207ae8
--- /dev/null
+++ b/PICA_proto.h
@@ -0,0 +1,100 @@
+//коды сообщений протокола и структура сообщения
+
+#ifndef PICA_PROTO_H
+#define PICA_PROTO_H
+
+
+#define PICA_PROTO_VER_HIGH 0
+#define PICA_PROTO_VER_LOW 2
+
+#define PICA_PROTO_INITREQ 0xCA
+#define PICA_PROTO_INITRESPOK 0x01
+#define PICA_PROTO_VERDIFFER 0x02
+
+#define PICA_PROTO_CONNREQOUTG 0x03
+#define PICA_PROTO_CONNREQINC 0x04
+#define PICA_PROTO_NOTFOUND 0x05
+#define PICA_PROTO_CONNALLOW 0x06
+#define PICA_PROTO_FOUND 0x07
+#define PICA_PROTO_CONNDENY 0x08
+#define PICA_PROTO_CLNODELISTREQ 0x09
+#define PICA_PROTO_CLNODELIST 0x0A
+
+#define PICA_PROTO_CONNID 0x20
+
+#define PICA_PROTO_MSGUTF8 0x30
+#define PICA_PROTO_MSGOK 0x40
+
+#define PICA_PROTO_PINGREQ 0x68
+#define PICA_PROTO_PINGREP 0x69
+
+#define PICA_PROTO_NODECONNREQ 0xCB
+
+#define PICA_PROTO_NEWNODE_IPV4 0xA0
+#define PICA_PROTO_NEWNODE_IPV6 0xA1
+#define PICA_PROTO_NEWNODE_DNS 0xA2
+
+#define PICA_PROTO_NODELIST 0xA4
+#define PICA_PROTO_NODELISTREQ 0x90
+
+#define PICA_PROTO_SEARCH 0xA5
+#define PICA_PROTO_N2NFOUND 0xA8
+#define PICA_PROTO_N2NFOUNDCACHE 0xA9
+#define PICA_PROTO_N2NCONNREQOUTG 0xAA
+#define PICA_PROTO_N2NALLOW 0xAB
+#define PICA_PROTO_N2NNOTFOUND 0xB8
+#define PICA_PROTO_N2NMSG 0xD0
+
+
+//size
+#define PICA_PROTO_INITREQ_SIZE 4
+#define PICA_PROTO_INITRESPOK_SIZE 4
+#define PICA_PROTO_VERDIFFER_SIZE 4
+
+#define PICA_PROTO_CONNREQOUTG_SIZE 6
+#define PICA_PROTO_CONNREQINC_SIZE 6
+#define PICA_PROTO_NOTFOUND_SIZE 6
+#define PICA_PROTO_CONNALLOW_SIZE 6
+#define PICA_PROTO_FOUND_SIZE 6
+#define PICA_PROTO_CONNDENY_SIZE 6
+#define PICA_PROTO_CLNODELISTREQ_SIZE 4
+
+#define PICA_PROTO_CONNID_SIZE 10
+
+#define PICA_PROTO_MSGOK_SIZE 4
+
+#define PICA_PROTO_PINGREQ_SIZE 4
+#define PICA_PROTO_PINGREP_SIZE 4
+
+#define PICA_PROTO_NODECONNREQ_SIZE 4
+
+#define PICA_PROTO_NEWNODE_IPV4_SIZE 8
+#define PICA_PROTO_NEWNODE_IPV6_SIZE 20
+
+#define PICA_PROTO_NODELIST_ITEM_IPV4_SIZE 7
+#define PICA_PROTO_NODELIST_ITEM_IPV6_SIZE 19
+
+
+#define PICA_PROTO_NODELISTREQ_SIZE 4
+
+#define PICA_PROTO_SEARCH_SIZE 6
+#define PICA_PROTO_N2NFOUND_SIZE 6
+#define PICA_PROTO_N2NCONNREQOUTG_SIZE 10
+#define PICA_PROTO_N2NALLOW_SIZE 10
+#define PICA_PROTO_N2NNOTFOUND_SIZE 10
+
+#define PICA_PROTO_N2NMSG_MAXDATASIZE 65524
+#define PICA_PROTO_C2CMSG_MAXDATASIZE 65532
+
+
+struct PICA_proto_msg
+{
+unsigned char head[2];
+unsigned char tail[0];
+};
+
+
+
+
+#endif
+
diff --git a/build.txt b/build.txt
new file mode 100755
index 0000000..7382e4f
--- /dev/null
+++ b/build.txt
@@ -0,0 +1,10 @@
+gcc -I /mnt/win_c3/pract/home_mdv_root_jr/Downloads/openssl-0.9.8h/openssl-0.9.8h/include/ -L /usr/lib PICA_msgproc.c PICA_node.c -l ssl -l crypto -o node
+
+gcc -I /mnt/win_c3/pract/home_mdv_root_jr/Downloads/openssl-0.9.8h/openssl-0.9.8h/include/ -L /mnt/win_c3/pract/home_mdv_root_jr/Downloads/openssl-0.9.8h/openssl-0.9.8h/ PICA_client.c PICA_msgproc.c tests/test_client.c -l ssl -l crypto -o tests/test_client
+
+
+
+/home/root_jr/Downloads/openssl-0.9.8k/openssl-0.9.8k
+
+
+gcc -I /home/root_jr/Downloads/openssl-0.9.8k/openssl-0.9.8k/include/ -L /home/root_jr/Downloads/openssl-0.9.8k/openssl-0.9.8k/ PICA_client.c PICA_msgproc.c tests/test_client.c -l ssl -l crypto -o tests/test_client
diff --git a/doc/TASKS.txt b/doc/TASKS.txt
new file mode 100755
index 0000000..c4151cd
--- /dev/null
+++ b/doc/TASKS.txt
@@ -0,0 +1,78 @@
++ Написать отдельную функцию, которая добавляет сообщение в исходящий буфер для заданного клиента/узла
+
++ Расширение сервера до распределенного взаимодействия. Открыть еще один порт для входящих соед. от других узлов. Разработать структуру, опис. соединение с др. узлами. Написать функцию, загружающую список адресов узлов,выполняющую подключение к узлам и обновляющую список. Написать функцию работы с соединениями с другими узлами.
+
+- Регрессионные тесты
+
+- Список узлов. Разработать структуру элемента списка, функции для работы со списком, которые будут применяться клиентом и узлом.
+
+- Реализовать поддержку туннелей, как в SSH
+
+- Возле каждой функции в коде сделать ее описание. Делать описание для будущих функций
+
+- Сделать обработку ошибок
+
+-(+--) Упорядочить названия идентификаторов
+
+- Добавить поддержку IPv6
+
+-(+--) Реализовать обработку тайм-аутов
+
+- Счетчики трафика входящего и исходящего, активных пользователей
+
+- Привести в соответствие требованиям GNU coding standards( к приемлемым требованиям)
+
+
+- Адресные пространства
+
++ Удалить понятие "название сети" из протокола и кода.Инкрементировать версию протокола
+
++ Добавить в клиентскую библиотеку еще одну callback-функцию, сообщающую о неуспешной попытке создания канала./Разобраться с неконсистентностью освобождения ресурсов в (close_channel,close_connection,establish_data_connection)/
+
+- Соединения между узлами шифруются при помощи ADH (не нужно?)
+
++ Все входящие соединения узла через один TCP порт, а не 3
+
+- Очередь задач (соединиться с узлом и т.д)
+
+- Балансировка нагрузки между узлами (Узлы обмениваются количеством подключенных юзеров, потом самые нагруженные узлы посылают клиентам команду "сменить узел" с отсортированным списком N наименее нагруженных узлов)
+
+- автоматическая регистрация аккаунтов
+
+- сайт, багтрекер, VCS, форум, почтовая рассылка
+
++ сохранение открытых ключей контактов и их сопоставление при каждом сеансе
+
+- список контактов, кому разрешено видеть, когда абонент в сети
+
++ хранение списка узлов pica-node в базе sqlite
+
+- autotools
+
+- хранение недоставленных сообщений на стороне клиента, в базе sqlite, пока абонент не появится в сети
+
+- история сообщений, с опциональным шифрованием
+
+- проверка сертификата клиента узлом (узел должен иметь копию корневого сертификата)
+
+- переводы интерфейса
+
+- документация пользователя
+
+- текст лицензий (OpenSSL, iniparser, pica pica, Qt) в окошке about и файлах LICENSE
+
+- собрать и протестировать под Windows (32 и 64 бита)
+
+- аудио-видео звонки (ffmpeg)
+
+- передача файлов
+
+- возможность прямого P2P соединения между клиентами, клиент может выбирать предпочитаемый вариант (запретить P2P / использовать по возможности)
+
+- периодическая смена CA ключей и уничтожение старого приватного ключа, публикация списков выданных сертификатов для каждого ключа (???)
+
+- конфигурация узла (конфиг файл, опции командной строки, дефолтные значения)
+
+- пакеты для разных дистрибутивов (ebuild, deb, rpm)
+
+
diff --git a/doc/bugtraq.txt b/doc/bugtraq.txt
new file mode 100755
index 0000000..bf312f7
--- /dev/null
+++ b/doc/bugtraq.txt
@@ -0,0 +1,21 @@
+1. [V] Падение test_client при вызове SSL_free:
+
+puts("CACA_close_channel_chkp3");//debug
+
+if (chn->ssl)
+SSL_free(chn->ssl);<<<<
+
+после
+verify result:10 // просроченный корневой сертификат
+////////////
+причина - повторный вызов SSL_free
+(сначала в функции establish_data_connection, затем в CACA_close_channel)
+
+Баг исправлен путем помещения вызова CACA_close_channel в establish_data_connection в случае ошибки при создании канала. Также добавлена новая callback функция channel_failed, вызываемая в случае ошибки при создании канала
+
+==========================================================
+2. Авторизация клиента узлом без сертификата
+
+Клиент возможно может установить TLS соединение с узлом используя ADH схему. Узел
+может выпасть с ошибкой, если не удается получить сертификат клиента. Надо явно задавать
+набор разрешенных схем шифрования в клиенте и сервере
diff --git a/doc/concepts.txt b/doc/concepts.txt
new file mode 100755
index 0000000..2383e28
--- /dev/null
+++ b/doc/concepts.txt
@@ -0,0 +1 @@
+[Ïîèñê ñîáåñåäíèêà.Íåêîòîðûå ñîîáðàæåíèÿ ïî îáëåã÷åíèþ ïîèñêà]
diff --git a/doc/glossary.txt b/doc/glossary.txt
new file mode 100755
index 0000000..8d1c8b6
--- /dev/null
+++ b/doc/glossary.txt
@@ -0,0 +1 @@
+
diff --git a/doc/proto.txt b/doc/proto.txt
new file mode 100755
index 0000000..1390698
--- /dev/null
+++ b/doc/proto.txt
@@ -0,0 +1,148 @@
+Формат пакета
+[Заголовок] - 2 байта с одинаковым значением, определяющим тип сообщения
+[Хвост] - остальная часть пакета. Может быть пустым. Имеет либо фиксированную длину, заданную для данного сообщения,
+либо содержит в начале 1 или 2 байта, содержащих размер (в виде целого беззнакового числа, порядок байт-младший байт по младшему адресу) остальной части, не включая байты размера
+
+Протокол авторизации и пересылки данных (между сервером и клиентом)
+
++++[запрос на подключение] -клиент
+0xCA 0xCA Ver_major Ver_minor - версия протокола клиента
+-4 байта
+
+===============новый вариант
++++[ответ сервера] - сервер
+0x01 0x01 'O' 'K'
+===============новый вариант
+При удачном прохождении проверки клиент ожидает инициирования защищенного соединения по протоколу TLS, выступая в роли сервера. Сервер выступает в роли TLS-клиента.
+
+
++++[различные версии протокола] -сервер
+0x02 0x02 Ver_major Ver_minor - версия протокола сервера
+
+после отправки данного пакета сервер разрывает соединение.
+
+ Нижеследующие сообщения передаются через защищенное управляющее соединение клиента с сервером.
+|КЛИЕНТ-СЕРВЕР|
+
++++[запрос на установление соединения с собеседником] - клиент, по установленному защищенному соединению
+0x03 0x03 (UIN вызываемого пользователя, в формате little-endian)
+
++++[запрос на разрешение соединения] - сервер, по установленному защищенному соединению
+0x04 0x04 (UIN инициатора, в формате little-endian)
+
++++[пользователь с данным UIN не найден (в оффлайне, отказался от разговора)]
+- сервер, по установленному защищенному соединению
+0x05 0x05 (UIN вызываемого пользователя, в формате little-endian)
+
++++[разрешение соединения от вызываемого пользователя] -вызываемый клиент, по установленному защищенному соединению
+0x06 0x06 (UIN вызывающего пользователя, в формате little-endian)
+после отправки данного сообщения вызваемый клиент осуществляет подключение к серверу на порт обмена данными (_DATA_PORT) и отправляет через это соединение сообщение 0x20 -идентификация нового соединения
+
++++[пользователь с данным UIN найден и готов общаться] - сервер вызывающему клиенту
+0x07 0x07 (UIN вызываемого пользователя, в формате little-endian)
+
++++[отказ от общения]-вызываемый клиент
+0x08 0x08 (UIN вызывающего пользователя, в формате little-endian)
+
++++[запрос списка узлов] - клиент
+0x09 0x09 (2 случайных байта)
+
++++[список узлов] - сервер
+0x0A 0x0A -- см формат 0xA4
+
+ Нижеследующие сообщения передаются через соединение сервера с клиентом для передачи данных
+
++++[идентификация нового соединения] - клиент
+0x20 0x20 (UIN вызывающего пользователя) (UIN вызываемого пользователя)
+
+сервер при получении данного сообщения проверяет UINы. При несовпадении соединение разрывается. После проверки сервер переходит в режим пересылки данных между двумя клиентами. Вызывающий клиент инициирует установку соединения по протоколу TLS
+
+
+
+Протокол обмена сообщениями (между клиентами через сервер(ы) ) |КЛИЕНТ-КЛИЕНТ|
+
++++[сообщение - текст в кодировке UTF8]
+0x30 0x30 (длина сообщения в байтах- 2 байта в формате little-endian) сообщение в кодировке UTF8
+
++++[отчет о доставке предыдущего сообщения]
+0x40 0x40 (2 случайных байта)
+
+
+Пинг
+
++++[пинг-запрос] - может быть отправлен любой стороной, как по управляющему соединению, так и по межклиентскому
+0x68 0x68 (2 случайных байта)
+
++++[пинг-ответ]
+0x69 0x69 (2 случайных байта)
+
+
+
+Протокол межсерверного взаимодействия | СЕРВЕР-СЕРВЕР (УЗЕЛ-УЗЕЛ)
+
++++[запрос на подключение сервера] -узел узлу
+0xCB 0xCB Ver_major Ver_minor - версия протокола узла
+
+(варианты ответа сервера - 0x01,0x02 - те же, что и для клиента)
+
++++[анонс нового узла - IPv4 адрес]
+0xA0 0xA0 | IP -адрес нового узла - 4 байта, сетевой порядок байт| номер порта - 2 байта, сетевой порядок байт|
+
++++[анонс нового узла - IPv6 адрес]
+0xA1 0xA1 | IPv6 -адрес нового узла - 16 байт, сетевой порядок байт| номер порта - 2 байта, сетевой порядок байт|
+
++++[анонс нового узла - DNS адрес]
+0xA2 0xA2 | длина адреса - 1 байт | DNS адрес нового узла| номер порта - 2 байта, сетевой порядок байт|
+
+Заявленный в анонсе адрес должнен соответствовать адресу, с которого произошло подключение. Получивший анонс узел должен это проверить.
+
++++[список узлов]
+0xA4 0xA4 | размер списка адресов в байтах, 2 байта - 0..65532| список адресов
+список адресов представляет собой последовательность из элементов 3 возможных типов
+|0xA0|IP -адрес - 4 байта | номер порта - 2 байта| - 7 байт
+|0xA1|IPv6 -адрес | номер порта - 2 байта|- 19 байт
+|0xA2|длина адреса - 1байт| DNS адрес| номер порта - 2 байта| 4 ... 259байт
+
+Первым в списке всегда идет собственный адрес узла-отправителя (в данном адресном пространстве).
+Сообщение может содержать не весь список узлов, а только часть. Весь список в таком случае будет пересылаться по частям. Адреса и номера портов имеют "сетевой" порядок байтов.
+Размер всего пакета не превышает 64кб
+
++++[запрос списка узлов] @@@
+0x90 0x90 (2 случайных байта)
+
+
++++[поиск клиента] - отправляется всем узлам. Узел-получатель отвечает на это сообщение, только если искомый UIN найден среди локальных клиентов либо в кэше
+0xA5 0xA5 (UIN вызываемого пользователя, в формате little-endian)
+
++++[результат поиска клиента-клиент найден ] - отправляется узлом, получившим 0xA5, если искомый UIN найден среди локальных подключенных клиентов
+0xA8 0xA8 (UIN вызываемого пользователя, в формате little-endian)
+
++++[результат поиска клиента-клиент найден в кэше ] - отправляется узлом, получившим 0xA5, если искомый UIN найден в кэше (т.е есть информация о подключении искомого клиента к другому узлу)
+0xA9 0xA9 (размер - два байта, [13-263] )(UIN вызываемого пользователя, в формате little-endian) (адрес узла, на котором может быть искомый UIN, в формате A0|A1|A2 см. описание 0xA4)
+
+
++++[запрос на разрешение соединения c клиентом узла-получателя]
+0xAA 0xAA (UIN инициатора, в формате little-endian) (UIN вызываемого пользователя, в формате little-endian)
+
++++[разрешение соединения от вызываемого пользователя]-отправляется после того, как вызываемый локальный клиент ответил сообщением 0x06 и создал новое соединение для обмена сообщениями
+0xAB 0xAB (UIN инициатора, в формате little-endian) (UIN вызываемого пользователя, в формате little-endian)
+
++++[клиент не найден (в оффлайне, отказался от разговора)]
+отправляется узлом в ответ на сообщение 0xD0, адресованное данному UIN-у,
+если данный пользователь уже отключился или его нету в списке локальных клиентов,
+либо в ответ на 0xAA, если вызываемый клиент отказался от соединения.
+0xB8 0xB8 (UIN инициатора, в формате little-endian)(UIN вызываемого пользователя, в формате little-endian)
+
++++[сообщение, передаваемое клиентами между узлами]
+0xD0 0xD0 (длина вложенного сообщения в байтах [8 - 65532] , - 2 байта в формате little-endian) (UIN локального пользователя-отправителя) (UIN пользователя на другом узле) вложенные данные
+
+вложенные данные - данные передаваемые через соединение КЛИЕНТ-КЛИЕНТ
+
+
+
+
+
+
+
+
+
diff --git a/macros.txt b/macros.txt
new file mode 100755
index 0000000..0f4d939
--- /dev/null
+++ b/macros.txt
@@ -0,0 +1,3 @@
+WIN32
+NO_RAND_DEV
+BIG_ENDIAN
diff --git a/pica-client/accounts.cpp b/pica-client/accounts.cpp
new file mode 100644
index 0000000..e9cf0e9
--- /dev/null
+++ b/pica-client/accounts.cpp
@@ -0,0 +1,107 @@
+#include "accounts.h"
+#include <QFile>
+
+Accounts::Accounts(QString storage)
+{
+ dbconn=QSqlDatabase::addDatabase("QSQLITE");
+ dbconn.setDatabaseName(storage);
+
+ if (!dbconn.open())
+ {
+ lasterr=dbconn.lastError();
+ return;
+ }
+
+ QSqlQuery query;
+
+ query.exec("PRAGMA foreign_keys=ON;");
+
+ //check result, if foreign keys support is on
+}
+
+void Accounts::Add(AccountRecord &acc)
+{
+ QSqlQuery query;
+
+ query.prepare("insert into accounts (id, name, cert_file, pkey_file, ca_file) values (:id, :name, :cert_file, :pkey_file, :ca_file);");
+ query.bindValue(":id",acc.id);
+ query.bindValue(":name",acc.name);
+ query.bindValue(":cert_file",acc.cert_file);
+ query.bindValue(":pkey_file",acc.pkey_file);
+ query.bindValue(":ca_file",acc.CA_file);
+ query.exec();
+
+ lasterr=query.lastError();
+}
+
+void Accounts::Delete(quint32 id)
+{
+ QSqlQuery query;
+
+ query.prepare("delete from accounts where id=:id");
+ query.bindValue(":id",id);
+ query.exec();
+
+ lasterr=query.lastError();
+}
+
+QList<Accounts::AccountRecord> Accounts::GetAccounts()
+{
+ AccountRecord r;
+ QList<Accounts::AccountRecord> L;
+ QSqlQuery query;
+
+ //
+ query.setForwardOnly(true);
+ query.exec("select id, name, cert_file, pkey_file, ca_file from accounts");
+ lasterr=query.lastError();
+
+ if (lasterr.isValid())
+ return L;
+
+ while(query.next())
+ {
+ r.id = query.value(0).toUInt();
+ r.name = query.value(1).toString();
+ r.cert_file = query.value(2).toString();
+ r.pkey_file = query.value(3).toString();
+ r.CA_file = query.value(4).toString();
+
+ L<<r;
+ }
+
+ return L;
+}
+
+bool Accounts::isOK()
+{
+ return !lasterr.isValid();
+}
+
+QString Accounts::GetLastError()
+{
+ return lasterr.text();
+}
+
+bool Accounts::CheckFiles(AccountRecord &acc, QString &error_string)
+{
+ if (!QFile::exists(acc.cert_file))
+ {
+ error_string = QObject::tr("Certificate file %1 does not exist").arg(acc.cert_file);
+ return false;
+ }
+
+ if (!QFile::exists(acc.pkey_file))
+ {
+ error_string = QObject::tr("Private key file %1 does not exist").arg(acc.pkey_file);
+ return false;
+ }
+
+ if (!QFile::exists(acc.CA_file))
+ {
+ error_string = QObject::tr("Certificate Authority file %1 does not exist").arg(acc.CA_file);
+ return false;
+ }
+
+ return true;
+}
diff --git a/pica-client/accounts.h b/pica-client/accounts.h
new file mode 100644
index 0000000..1205583
--- /dev/null
+++ b/pica-client/accounts.h
@@ -0,0 +1,35 @@
+#ifndef ACCOUNTS_H
+#define ACCOUNTS_H
+
+#include <QString>
+#include <QtSql>
+#include <QSqlDatabase>
+#include <QStringList>
+
+class Accounts
+{
+public:
+ struct AccountRecord
+ {
+ quint32 id;
+ QString name;
+ QString cert_file;
+ QString pkey_file;
+ QString CA_file;
+ };
+
+ Accounts(QString storage);
+ void Add(AccountRecord& acc);
+ void Delete(quint32 id);
+ QList<AccountRecord> GetAccounts();
+ bool isOK();
+ QString GetLastError();
+
+ static bool CheckFiles(AccountRecord& acc, QString &error_string);
+
+private:
+ QSqlDatabase dbconn;
+ QSqlError lasterr;
+};
+
+#endif // ACCOUNTS_H
diff --git a/pica-client/accountswindow.cpp b/pica-client/accountswindow.cpp
new file mode 100644
index 0000000..151e366
--- /dev/null
+++ b/pica-client/accountswindow.cpp
@@ -0,0 +1,205 @@
+#include "accountswindow.h"
+#include <QVBoxLayout>
+#include "mainwindow.h"
+#include "globals.h"
+#include "dialogs/addaccountdialog.h"
+#include "../PICA_client.h"
+#include "skynet.h"
+#include <QRegExp>
+#include <QMessageBox>
+#include <QApplication>
+
+AccountsWindow::AccountsWindow(QWidget *parent) :
+ QMainWindow(parent),
+ accounts(config_dbname)
+{
+ QVBoxLayout *layout;
+ layout = new QVBoxLayout;
+
+ cb_accounts = new QComboBox();
+ bt_login = new QPushButton(tr("Log In"));
+ bt_registernew = new QPushButton(tr("Register New Account..."));
+ bt_addimport = new QPushButton(tr("Add (Import) existing account..."));
+ bt_delete = new QPushButton(tr("Delete Account"));
+
+ {
+ QFont f;
+ f= bt_login->font();
+ f.setBold(true);
+ bt_login->setFont(f);
+ }
+
+ layout->addWidget(cb_accounts);
+ layout->addWidget(bt_login);
+ layout->addWidget(bt_registernew);
+ layout->addWidget(bt_addimport);
+ layout->addWidget(bt_delete);
+
+ QWidget *central = new QWidget(this);
+ setCentralWidget(central);
+
+ centralWidget()->setLayout(layout);
+
+ setWindowIcon(picapica_ico_sit);
+
+ connect(bt_login,SIGNAL(clicked()),this,SLOT(login_click()));
+ connect(bt_registernew,SIGNAL(clicked()),this,SLOT(register_click()));
+ connect(bt_addimport,SIGNAL(clicked()),this,SLOT(add_click()));
+ connect(bt_delete,SIGNAL(clicked()),this,SLOT(delete_click()));
+
+ LoadAccounts();
+}
+
+void AccountsWindow::LoadAccounts()
+{
+ cb_accounts->clear();
+ L.clear();
+ L=accounts.GetAccounts();
+
+ if (0==L.count())
+ {
+ cb_accounts->addItem(tr(" ---No Accounts --"));
+
+ bt_login->setDisabled(true);
+ }
+ else
+ {
+ for (int i=0;i<L.count();i++)
+ cb_accounts->addItem(QString("(%1) %2").arg(L[i].id).arg(L[i].name),L[i].id);
+
+ bt_login->setEnabled(true);
+ }
+}
+
+void AccountsWindow::login_click()
+{
+
+ account_id = (cb_accounts->itemData(cb_accounts->currentIndex())).toUInt();
+
+ int L_index;
+
+ for (int i=0;i<L.count();i++)
+ {
+ if (L[i].id==account_id)
+ {
+ L_index = i;
+ break;
+ }
+ }
+
+ {
+ QString error_str;
+ if (!Accounts::CheckFiles(L[L_index],error_str))
+ {
+ QMessageBox mbx;
+ mbx.setText(error_str);
+ mbx.exec();
+ return;
+ }
+ }
+ skynet->Join(L[L_index]);
+
+ mainwindow = new MainWindow();
+ mainwindow->show();
+ this->hide();
+
+}
+
+void AccountsWindow::register_click()
+{
+
+}
+
+void AccountsWindow::add_click()
+{
+ AddAccountDialog d;
+ d.exec();
+
+ if (d.result())
+ {
+ quint32 id;
+
+ PICA_get_id_from_cert(d.GetCertFilename().toUtf8().constData(),&id);
+
+ //
+ QProcess openssl;
+
+ openssl.start("openssl",QStringList()<<"x509"<<"-in"<<d.GetCertFilename()<<"-subject"<<"-nameopt"<<"oneline,-esc_msb,utf8"<<"-noout");
+ if (!openssl.waitForStarted())
+ return;
+
+ if (!openssl.waitForFinished())
+ return;
+
+ QByteArray result = openssl.readAll();
+ QRegExp rx("CN = [0-9]+\\#([^\\s\\#]+)");
+
+
+ QString name=QString::fromUtf8( result.constData());
+
+ if (name.contains(rx))
+ {
+ name=rx.cap(1);
+ }
+
+ //
+ Accounts::AccountRecord rec;
+ QString certfilename,pkeyfilename;
+
+ if (d.isCopyFilesChecked())
+ {
+ QDir dir(config_dir);
+ dir.mkdir(QString::number(id));
+ dir.cd(QString::number(id));
+
+ certfilename=dir.absolutePath()+"/"+QString::number(id)+"_cert.pem";
+ pkeyfilename=dir.absolutePath()+"/"+QString::number(id)+"_pkey.pem";
+
+ QFile::copy(d.GetCertFilename(),certfilename);
+ QFile::copy(d.GetPkeyFilename(),pkeyfilename);
+ }
+ else
+ {
+ certfilename = d.GetCertFilename();
+ pkeyfilename = d.GetPkeyFilename();
+ }
+
+ rec.id=id;
+ rec.name=name;
+ rec.cert_file=certfilename;
+ rec.pkey_file=pkeyfilename;
+ rec.CA_file= config_defaultCA;
+
+ accounts.Add(rec);
+
+ if (!accounts.isOK())
+ {
+ QMessageBox mbx;
+ mbx.setText(accounts.GetLastError());
+ mbx.exec();
+ }
+ LoadAccounts();
+ }
+}
+
+void AccountsWindow::delete_click()
+{
+ quint32 id = (cb_accounts->itemData(cb_accounts->currentIndex())).toUInt();
+
+ if (QMessageBox::No==
+ QMessageBox::question(this,tr("Deleting account"),tr("Are you sure you want to delete account %1 ?").arg(id),QMessageBox::Yes,QMessageBox::No))
+ return;
+
+ //TODO ask user if he wants to wipe certificate and private key too
+
+ accounts.Delete(id);
+
+ LoadAccounts();
+
+}
+
+void AccountsWindow::closeEvent(QCloseEvent *e)
+{
+ QApplication::instance()->quit();
+ QMainWindow::closeEvent(e);
+}
diff --git a/pica-client/accountswindow.h b/pica-client/accountswindow.h
new file mode 100644
index 0000000..30e3c0d
--- /dev/null
+++ b/pica-client/accountswindow.h
@@ -0,0 +1,43 @@
+#ifndef ACCOUNTSWINDOW_H
+#define ACCOUNTSWINDOW_H
+
+#include <QMainWindow>
+#include <QComboBox>
+#include <QPushButton>
+#include "accounts.h"
+#include <QList>
+
+class AccountsWindow : public QMainWindow
+{
+ Q_OBJECT
+public:
+ explicit AccountsWindow(QWidget *parent = 0);
+
+private:
+ QComboBox *cb_accounts;
+ QPushButton *bt_login;
+ QPushButton *bt_registernew;
+ QPushButton *bt_addimport;
+ QPushButton *bt_delete;
+
+ Accounts accounts;
+ QList<Accounts::AccountRecord> L;
+
+ void LoadAccounts();
+
+ void closeEvent(QCloseEvent *);
+
+
+signals:
+
+public slots:
+
+private slots:
+ void login_click();
+ void register_click();
+ void add_click();
+ void delete_click();
+
+};
+
+#endif // ACCOUNTSWINDOW_H
diff --git a/pica-client/askpassword.cpp b/pica-client/askpassword.cpp
new file mode 100644
index 0000000..0629502
--- /dev/null
+++ b/pica-client/askpassword.cpp
@@ -0,0 +1,52 @@
+#include "askpassword.h"
+#include "globals.h"
+#include <string.h>
+#include <QInputDialog>
+
+QMutex AskPassword::passwd_mutex;
+QMap<quint32,QString> AskPassword::idpasswd;
+
+AskPassword::AskPassword(QObject *parent) :
+ QObject(parent)
+{
+ connect(this,SIGNAL(password_requested()),this,SLOT(password_dialog()),Qt::BlockingQueuedConnection);
+}
+
+int AskPassword::ask_password_cb(char *buf, int size, int rwflag, void *userdata)
+{
+ QString pswd;
+ quint32 id = *((quint32*)userdata);
+
+ passwd_mutex.lock();
+
+ if (idpasswd.contains(id))
+ {
+ pswd = idpasswd[id];
+ }
+ else
+ {
+ askpassword->emit_sig();
+ pswd = idpasswd[id];
+ }
+ passwd_mutex.unlock();
+
+ strncpy(buf, pswd.toUtf8().constData(), size);
+ buf[size - 1] = 0;
+ return(strlen(buf));
+}
+
+void AskPassword::emit_sig()
+{
+ emit password_requested();
+}
+
+void AskPassword::password_dialog()
+{
+ bool ok;
+ QString title = QObject::tr("Enter private key passphrase");
+ QString text = QObject::tr(QString("Passphrase for (%1)").arg(account_id).toUtf8().constData());
+ QString psw = QInputDialog::getText((QWidget*)mainwindow,title,text,QLineEdit::Password,QString(),&ok);
+ if (ok)
+ idpasswd[account_id] = psw;
+}
+
diff --git a/pica-client/askpassword.h b/pica-client/askpassword.h
new file mode 100644
index 0000000..c841693
--- /dev/null
+++ b/pica-client/askpassword.h
@@ -0,0 +1,27 @@
+#ifndef ASKPASSWORD_H
+#define ASKPASSWORD_H
+
+#include <QObject>
+#include <QMap>
+#include <QMutex>
+
+class AskPassword : public QObject
+{
+ Q_OBJECT
+public:
+ explicit AskPassword(QObject *parent = 0);
+ static int ask_password_cb(char *buf, int size, int rwflag, void *userdata);
+
+private:
+ void emit_sig();
+ static QMap<quint32,QString> idpasswd;
+ static QMutex passwd_mutex;
+
+signals:
+ void password_requested();
+
+public slots:
+ void password_dialog();
+};
+
+#endif // ASKPASSWORD_H
diff --git a/pica-client/chatwindow.cpp b/pica-client/chatwindow.cpp
new file mode 100644
index 0000000..dd495ae
--- /dev/null
+++ b/pica-client/chatwindow.cpp
@@ -0,0 +1,140 @@
+#include "chatwindow.h"
+#include "globals.h"
+#include <QVBoxLayout>
+#include <QDateTime>
+#include <QAction>
+#include <QKeyEvent>
+#include <QMessageBox>
+
+ChatWindow::ChatWindow(quint32 peer_id) :
+ QWidget(0), peer_id_(peer_id)
+{
+ QVBoxLayout *layout;
+
+ layout = new QVBoxLayout;
+
+ chatw = new QTextEdit(this);
+ sendtextw = new TextSend(this);
+
+ connect(sendtextw, SIGNAL(send_pressed()), this, SLOT(send_message()));
+
+ sendtextw->setMinimumHeight(48);
+ sendtextw->setMinimumWidth(240);
+ sendtextw->setAcceptRichText(false);
+
+ chatw->setReadOnly(true);
+
+ layout->addWidget(chatw, 3);
+ layout->addWidget(sendtextw, 1, Qt::AlignBottom);
+
+ setLayout(layout);
+
+ setWindowTitle(QString::number(peer_id));
+ setWindowIcon(picapica_ico_sit);
+
+ QWidget::setAttribute(Qt::WA_DeleteOnClose);
+ QWidget::setAttribute(Qt::WA_QuitOnClose, false);
+}
+
+void ChatWindow::put_message(QString msg, quint32 id, bool is_me)
+{
+ QString color;
+ QTextCharFormat fmt;
+
+ if (is_me)
+ color = "#0000ff";
+ else
+ color = "#ff0000";
+
+ QTextCursor c(chatw->document());
+
+ c.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
+
+ if (is_me)
+ undelivered_msgs.append(c.position());
+
+ fmt = chatw->currentCharFormat();
+ chatw->insertHtml(
+ QString("<b><font color=%1>&nbsp;(%2) %3 : </font></b>").
+ arg(color).
+ arg(QDateTime::currentDateTime().toString()).
+ arg(QString::number(id))
+ );
+ chatw->setCurrentCharFormat(fmt);
+ chatw->insertPlainText(msg + '\n');
+
+ chatw->moveCursor(QTextCursor::End, QTextCursor::KeepAnchor);
+}
+
+void ChatWindow::msg_from_peer(QString msg)
+{
+ if (isMinimized())
+ showNormal();
+
+ put_message(msg, peer_id_, false);
+}
+
+void ChatWindow::msg_delivered()
+{
+ QTextCursor c(chatw->document());
+ c.setPosition(undelivered_msgs.first());
+ c.deleteChar();
+ c.insertText("+");
+ undelivered_msgs.removeFirst();
+}
+
+void ChatWindow::msg_informational(QString text)
+{
+ QString color;
+ QTextCharFormat fmt;
+
+ color = "#303030";
+
+ fmt = chatw->currentCharFormat();
+
+ chatw->insertHtml(
+ QString("<i><font color=%1>(%2)</font></i>").
+ arg(color).
+ arg(text)
+ );
+ chatw->insertPlainText("\n");
+
+ chatw->setCurrentCharFormat(fmt);
+ chatw->moveCursor(QTextCursor::End, QTextCursor::KeepAnchor);
+}
+
+void ChatWindow::send_message()
+{
+ put_message(sendtextw->toPlainText(), account_id, true);
+
+ emit msg_input(sendtextw->toPlainText(), this);
+
+ sendtextw->clear();
+}
+
+void ChatWindow::closeEvent(QCloseEvent *e)
+{
+ emit chatwindow_close(this);
+
+ e->accept();
+}
+
+TextSend::TextSend(QWidget *parent) :
+ QTextEdit(parent)
+{
+
+}
+
+void TextSend::keyPressEvent(QKeyEvent *e)
+{
+ if (e->key() == Qt::Key_Return && e->modifiers() == Qt::NoModifier )
+ {
+ emit send_pressed();
+ return;
+ }
+
+ QTextEdit::keyPressEvent(e);
+}
+
+
+
diff --git a/pica-client/chatwindow.h b/pica-client/chatwindow.h
new file mode 100644
index 0000000..1babd31
--- /dev/null
+++ b/pica-client/chatwindow.h
@@ -0,0 +1,53 @@
+#ifndef CHATWINDOW_H
+#define CHATWINDOW_H
+
+#include <QWidget>
+#include <QTextEdit>
+#include <QList>
+
+class TextSend : public QTextEdit
+{
+ Q_OBJECT
+public:
+ explicit TextSend(QWidget *parent);
+signals:
+ void send_pressed();
+private:
+ void keyPressEvent(QKeyEvent *e);
+
+};
+
+class ChatWindow : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit ChatWindow(quint32 peer_id);
+
+ quint32 getPeerId() { return peer_id_;};
+
+signals:
+ void msg_input(QString msg, ChatWindow *sender_window);
+ void chatwindow_close(ChatWindow *sender_window);
+
+public slots:
+ void msg_from_peer(QString msg);
+ void msg_delivered();
+ void msg_informational(QString text);//юзер в оффлайне, набирает сообщение, и т.д. серым шрифтом, курсив
+
+
+private:
+ QTextEdit *chatw;
+ TextSend *sendtextw;
+ quint32 peer_id_;
+ QList<int> undelivered_msgs;
+
+
+ void put_message(QString msg, quint32 id, bool is_me);
+
+ void closeEvent(QCloseEvent *e);
+
+private slots:
+ void send_message();
+};
+
+#endif // CHATWINDOW_H
diff --git a/pica-client/contactlistwidget.cpp b/pica-client/contactlistwidget.cpp
new file mode 100644
index 0000000..bff36bd
--- /dev/null
+++ b/pica-client/contactlistwidget.cpp
@@ -0,0 +1,188 @@
+#include "contactlistwidget.h"
+#include "globals.h"
+#include "dialogs/viewcertdialog.h"
+#include "msguirouter.h"
+#include <QMenu>
+#include <QMessageBox>
+#include <QContextMenuEvent>
+#include <QInputDialog>
+
+ContactListWidget::ContactListWidget(QWidget *parent) :
+ QListWidget(parent)
+{
+
+ addcontactAct = new QAction(tr("&Add Contact..."), this);
+ addcontactAct->setStatusTip(tr("add new contact"));
+ connect(addcontactAct,SIGNAL(triggered()),this,SLOT(add_contact()));
+
+
+ delcontactAct = new QAction(tr("&Delete Contact"),this);
+ connect(delcontactAct,SIGNAL(triggered()),this,SLOT(del_contact()));
+
+ startchatAct = new QAction(tr("Start &Chat"), this);
+ connect(startchatAct, SIGNAL(triggered()), this, SLOT(start_chat()));
+
+ viewcertAct = new QAction(tr("&View Certificate"), this);
+ connect(viewcertAct, SIGNAL(triggered()), this, SLOT(view_cert()));
+
+ connect(this, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(start_chat()));
+
+ setContactsStorage(new Contacts(config_dbname, account_id));
+}
+
+ContactListWidget::~ContactListWidget()
+{
+ delete storage;
+}
+
+
+void ContactListWidget::contextMenuEvent(QContextMenuEvent *event)
+{
+ QMenu menu(this);
+
+ if (this->itemAt(event->pos()))
+ menu.addAction(delcontactAct);
+
+ menu.addAction(addcontactAct);
+ menu.addAction(startchatAct);
+ menu.addAction(viewcertAct);
+ menu.exec(event->globalPos());
+}
+
+void ContactListWidget::setContactsStorage(Contacts *ct)
+{
+ clear();
+ // this->addItems(ct->GetContacts());
+ storage=ct;
+ contact_records=ct->GetContacts();
+
+ for (int i=0;i<contact_records.size();i++)
+ {
+ addItem(QString("(%1) ").arg(contact_records[i].id)+contact_records[i].name);
+ wgitem_to_recs[this->item(i)]=&contact_records[i];
+ this->item(i)->setStatusTip(QString().setNum(contact_records[i].id));
+ }
+}
+
+void ContactListWidget::add_contact()
+{
+ bool ok;
+ QString input=QInputDialog::getText(this,tr("Add New Contact"),tr("User ID: "),QLineEdit::Normal,NULL,&ok);
+
+ if (ok && !input.isEmpty())
+ {
+ quint32 user_id=input.toUInt(&ok);
+
+ if (!ok)
+ {
+ QMessageBox mbx;
+ mbx.setIcon(QMessageBox::Warning);
+ mbx.setText(tr("Invalid user ID"));
+ mbx.exec();
+ return;
+ }
+
+ for(int i=0;i<contact_records.size();i++)
+ if (contact_records[i].id==user_id)
+ {
+ QMessageBox mbx;
+ mbx.setIcon(QMessageBox::Warning);
+ mbx.setText(tr("User \"(%1) %2\" already exists").arg(user_id).arg(contact_records[i].name));
+ mbx.exec();
+ return;
+ }
+
+ Contacts::ContactRecord r;
+
+ r.id=user_id;
+ r.name="";
+ //void ContactListWidget::debug_onmsginput(QString msg, ChatWindow *sender_window)
+ //{
+ // skynet->SendMessage(sender_window->getPeerId(), msg);
+ //}
+
+ storage->Add(user_id);
+
+ if (!storage->isOK())
+ {
+ QMessageBox mbx;
+ mbx.setIcon(QMessageBox::Warning);
+ mbx.setText(storage->GetLastError());
+ mbx.exec();
+ return;
+ }
+
+ addItem(QString("(%1) ").arg(user_id));
+ contact_records.append(r);
+ wgitem_to_recs[this->item(this->count()-1)]=&contact_records.last();
+
+ }
+
+}
+
+void ContactListWidget::del_contact()
+{
+QMessageBox mbx;
+mbx.setText(QString(tr("Are you sure you want to remove user \"%1\" from your contact list?")).arg(currentItem()->text()));
+mbx.setIcon(QMessageBox::Question);
+mbx.addButton(QMessageBox::Yes);
+mbx.addButton(QMessageBox::No);
+mbx.setDefaultButton(QMessageBox::No);
+
+if (QMessageBox::Yes==mbx.exec())
+ {
+ quint32 rm_id;
+ QListWidgetItem *rm_ptr;
+
+ storage->Delete(rm_id = wgitem_to_recs[currentItem()]->id);
+ delete (rm_ptr=takeItem(currentRow()));
+
+ for(int i=0;i<contact_records.size();i++)
+ {
+ if (contact_records[i].id==rm_id)
+ {
+ contact_records.removeAt(i);
+ break;
+ }
+ }
+
+ wgitem_to_recs.remove(rm_ptr);
+ }
+
+}
+
+void ContactListWidget::start_chat()
+{
+// ChatWindow *cw_debug_remove;
+
+// (cw_debug_remove = new ChatWindow(wgitem_to_recs[currentItem()]->id)) ->show();//debug
+
+// connect(cw_debug_remove, SIGNAL(msg_input(QString,ChatWindow*)), this, SLOT(debug_onmsginput(QString, ChatWindow*)));
+
+ msguirouter->start_chat(wgitem_to_recs[currentItem()]->id);
+}
+
+void ContactListWidget::view_cert()
+{
+ QString cert_pem = storage->GetContactCert(wgitem_to_recs[currentItem()]->id);
+
+ if (!storage->isOK())
+ {
+ QMessageBox mbx;
+ mbx.setText(storage->GetLastError());
+ mbx.exec();
+ }
+
+ if (!cert_pem.isEmpty())
+ {
+ ViewCertDialog vcd;
+ vcd.SetCert(cert_pem);
+ vcd.exec();
+ }
+ else
+ {
+ QMessageBox mbx;
+ mbx.setText(tr("Certificate is not stored for this contact yet"));
+ mbx.exec();
+ }
+}
diff --git a/pica-client/contactlistwidget.h b/pica-client/contactlistwidget.h
new file mode 100644
index 0000000..d884bc4
--- /dev/null
+++ b/pica-client/contactlistwidget.h
@@ -0,0 +1,43 @@
+#ifndef CONTACTLISTWIDGET_H
+#define CONTACTLISTWIDGET_H
+
+#include <QListWidget>
+#include "contacts.h"
+#include <QAction>
+
+#include "chatwindow.h" //debug remove
+
+class ContactListWidget : public QListWidget
+{
+ Q_OBJECT
+public:
+ explicit ContactListWidget(QWidget *parent = 0);
+ ~ContactListWidget();
+
+ QAction *addcontactAct;
+ QAction *delcontactAct;
+ QAction *startchatAct;
+ QAction *viewcertAct;
+protected:
+ void contextMenuEvent(QContextMenuEvent *event);
+
+private:
+ void setContactsStorage(Contacts *ct);
+ Contacts* storage;
+ QList<Contacts::ContactRecord> contact_records;
+ QMap<QListWidgetItem*,Contacts::ContactRecord*> wgitem_to_recs;
+
+ //ChatWindow *cw_debug_remove;
+
+signals:
+
+private slots:
+ void add_contact();
+ void del_contact();
+ void start_chat();
+ void view_cert();
+
+ //void debug_onmsginput(QString msg, ChatWindow *sender_window);//debug remove after debug
+};
+
+#endif // CONTACTLISTWIDGET_H
diff --git a/pica-client/contacts.cpp b/pica-client/contacts.cpp
new file mode 100644
index 0000000..7b66c4f
--- /dev/null
+++ b/pica-client/contacts.cpp
@@ -0,0 +1,133 @@
+#include "contacts.h"
+
+
+//#include <QMessageBox>
+
+Contacts::Contacts(QString storage, quint32 user_account_id)
+ : account_id_(user_account_id)
+{
+ dbconn=QSqlDatabase::addDatabase("QSQLITE");
+ dbconn.setDatabaseName(storage);
+
+ if (!dbconn.open())
+ {
+ lasterr=dbconn.lastError();
+ return;
+ }
+
+//check if table contacts exists
+//...
+
+ QSqlQuery query;
+
+ query.exec("PRAGMA foreign_keys=ON;");
+
+ //check result, if foreign keys support is on
+}
+
+bool Contacts::isOK()
+{
+ return !lasterr.isValid();
+}
+
+QString Contacts::GetLastError()
+{
+ return lasterr.text();
+}
+
+void Contacts::Add(quint32 id)
+{
+ QSqlQuery query;
+
+ query.prepare("insert into contacts (id, name, cert_pem, account_id) values (:id, NULL, NULL, :account_id)");
+ query.bindValue(":id",id);
+ query.bindValue(":account_id",account_id_);
+ query.exec();
+
+ lasterr=query.lastError();
+}
+
+void Contacts::Delete(quint32 id)
+{
+ QSqlQuery query;
+
+ query.prepare("delete from contacts where id=:id and account_id=:account_id");
+ query.bindValue(":id",id);
+ query.bindValue(":account_id",account_id_);
+ query.exec();
+
+ lasterr=query.lastError();
+}
+
+QList<Contacts::ContactRecord> Contacts::GetContacts()
+{
+ QList<ContactRecord> L;
+ ContactRecord r;
+ QSqlQuery query;
+
+ query.setForwardOnly(true);
+ query.prepare("select id, name from contacts where account_id=:account_id");
+ query.bindValue(":account_id",account_id_);
+ query.exec();
+ lasterr=query.lastError();
+
+ if (lasterr.isValid())
+ return L;
+
+ while(query.next())
+ {
+ r.id=query.value(0).toUInt();
+ r.name=query.value(1).toString();
+ //CSTRL.append(" ("+query.value(0).toString()+") "+query.value(1).toString());
+ L<<r;
+ }
+
+ return L;
+}
+
+QString Contacts::GetContactCert(quint32 id)
+{
+ QSqlQuery query;
+
+ query.prepare("select cert_pem from contacts where id=:id and account_id=:account_id");
+ query.bindValue(":id",id);
+ query.bindValue(":account_id",account_id_);
+ query.exec();
+
+ lasterr=query.lastError();
+
+ if (lasterr.isValid())
+ return QString::null;
+
+ query.next();
+
+ return query.value(0).toString();
+}
+
+void Contacts::SetContactCert(quint32 id, QString &cert_pem)
+{
+ QSqlQuery query;
+
+ query.prepare("update contacts set cert_pem=:cert_pem where id=:id and account_id=:account_id");
+ query.bindValue(":cert_pem",cert_pem);
+ query.bindValue(":id",id);
+ query.bindValue(":account_id",account_id_);
+ query.exec();
+
+ lasterr=query.lastError();
+
+}
+
+void Contacts::SetContactName(quint32 id, QString name)
+{
+ QSqlQuery query;
+
+ query.prepare("update contacts set name=:name where id=:id and account_id=:account_id");
+ query.bindValue(":name",name);
+ query.bindValue(":id",id);
+ query.bindValue(":account_id",account_id_);
+ query.exec();
+
+ lasterr=query.lastError();
+}
+
diff --git a/pica-client/contacts.h b/pica-client/contacts.h
new file mode 100644
index 0000000..54042b5
--- /dev/null
+++ b/pica-client/contacts.h
@@ -0,0 +1,34 @@
+#ifndef CONTACTS_H
+#define CONTACTS_H
+
+#include <QString>
+#include <QtSql>
+#include <QSqlDatabase>
+#include <QStringList>
+
+class Contacts
+{
+public:
+ struct ContactRecord
+ {
+ quint32 id;
+ QString name;
+ };
+
+ Contacts(QString storage, quint32 user_account_id);
+
+ void Add(quint32 id);
+ void Delete(quint32 id);
+ QList<ContactRecord> GetContacts();
+ QString GetContactCert(quint32 id);
+ void SetContactCert(quint32 id, QString &cert_pem);
+ void SetContactName(quint32 id, QString name);
+ QString GetLastError();
+ bool isOK();
+private:
+ QSqlDatabase dbconn;
+ QSqlError lasterr;
+ quint32 account_id_;
+};
+
+#endif // CONTACTS_H
diff --git a/pica-client/dialogs/addaccountdialog.cpp b/pica-client/dialogs/addaccountdialog.cpp
new file mode 100644
index 0000000..86849d4
--- /dev/null
+++ b/pica-client/dialogs/addaccountdialog.cpp
@@ -0,0 +1,114 @@
+#include "addaccountdialog.h"
+#include <QGridLayout>
+#include <QLabel>
+#include <QFile>
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QProcess>
+#include "../PICA_client.h"
+
+AddAccountDialog::AddAccountDialog(QWidget *parent) :
+ QDialog(parent)
+{
+ QGridLayout *grid = new QGridLayout(this);
+
+ QLabel *lb_cert_file = new QLabel(tr("Pica Pica Certificate File:"));
+ QLabel *lb_pkey_file = new QLabel(tr("Private Key File:"));
+
+ btOK = new QPushButton(tr("OK"));
+ btCancel = new QPushButton(tr("Cancel"));
+
+ cert_filename = new QLineEdit();
+ pkey_filename = new QLineEdit();
+
+ bt_browse_cert = new QPushButton("...");
+ bt_browse_pkey = new QPushButton("...");
+
+ rb_copyfiles = new QRadioButton(tr("Copy files to config directory"));
+ rb_readinplace = new QRadioButton(tr("Read files from selected place"));
+
+ grid->addWidget(lb_cert_file,0,0,1,1);
+
+ grid->addWidget(cert_filename,1,0,1,1);
+ grid->addWidget(bt_browse_cert,1,1,1,1);
+
+ grid->addWidget(lb_pkey_file,2,0,1,1);
+
+ grid->addWidget(pkey_filename,3,0,1,1);
+ grid->addWidget(bt_browse_pkey,3,1,1,1);
+
+ grid->addWidget(rb_copyfiles,4,0,1,1);
+ grid->addWidget(rb_readinplace,5,0,1,1);
+
+ grid->addWidget(btOK,6,0,1,1,Qt::AlignRight);
+ grid->addWidget(btCancel,6,1,1,1,Qt::AlignLeft);
+ //===========
+ rb_copyfiles->setChecked(true);
+
+ btOK->setMaximumWidth(90);
+ btCancel->setMaximumWidth(90);
+
+ connect(btOK,SIGNAL(clicked()),this,SLOT(OK()));
+ connect(btCancel,SIGNAL(clicked()),this,SLOT(Cancel()));
+ connect(bt_browse_cert,SIGNAL(clicked()),this,SLOT(browse_cert()));
+ connect(bt_browse_pkey,SIGNAL(clicked()),this,SLOT(browse_pkey()));
+
+
+}
+
+void AddAccountDialog::OK()
+{
+ {
+ QString s;
+ if (!QFile::exists(s=cert_filename->text()) || !QFile::exists(s=pkey_filename->text()) )
+ {
+ QMessageBox mbx;
+ mbx.setText(tr("File %1 does not exist").arg(s));
+ mbx.exec();
+ return;
+ }
+
+ quint32 id;
+
+ if (!PICA_get_id_from_cert(cert_filename->text().toUtf8().constData(),&id))
+ {
+ QMessageBox mbx;
+ mbx.setText(tr("Invalid certificate file"));
+ mbx.exec();
+ return;
+ }
+ }
+
+ done(1);
+
+}
+
+void AddAccountDialog::Cancel()
+{
+ done(0);
+}
+
+void AddAccountDialog::browse_cert()
+{
+ cert_filename->setText(QFileDialog::getOpenFileName(this,tr("Select Pica Pica Certificate"),"","PEM file (*.pem)"));
+}
+
+void AddAccountDialog::browse_pkey()
+{
+ pkey_filename->setText(QFileDialog::getOpenFileName(this,tr("Select Private Key"),"","PEM file (*.pem)"));
+}
+
+QString AddAccountDialog::GetCertFilename()
+{
+ return cert_filename->text();
+}
+
+QString AddAccountDialog::GetPkeyFilename()
+{
+ return pkey_filename->text();
+}
+
+bool AddAccountDialog::isCopyFilesChecked()
+{
+ return rb_copyfiles->isChecked();
+}
diff --git a/pica-client/dialogs/addaccountdialog.h b/pica-client/dialogs/addaccountdialog.h
new file mode 100644
index 0000000..976d020
--- /dev/null
+++ b/pica-client/dialogs/addaccountdialog.h
@@ -0,0 +1,41 @@
+#ifndef ADDACCOUNTDIALOG_H
+#define ADDACCOUNTDIALOG_H
+
+#include <QDialog>
+#include <QPushButton>
+#include <QLineEdit>
+#include <QRadioButton>
+
+class AddAccountDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ explicit AddAccountDialog(QWidget *parent = 0);
+ QString GetCertFilename();
+ QString GetPkeyFilename();
+ bool isCopyFilesChecked();
+
+private:
+ QPushButton *btOK;
+ QPushButton *btCancel;
+
+ QLineEdit *cert_filename;
+ QLineEdit *pkey_filename;
+
+ QPushButton *bt_browse_cert;
+ QPushButton *bt_browse_pkey;
+
+ QRadioButton *rb_copyfiles;
+ QRadioButton *rb_readinplace;
+signals:
+
+public slots:
+private slots:
+ void OK();
+ void Cancel();
+ void browse_cert();
+ void browse_pkey();
+
+};
+
+#endif // ADDACCOUNTDIALOG_H
diff --git a/pica-client/dialogs/forgedcertdialog.cpp b/pica-client/dialogs/forgedcertdialog.cpp
new file mode 100644
index 0000000..5ca7f23
--- /dev/null
+++ b/pica-client/dialogs/forgedcertdialog.cpp
@@ -0,0 +1,53 @@
+#include "forgedcertdialog.h"
+#include "viewcertdialog.h"
+#include <QGridLayout>
+#include <QLabel>
+
+ForgedCertDialog::ForgedCertDialog(quint32 peer_id, QString received_cert, QString stored_cert, QWidget *parent) :
+ QDialog(parent),
+ received_cert_(received_cert),
+ stored_cert_(stored_cert)
+{
+ QGridLayout *grid = new QGridLayout(this);
+
+ QLabel *scary_text = new QLabel(tr(
+"Previously stored and currently present certificates for user %1 do not match.\n\
+This means that probably someone is trying to eavesdrop your communication\n\
+using man-in-the-middle attack or impersonate the user.\n\
+Connection aborted.").arg(peer_id));
+
+ btOK = new QPushButton(tr("OK"));
+ btShow_stored_cert = new QPushButton(tr("View Stored Certificate..."));
+ btShow_received_cert = new QPushButton(tr("View Received Certificate..."));
+
+ grid->addWidget(scary_text, 0,0, 1, 3, Qt::AlignCenter);
+ grid->addWidget(btShow_stored_cert, 1, 0, Qt::AlignCenter);
+ grid->addWidget(btShow_received_cert, 1, 2, Qt::AlignCenter);
+ grid->addWidget(btOK, 2, 1, Qt::AlignCenter);
+
+
+ setLayout(grid);
+
+ connect(btOK, SIGNAL(clicked()), this, SLOT(OK()));
+ connect(btShow_stored_cert, SIGNAL(clicked()), this, SLOT(StoredCert()));
+ connect(btShow_received_cert, SIGNAL(clicked()), this, SLOT(ReceivedCert()));
+}
+
+void ForgedCertDialog::OK()
+{
+ done(0);
+}
+
+void ForgedCertDialog::ReceivedCert()
+{
+ ViewCertDialog vcd;
+ vcd.SetCert(received_cert_);
+ vcd.exec();
+}
+
+void ForgedCertDialog::StoredCert()
+{
+ ViewCertDialog vcd;
+ vcd.SetCert(stored_cert_);
+ vcd.exec();
+}
diff --git a/pica-client/dialogs/forgedcertdialog.h b/pica-client/dialogs/forgedcertdialog.h
new file mode 100644
index 0000000..bc259d4
--- /dev/null
+++ b/pica-client/dialogs/forgedcertdialog.h
@@ -0,0 +1,34 @@
+#ifndef FORGEDCERTDIALOG_H
+#define FORGEDCERTDIALOG_H
+
+#include <QDialog>
+#include <QPushButton>
+#include "viewcertdialog.h"
+
+class ForgedCertDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ explicit ForgedCertDialog(quint32 peer_id, QString received_cert, QString stored_cert, QWidget *parent = 0);
+
+private:
+ QString received_cert_;
+ QString stored_cert_;
+
+ QPushButton *btOK;
+ QPushButton *btShow_stored_cert;
+ QPushButton *btShow_received_cert;
+
+
+signals:
+
+public slots:
+
+private slots:
+ void OK();
+ void StoredCert();
+ void ReceivedCert();
+
+};
+
+#endif // FORGEDCERTDIALOG_H
diff --git a/pica-client/dialogs/viewcertdialog.cpp b/pica-client/dialogs/viewcertdialog.cpp
new file mode 100644
index 0000000..8b096c2
--- /dev/null
+++ b/pica-client/dialogs/viewcertdialog.cpp
@@ -0,0 +1,68 @@
+#include "viewcertdialog.h"
+#include <QVBoxLayout>
+#include <QProcess>
+
+#include <QMessageBox>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/wait.h>
+#include <QFile>
+
+ViewCertDialog::ViewCertDialog(QWidget *parent) :
+ QDialog(parent)
+{
+ QVBoxLayout *layout;
+
+ layout = new QVBoxLayout;
+ cert_text = new QTextEdit(this);
+ btOK = new QPushButton(tr("OK"));
+ btOK->setMaximumWidth(45);
+ this->setMinimumWidth(640);
+ this->setMinimumHeight(480);
+
+ QTextCharFormat fmt;
+
+ fmt = cert_text->currentCharFormat();
+ fmt.setFontFixedPitch(true);
+ fmt.setFontStyleHint(QFont::Monospace);
+ fmt.setFontFamily("Liberation Mono");
+ cert_text->setCurrentCharFormat(fmt);
+
+ cert_text->setReadOnly(true);
+
+ layout->addWidget(cert_text);
+ layout->addWidget(btOK, 0, Qt::AlignCenter);
+
+ setLayout(layout);
+
+ connect(btOK, SIGNAL(clicked()), this, SLOT(OK()));
+
+}
+
+void ViewCertDialog::SetCert(QString cert_pem)
+{
+ QProcess openssl(this);
+ qint64 ret;
+ char *data;
+ QByteArray ba;
+
+ openssl.start("openssl", QStringList()<<"x509"<<"-noout"<<"-text"<<"-nameopt"<<"multiline,-esc_msb,utf8");
+
+ if (!openssl.waitForStarted())
+ return;
+
+ ba = cert_pem.toAscii();
+ ret = openssl.write(data = (char*)ba.constData());
+ openssl.closeWriteChannel();
+
+ if (!openssl.waitForFinished())
+ return;
+
+ cert_text->setPlainText(QString::fromUtf8(openssl.readAllStandardOutput().constData()));
+}
+
+void ViewCertDialog::OK()
+{
+ done(0);
+}
diff --git a/pica-client/dialogs/viewcertdialog.h b/pica-client/dialogs/viewcertdialog.h
new file mode 100644
index 0000000..013815c
--- /dev/null
+++ b/pica-client/dialogs/viewcertdialog.h
@@ -0,0 +1,28 @@
+#ifndef VIEWCERTDIALOG_H
+#define VIEWCERTDIALOG_H
+
+#include <QDialog>
+#include <QTextEdit>
+#include <QPushButton>
+
+class ViewCertDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ explicit ViewCertDialog(QWidget *parent = 0);
+ void SetCert(QString cert_pem);
+
+private:
+ QTextEdit *cert_text;
+ QPushButton *btOK;
+
+signals:
+
+public slots:
+
+private slots:
+ void OK();
+
+};
+
+#endif // VIEWCERTDIALOG_H
diff --git a/pica-client/globals.h b/pica-client/globals.h
new file mode 100644
index 0000000..b6c2d7d
--- /dev/null
+++ b/pica-client/globals.h
@@ -0,0 +1,26 @@
+#ifndef GLOBALS_H
+#define GLOBALS_H
+
+#define PICA_CLIENT_STORAGEDB "pica-client.sqlite"
+#define PICA_CLIENT_CONFIGDIR ".pica-client"
+
+#ifndef PICA_INSTALLPREFIX
+
+#define PICA_INSTALLPREFIX "/usr/"
+
+#endif
+
+//variables are defined in main.cpp
+extern class QString config_dir;
+extern class QString config_dbname;
+extern class QString config_defaultCA;
+extern class SkyNet *skynet;
+extern class MainWindow *mainwindow;
+extern unsigned int account_id;
+extern class AskPassword *askpassword;
+extern class MsgUIRouter *msguirouter;
+extern class QIcon picapica_ico_sit;
+extern class QIcon picapica_ico_fly;
+extern class PicaActionCenter *action_center;
+extern class PicaSysTray *systray;
+#endif // GLOBALS_H
diff --git a/pica-client/main.cpp b/pica-client/main.cpp
new file mode 100644
index 0000000..f7a725b
--- /dev/null
+++ b/pica-client/main.cpp
@@ -0,0 +1,161 @@
+#include <QtGui/QApplication>
+#include "mainwindow.h"
+#include "accountswindow.h"
+#include "globals.h"
+#include "contacts.h"
+#include "skynet.h"
+#include "msguirouter.h"
+#include "picaactioncenter.h"
+#include "picasystray.h"
+#include <QDir>
+#include <QMessageBox>
+#include <QString>
+#include <QIcon>
+
+//globals
+QString config_dir;
+QString config_dbname;
+SkyNet *skynet;
+MainWindow *mainwindow;
+unsigned int account_id;
+class AskPassword *askpassword;
+class MsgUIRouter *msguirouter;
+
+QIcon picapica_ico_sit;
+QIcon picapica_ico_fly;
+
+class PicaActionCenter *action_center;
+class PicaSysTray *systray;
+
+// /usr/share/pica-client/pica-pica_CA.pem
+//QString config_defaultCA("/home/root_jr/files/projects/picapica_wc/localhost/project_picapica/trunk/tests/trusted_CA.pem");
+QString config_defaultCA(PICA_INSTALLPREFIX"share/pica-client/trusted_CA.pem");
+
+static bool create_config_dir()
+{
+ QMessageBox msgBox;
+ config_dir=QDir::homePath()+QDir::separator() +QString(PICA_CLIENT_CONFIGDIR);
+
+ if (!QFile::exists(config_dir))
+ {
+ msgBox.setText(QString("%1 does not exist").arg(config_dir));//debug
+ msgBox.exec();
+
+ QDir dir;
+
+ if (!dir.mkpath(config_dir))
+ {
+ msgBox.setText(QString(QObject::tr("Unable to create configuration directory:\n %1")).arg(config_dir));
+ msgBox.exec();
+ return false;
+ }
+ }
+
+ config_dbname=config_dir+QDir::separator()+QString(PICA_CLIENT_STORAGEDB);
+
+ if (!QFile::exists(config_dbname))
+ {
+ QSqlDatabase dbconn=QSqlDatabase::addDatabase("QSQLITE");
+ dbconn.setDatabaseName(config_dbname);
+
+ if (!dbconn.open())
+ {
+
+ msgBox.setText(dbconn.lastError().text());
+ msgBox.exec();
+ return false;
+ }
+
+ QSqlQuery query;
+
+ query.exec("PRAGMA foreign_keys=ON;");
+
+ if (query.lastError().isValid())
+ goto showerror;
+
+ query.exec("create table accounts \
+ (\
+ id int primary key, \
+ name varchar(64), \
+ cert_file varchar(255) not null, \
+ pkey_file varchar(255) not null, \
+ ca_file varchar(255) default null \
+ );");
+
+ if (query.lastError().isValid())
+ goto showerror;
+
+ query.exec("create table contacts \
+ (\
+ id int,\
+ name varchar(64), \
+ cert_pem text(2048), \
+ account_id int not null, \
+ primary key(id, account_id),\
+ foreign key(account_id) references accounts(id) on delete cascade\
+ );");
+
+ if (query.lastError().isValid())
+ goto showerror;
+
+ query.exec(
+ "create table nodes \
+ (\
+ address varchar(255) not null,\
+ port int not null,\
+ last_active int not null,\
+ inactive_count int not null,\
+ constraint pk primary key (address,port) on conflict replace \
+ );");
+
+
+ if (query.lastError().isValid())
+ showerror:
+ {
+ msgBox.setText(query.lastError().text());
+ msgBox.exec();
+ return false;
+ }
+
+ }
+
+
+ return true;
+
+
+}
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+
+ if (!create_config_dir())
+ return -1;
+
+ picapica_ico_sit = QIcon(PICA_INSTALLPREFIX"share/pica-client/picapica-icon-sit.png");
+ picapica_ico_fly = QIcon(PICA_INSTALLPREFIX"share/pica-client/picapica-icon-fly.png");
+
+ PicaActionCenter ac;
+ action_center = &ac;
+
+ AccountsWindow aw;
+
+ SkyNet s;
+ skynet=&s;
+
+ PicaSysTray pst;
+ systray = &pst;
+
+ AskPassword askpsw;
+ askpassword=&askpsw;
+
+ MsgUIRouter muir;
+ msguirouter = &muir;
+
+
+ aw.show();
+
+ a.setQuitOnLastWindowClosed(false);
+
+ return a.exec();
+}
diff --git a/pica-client/mainwindow.cpp b/pica-client/mainwindow.cpp
new file mode 100644
index 0000000..5bf44dd
--- /dev/null
+++ b/pica-client/mainwindow.cpp
@@ -0,0 +1,110 @@
+#include "mainwindow.h"
+#include "globals.h"
+#include "skynet.h"
+#include "picaactioncenter.h"
+#include "accounts.h"
+#include "ui_mainwindow.h"
+#include <QVBoxLayout>
+#include <QMessageBox>
+
+
+MainWindow::MainWindow(QWidget *parent) :
+ QMainWindow(parent),
+ ui(new Ui::MainWindow)
+{
+ ui->setupUi(this);
+
+ QVBoxLayout *layout;
+
+ layout = new QVBoxLayout;
+ contact_list = new ContactListWidget;
+ status = new QComboBox;
+
+ status->setMinimumHeight(36);
+
+ layout->addWidget(contact_list);
+ layout->addWidget(status,0,Qt::AlignBottom);
+
+ centralWidget()->setLayout(layout);
+ setWindowIcon(picapica_ico_sit);
+
+ createMenus();
+
+
+ //for (int i=0;i<255;i++)
+ // contact_list->addItem(QString("test %1").arg(i));
+
+ status->addItem(picapica_ico_fly, QString(tr("Connected")));
+ status->addItem(picapica_ico_sit, QString(tr("Disconnected")));
+
+ connect(skynet, SIGNAL(BecameSelfAware()), this, SLOT(set_online()));
+ connect(skynet, SIGNAL(LostSelfAwareness()), this, SLOT(set_offline()));
+ connect(status, SIGNAL(currentIndexChanged(int)), this, SLOT(status_changed(int)));
+}
+
+MainWindow::~MainWindow()
+{
+ delete ui;
+}
+
+void MainWindow::set_offline()
+{
+ SetStatus(false);
+}
+
+void MainWindow::set_online()
+{
+ SetStatus(true);
+}
+
+void MainWindow::status_changed(int index)
+{
+ if (status_change_disable_flag)
+ return;
+
+ if (index == 0 && !skynet->isSelfAware())
+ {
+ Accounts::AccountRecord curr_acc = skynet->CurrentAccount();
+ skynet->Join(curr_acc);
+ }
+ if (1 == index && skynet->isSelfAware())
+ skynet->Exit();
+}
+
+void MainWindow::SetStatus(bool connected)
+{
+ status_change_disable_flag = true;
+
+ if (connected)
+ {
+ status->setCurrentIndex(0);
+ setWindowTitle(tr("Pica Pica Messenger - ") + QString::number(account_id));
+ }
+ else
+ {
+ status->setCurrentIndex(1);
+ setWindowTitle(tr("Pica Pica Messenger - Disconnected"));
+ }
+
+ status_change_disable_flag = false;
+}
+
+
+void MainWindow::createMenus()
+{
+ contactsMenu=menuBar()->addMenu(tr("&Contacts"));
+ contactsMenu->addAction(contact_list->addcontactAct);
+ contactsMenu->addSeparator();
+ contactsMenu->addAction(action_center->ExitAct());
+
+ nodesMenu=menuBar()->addMenu(tr("&Nodes"));
+
+ helpMenu=menuBar()->addMenu(tr("&Help"));
+ helpMenu->addAction(action_center->AboutAct());
+
+}
+
+
+
+
+
diff --git a/pica-client/mainwindow.h b/pica-client/mainwindow.h
new file mode 100644
index 0000000..3361c69
--- /dev/null
+++ b/pica-client/mainwindow.h
@@ -0,0 +1,48 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QListWidget>
+#include <QComboBox>
+#include <QSystemTrayIcon>
+
+#include "contacts.h"
+#include "contactlistwidget.h"
+#include "askpassword.h"
+
+namespace Ui {
+ class MainWindow;
+}
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ explicit MainWindow(QWidget *parent = 0);
+ ~MainWindow();
+
+protected:
+
+private:
+ void createMenus();
+
+ Ui::MainWindow *ui;
+ ContactListWidget *contact_list;
+ QComboBox *status;
+
+ QMenu *contactsMenu;
+ QMenu *helpMenu;
+ QMenu *nodesMenu;
+
+ bool status_change_disable_flag;
+ void SetStatus(bool connected);
+
+private slots:
+ void set_online();
+ void set_offline();
+ void status_changed(int index);
+
+};
+
+#endif // MAINWINDOW_H
diff --git a/pica-client/mainwindow.ui b/pica-client/mainwindow.ui
new file mode 100644
index 0000000..0f2e2a7
--- /dev/null
+++ b/pica-client/mainwindow.ui
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>442</width>
+ <height>565</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Pica Pica Messenger</string>
+ </property>
+ <widget class="QWidget" name="centralWidget"/>
+ <widget class="QMenuBar" name="menuBar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>442</width>
+ <height>22</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QToolBar" name="mainToolBar">
+ <attribute name="toolBarArea">
+ <enum>TopToolBarArea</enum>
+ </attribute>
+ <attribute name="toolBarBreak">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ <widget class="QStatusBar" name="statusBar"/>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pica-client/msguirouter.cpp b/pica-client/msguirouter.cpp
new file mode 100644
index 0000000..efa080c
--- /dev/null
+++ b/pica-client/msguirouter.cpp
@@ -0,0 +1,93 @@
+#include "msguirouter.h"
+#include "dialogs/forgedcertdialog.h"
+
+
+/*
+ этот класс должен обеспечивать связь между приёмом-отправкой сообщений и их отображением в пользовательском интерфейсе.
+ Класс SkyNet отвечает только за прием и отправку сообщений, пересылку через сеть и работу с протоколом, ему пофиг,
+ как они показываются юзеру. Данный класс должен работать с GUI, например, по приходу нового сообщения (через сигнал от
+ класса SkyNet) либо помигать иконкой в трее, либо создать новое окно чата, либо развернуть уже имеющееся, либо помигать
+ свернутым окном на таскбаре. Это поведение может настраиваться, настройки хранить в базе и загружать в конструкторе из
+ базы sqlite. Например, данный класс должен управлять экземплярами ChatWindow, создавать новые при необходимости либо
+ активировать имеющиеся. Также этот класс должен обеспечивать хранение отложенных сообщений (когда собеседник недоступен).
+ Можно придумать название получше
+ */
+MsgUIRouter::MsgUIRouter(QObject *parent) :
+ QObject(parent)
+{
+ connect(skynet, SIGNAL(MessageReceived(quint32,QString)), this, SLOT(msg_from_peer(quint32,QString)));
+ connect(skynet, SIGNAL(UnableToDeliver(quint32,QString)), this, SLOT(delivery_failed(quint32,QString)));
+ connect(skynet, SIGNAL(Delivered(quint32)), this, SLOT(delivered(quint32)));
+ connect(skynet, SIGNAL(CertificateForged(quint32,QString,QString)), this, SLOT(scary_cert_message(quint32,QString,QString)));
+
+}
+
+void MsgUIRouter::create_chatwindow(quint32 peer_id)
+{
+ ChatWindow *cw;
+
+ cw = new ChatWindow(peer_id);
+
+ chatwindows[peer_id] = cw;
+
+ connect(cw, SIGNAL(msg_input(QString,ChatWindow*)), this, SLOT(msg_to_peer(QString,ChatWindow*)));
+ connect(cw, SIGNAL(chatwindow_close(ChatWindow*)), this, SLOT(chatwindow_closed(ChatWindow*)));
+
+ cw->show();
+}
+
+void MsgUIRouter::msg_from_peer(quint32 from, QString msg)
+{
+
+ if ( ! chatwindows.contains(from))
+ {
+ create_chatwindow(from);
+ }
+
+ chatwindows[from]->msg_from_peer(msg);
+
+}
+
+void MsgUIRouter::msg_to_peer(QString msg, ChatWindow *sender_window)
+{
+ skynet->SendMessage(sender_window->getPeerId(), msg);
+}
+
+void MsgUIRouter::start_chat(quint32 peer_id)
+{
+ if ( ! chatwindows.contains(peer_id))
+ {
+ create_chatwindow(peer_id);
+ }
+ else
+ {
+ chatwindows[peer_id]->showNormal();
+ }
+}
+
+void MsgUIRouter::chatwindow_closed(ChatWindow *sender_window)
+{
+ chatwindows.remove(sender_window->getPeerId());
+}
+
+void MsgUIRouter::delivery_failed(quint32 to, QString msg)
+{
+// if (chatwindows.contains(to))
+// {
+// chatwindows[to]->msg_informational(tr("failed to deliver"));
+// }
+}
+
+void MsgUIRouter::delivered(quint32 to)
+{
+ if (chatwindows.contains(to))
+ {
+ chatwindows[to]->msg_delivered();
+ }
+}
+
+void MsgUIRouter::scary_cert_message(quint32 peer_id, QString received_cert, QString stored_cert)
+{
+ ForgedCertDialog fcd(peer_id, received_cert, stored_cert);
+ fcd.exec();
+}
diff --git a/pica-client/msguirouter.h b/pica-client/msguirouter.h
new file mode 100644
index 0000000..5988878
--- /dev/null
+++ b/pica-client/msguirouter.h
@@ -0,0 +1,37 @@
+#ifndef MSGUIROUTER_H
+#define MSGUIROUTER_H
+
+#include <QObject>
+#include <QList>
+#include <QMap>
+#include "chatwindow.h"
+#include "skynet.h"
+#include "globals.h"
+
+class MsgUIRouter : public QObject
+{
+ Q_OBJECT
+public:
+ explicit MsgUIRouter(QObject *parent = 0);
+
+signals:
+
+public slots:
+ void msg_to_peer(QString msg, ChatWindow *sender_window);
+ void msg_from_peer(quint32 from, QString msg);
+
+ void chatwindow_closed(ChatWindow *sender_window);
+ void start_chat(quint32 peer_id);
+
+ void delivery_failed(quint32 to, QString msg);
+ void delivered(quint32 to);
+
+ void scary_cert_message(quint32 peer_id, QString received_cert, QString stored_cert);
+private:
+ QMap<quint32,ChatWindow*> chatwindows;
+
+ void create_chatwindow(quint32 peer_id);
+
+};
+
+#endif // MSGUIROUTER_H
diff --git a/pica-client/nodes.cpp b/pica-client/nodes.cpp
new file mode 100644
index 0000000..eb8bfcc
--- /dev/null
+++ b/pica-client/nodes.cpp
@@ -0,0 +1,73 @@
+#include "nodes.h"
+
+Nodes::Nodes(QString storage)
+{
+ dbconn=QSqlDatabase::addDatabase("QSQLITE");
+ dbconn.setDatabaseName(storage);
+
+ if (!dbconn.open())
+ {
+ lasterr=dbconn.lastError();
+ return;
+ }
+//check if table nodes exists
+}
+
+void Nodes::Add(NodeRecord &n)
+{
+ QSqlQuery query;
+
+ query.prepare("insert into nodes (address, port, last_active, inactive_count) values (:addr, :port, strftime('%s','now'), 0)");
+ query.bindValue(":addr",n.address);
+ query.bindValue(":port",n.port);
+ query.exec();
+
+ lasterr=query.lastError();
+}
+
+QList<Nodes::NodeRecord> Nodes::GetNodes()
+{
+ QList<NodeRecord> L;
+ NodeRecord r;
+ QSqlQuery query;
+
+ query.setForwardOnly(true);
+ query.exec("select address, port from nodes order by inactive_count asc, last_active desc");
+ lasterr=query.lastError();
+
+ if (lasterr.isValid())
+ return L;
+
+ while(query.next())
+ {
+ r.address =query.value(0).toString();
+ r.port =query.value(1).toUInt();
+
+ L<<r;
+ }
+
+ return L;
+}
+
+void Nodes::MakeClean()
+{
+
+}
+
+void Nodes::UpdateStatus(NodeRecord &n, bool alive)
+{
+ QSqlQuery query;
+
+ if (alive)
+ query.prepare("update nodes set last_active = strftime('%s','now'), inactive_count = 0 where address = :addr and port = :port");
+ else
+ query.prepare("update nodes set inactive_count = inactive_count + 1 where address = :addr and port = :port");
+
+ query.bindValue(":addr",n.address);
+ query.bindValue(":port",n.port);
+ query.exec();
+
+ lasterr=query.lastError();
+}
+
+
diff --git a/pica-client/nodes.h b/pica-client/nodes.h
new file mode 100644
index 0000000..a24fed2
--- /dev/null
+++ b/pica-client/nodes.h
@@ -0,0 +1,30 @@
+#ifndef NODES_H
+#define NODES_H
+
+#include <QList>
+#include <QString>
+#include <QtSql>
+#include <QSqlDatabase>
+
+class Nodes
+{
+public:
+ struct NodeRecord
+ {
+ QString address;
+ quint16 port;
+ };
+
+ Nodes(QString storage);
+ void Add(NodeRecord &n);
+ QList<NodeRecord> GetNodes();
+ void MakeClean();//remove old inactive node records
+ void UpdateStatus(NodeRecord &n, bool alive);
+
+private:
+ QSqlDatabase dbconn;
+ QSqlError lasterr;
+
+};
+
+#endif // NODES_H
diff --git a/pica-client/nodethread.cpp b/pica-client/nodethread.cpp
new file mode 100644
index 0000000..1524cbd
--- /dev/null
+++ b/pica-client/nodethread.cpp
@@ -0,0 +1,94 @@
+#include "nodethread.h"
+#include "mainwindow.h"
+
+QMutex NodeThread::finished_mutex;
+
+NodeThread::NodeThread(Nodes::NodeRecord &addr, bool *completed, Accounts::AccountRecord &acc, PICA_conninfo **pica_conn, QMutex *wm) :
+ QThread(0)
+{
+ node_addr = addr;
+ finished_flag = completed;
+ user_account = acc;
+ connection = pica_conn;
+ write_mutex = wm;
+ exit = 0;
+ start();
+}
+
+void NodeThread::run()
+{
+ int ret;
+ exit = 0;
+
+ ret = PICA_new_connection(node_addr.address.toUtf8().constData(),
+ node_addr.port,
+ user_account.CA_file.toUtf8().constData(),
+ user_account.cert_file.toUtf8().constData(),
+ user_account.pkey_file.toUtf8().constData(),
+ AskPassword::ask_password_cb,
+ &ci);
+
+ switch(ret)
+ {
+ case PICA_OK:
+
+ emit NodeStatusChanged(node_addr.address, node_addr.port, true);
+
+ finished_mutex.lock();
+ if (*finished_flag)
+ {
+ PICA_close_connection(ci);
+ exit = 1;
+ }
+ else
+ {
+ *finished_flag = true;
+ *connection = ci;
+ emit ConnectedToNode(node_addr.address, node_addr.port, this);
+ }
+ finished_mutex.unlock();
+
+
+
+ break;
+
+ case PICA_ERRSOCK:
+ case PICA_ERRDNSRESOLVE:
+
+ emit NodeStatusChanged(node_addr.address, node_addr.port, false);
+
+ exit = 1;
+ break;
+
+ case PICA_ERRSSL:
+ exit = 1;
+ break;
+
+ default:
+ exit = 1;
+ }
+
+ if (exit)
+ return;
+
+ do
+ {
+ write_mutex->lock();
+ if (PICA_read(ci,10000) != PICA_OK || PICA_write(ci) != PICA_OK)
+ exit=1;
+ write_mutex->unlock();
+ msleep(300);
+ }
+ while (!exit);
+
+ write_mutex->lock();
+ PICA_close_connection(ci);
+ *connection = NULL;
+ write_mutex->unlock();
+}
+
+void NodeThread::CloseThread()
+{
+ exit = 1;
+}
+
diff --git a/pica-client/nodethread.h b/pica-client/nodethread.h
new file mode 100644
index 0000000..1d17fda
--- /dev/null
+++ b/pica-client/nodethread.h
@@ -0,0 +1,40 @@
+#ifndef NODETHREAD_H
+#define NODETHREAD_H
+
+#include <QThread>
+#include <QMutex>
+#include "nodes.h"
+#include "../PICA_client.h"
+#include "accounts.h"
+
+
+class NodeThread : public QThread
+{
+ Q_OBJECT
+public:
+ explicit NodeThread( Nodes::NodeRecord &addr, bool *completed,Accounts::AccountRecord &acc,struct PICA_conninfo **pica_conn, QMutex *wm);
+ void CloseThread();
+private:
+ void run();
+ static QMutex finished_mutex;
+
+ QMutex *write_mutex;
+
+ bool *finished_flag;
+ Nodes::NodeRecord node_addr;
+ struct PICA_conninfo *ci;
+ Accounts::AccountRecord user_account;
+ struct PICA_conninfo **connection;
+
+ int exit;
+
+
+signals:
+ void NodeStatusChanged(QString addr, quint16 port, bool alive);
+ void ConnectedToNode(QString addr, quint16 port, NodeThread *thread);
+
+public slots:
+
+};
+
+#endif // NODETHREAD_H
diff --git a/pica-client/pica-client.pro b/pica-client/pica-client.pro
new file mode 100644
index 0000000..30c401e
--- /dev/null
+++ b/pica-client/pica-client.pro
@@ -0,0 +1,70 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2011-10-22T02:19:02
+#
+#-------------------------------------------------
+
+QT += core gui sql
+
+TARGET = pica-client
+TEMPLATE = app
+
+
+SOURCES += main.cpp\
+ mainwindow.cpp \
+ chatwindow.cpp \
+ contacts.cpp \
+ ../PICA_client.c \
+ ../PICA_msgproc.c \
+ contactlistwidget.cpp \
+ skynet.cpp \
+ nodes.cpp \
+ accountswindow.cpp \
+ accounts.cpp \
+ dialogs/addaccountdialog.cpp \
+ nodethread.cpp \
+ askpassword.cpp \
+ msguirouter.cpp \
+ picasystray.cpp \
+ picaactioncenter.cpp \
+ dialogs/viewcertdialog.cpp \
+ dialogs/forgedcertdialog.cpp
+
+HEADERS += mainwindow.h \
+ chatwindow.h \
+ contacts.h \
+ globals.h \
+ ../PICA_client.h \
+ ../PICA_msgproc.h \
+ contactlistwidget.h \
+ skynet.h \
+ nodes.h \
+ accountswindow.h \
+ accounts.h \
+ dialogs/addaccountdialog.h \
+ nodethread.h \
+ askpassword.h \
+ msguirouter.h \
+ picasystray.h \
+ picaactioncenter.h \
+ dialogs/viewcertdialog.h \
+ dialogs/forgedcertdialog.h \
+ ../PICA_common.h \
+ ../PICA_proto.h
+
+
+DEFINES = PICA_MULTITHREADED
+
+FORMS += mainwindow.ui
+
+
+
+unix: CONFIG += link_pkgconfig
+unix: PKGCONFIG += libssl
+
+unix: CONFIG += link_pkgconfig
+unix: PKGCONFIG += libcrypto
+
+
+
+
diff --git a/pica-client/picaactioncenter.cpp b/pica-client/picaactioncenter.cpp
new file mode 100644
index 0000000..9a811dc
--- /dev/null
+++ b/pica-client/picaactioncenter.cpp
@@ -0,0 +1,37 @@
+#include "picaactioncenter.h"
+#include "globals.h"
+#include "mainwindow.h"
+#include "skynet.h"
+#include <QApplication>
+
+#include <QMessageBox>
+
+PicaActionCenter::PicaActionCenter(QObject *parent) :
+ QObject(parent)
+{
+ exitAct = new QAction(tr("E&xit"),this);
+ exitAct->setStatusTip(tr("Close Pica Pica messenger and exit"));
+ connect(exitAct,SIGNAL(triggered()),this,SLOT(exit()));
+
+ aboutAct = new QAction(tr("A&bout"),this);
+ aboutAct->setStatusTip(tr("About Pica Pica Messenger"));
+ connect(aboutAct,SIGNAL(triggered()),this,SLOT(about()));
+}
+
+void PicaActionCenter::about()
+{
+ QMessageBox mbx;
+ mbx.setWindowIcon(picapica_ico_sit);
+ mbx.setIconPixmap(picapica_ico_sit.pixmap(32));
+ mbx.setText(tr("(c) 2012 Anton Sviridenko"));
+ mbx.exec();
+}
+
+void PicaActionCenter::exit()
+{
+ if (mainwindow)
+ mainwindow->close();
+
+ skynet->Exit();
+ QApplication::instance()->quit();
+}
diff --git a/pica-client/picaactioncenter.h b/pica-client/picaactioncenter.h
new file mode 100644
index 0000000..a406743
--- /dev/null
+++ b/pica-client/picaactioncenter.h
@@ -0,0 +1,28 @@
+#ifndef PICAACTIONCENTER_H
+#define PICAACTIONCENTER_H
+
+#include <QObject>
+#include <QAction>
+
+class PicaActionCenter : public QObject
+{
+ Q_OBJECT
+public:
+ explicit PicaActionCenter(QObject *parent = 0);
+
+ QAction *AboutAct() {return aboutAct;};
+ QAction *ExitAct() {return exitAct;};
+
+private:
+ QAction *aboutAct;
+ QAction *exitAct;
+
+signals:
+
+public slots:
+private slots:
+ void about();
+ void exit();
+};
+
+#endif // PICAACTIONCENTER_H
diff --git a/pica-client/picapica-icon-fly.png b/pica-client/picapica-icon-fly.png
new file mode 100644
index 0000000..b01e972
--- /dev/null
+++ b/pica-client/picapica-icon-fly.png
Binary files differ
diff --git a/pica-client/picapica-icon-sit.png b/pica-client/picapica-icon-sit.png
new file mode 100644
index 0000000..6dfc5c9
--- /dev/null
+++ b/pica-client/picapica-icon-sit.png
Binary files differ
diff --git a/pica-client/picasystray.cpp b/pica-client/picasystray.cpp
new file mode 100644
index 0000000..ab31887
--- /dev/null
+++ b/pica-client/picasystray.cpp
@@ -0,0 +1,39 @@
+#include "picasystray.h"
+#include "globals.h"
+#include "picaactioncenter.h"
+
+PicaSysTray::PicaSysTray(QObject *parent) :
+ QObject(parent)
+{
+ systrayMenu_ = new QMenu();
+
+ systrayMenu_->addAction(action_center->AboutAct());
+ systrayMenu_->addSeparator();
+ systrayMenu_->addAction(action_center->ExitAct());
+
+ systray_ = new QSystemTrayIcon(picapica_ico_sit);
+ systray_->setContextMenu(systrayMenu_);
+ systray_->show();
+
+ connect(systray_, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(systray_activated(QSystemTrayIcon::ActivationReason)));
+ connect(skynet, SIGNAL(BecameSelfAware()), this, SLOT(skynet_became_self_aware()));
+ connect(skynet, SIGNAL(LostSelfAwareness()), this, SLOT(skynet_lost_self_awareness()));
+}
+
+void PicaSysTray::systray_activated(QSystemTrayIcon::ActivationReason r)
+{
+ if (r == QSystemTrayIcon::DoubleClick && mainwindow!=NULL)
+ {
+ mainwindow->showNormal();
+ }
+}
+
+void PicaSysTray::skynet_became_self_aware()
+{
+ systray_->setIcon(picapica_ico_fly);
+}
+
+void PicaSysTray::skynet_lost_self_awareness()
+{
+ systray_->setIcon(picapica_ico_sit);
+}
diff --git a/pica-client/picasystray.h b/pica-client/picasystray.h
new file mode 100644
index 0000000..cf9706d
--- /dev/null
+++ b/pica-client/picasystray.h
@@ -0,0 +1,31 @@
+#ifndef PICASYSTRAY_H
+#define PICASYSTRAY_H
+
+#include <QObject>
+#include <QSystemTrayIcon>
+#include <QMenu>
+#include "skynet.h"
+#include "mainwindow.h"
+#include "globals.h"
+
+class PicaSysTray : public QObject
+{
+ Q_OBJECT
+public:
+ explicit PicaSysTray(QObject *parent = 0);
+
+signals:
+
+public slots:
+
+private:
+ QSystemTrayIcon *systray_;
+ QMenu *systrayMenu_;
+
+private slots:
+ void systray_activated(QSystemTrayIcon::ActivationReason);
+ void skynet_became_self_aware();//debug
+ void skynet_lost_self_awareness();//debug
+};
+
+#endif // PICASYSTRAY_H
diff --git a/pica-client/skynet.cpp b/pica-client/skynet.cpp
new file mode 100644
index 0000000..d522d60
--- /dev/null
+++ b/pica-client/skynet.cpp
@@ -0,0 +1,437 @@
+#include "skynet.h"
+#include "globals.h"
+#include <QMutex>
+//#include "dialogs/viewcertdialog.h"
+#include "contacts.h"
+//#include <QMessageBox> //debug
+//#include "dialogs/forgedcertdialog.h"
+#include <QDebug>
+
+
+SkyNet::SkyNet()
+ : nodes(config_dbname), QObject(0)
+{
+ self_aware = false;
+
+PICA_client_callbacks cbs = {
+ newmsg_cb,
+ msgok_cb,
+ channel_established_cb,
+ channel_failed,
+ accept_cb,
+ notfound_cb,
+ channel_closed_cb,
+ nodelist_cb,
+ peer_cert_verify_cb
+ };
+
+PICA_client_init(&cbs);
+
+nodelink = NULL;
+
+connect(this, SIGNAL(PeerCertificateReceived(quint32,QString,bool*)), this, SLOT(verify_peer_cert(quint32,QString,bool*)),Qt::BlockingQueuedConnection);
+
+}
+
+void SkyNet::nodethread_finished()
+{
+//удалить здесь завершившийся поток
+ for (int i=0;i<threads.count();i++)
+ {
+ if (threads[i]->isFinished())
+ {
+ delete threads.takeAt(i);
+ break;
+ }
+ }
+
+ if (threads.count() == 0)
+ {
+ self_aware = false;
+ emit LostSelfAwareness();
+ }
+}
+
+void SkyNet::nodethread_connected(QString addr, quint16 port, NodeThread *thread)
+{
+ if (self_aware)
+ {
+ emit BecameSelfAware();
+
+ //restore old peer connections, if any
+ QList<quint32> c2c_peer_ids;
+
+ c2c_peer_ids = msgqueues.keys();
+
+ for (int i = 0; i < c2c_peer_ids.size(); i++)
+ {
+ int ret;
+ write_mutex.lock();
+ struct PICA_chaninfo *chan = NULL;
+
+ ret = PICA_create_channel(nodelink, c2c_peer_ids[i], &chan);
+
+ qDebug()<<"restoring channel to "<<c2c_peer_ids[i]<<" ret ="<<ret<<"\n";
+ write_mutex.unlock();
+ }
+
+ //start timer
+ timer_id = startTimer(10000);
+ }
+}
+
+void SkyNet::node_status_changed(QString addr, quint16 port, bool alive)
+{
+ Nodes::NodeRecord nr = {addr, port};
+ nodes.UpdateStatus(nr, alive);
+}
+
+void SkyNet::verify_peer_cert(quint32 peer_id, QString cert_pem, bool *verified)
+{
+// ViewCertDialog vcd;
+// vcd.SetCert(cert_pem);
+// vcd.exec();
+ Contacts cnt(config_dbname, account_id);
+ QString stored_cert;
+
+ if ((stored_cert = cnt.GetContactCert(peer_id)).isEmpty())
+ {
+ cnt.SetContactCert(peer_id, cert_pem);
+
+ // -- move to OpenSSL class
+ QProcess openssl;
+
+ openssl.start("openssl",QStringList()<<"x509"<<"-subject"<<"-nameopt"<<"oneline,-esc_msb,utf8"<<"-noout");
+ if (!openssl.waitForStarted())
+ return;
+
+ openssl.write(cert_pem.toAscii());
+
+ if (!openssl.waitForFinished())
+ return;
+
+ QByteArray result = openssl.readAll();
+ QRegExp rx("CN = [0-9]+\\#([^\\s\\#]+)");
+
+
+ QString name=QString::fromUtf8( result.constData());
+
+ if (name.contains(rx))
+ {
+ name=rx.cap(1);
+ }
+
+ //
+ cnt.SetContactName(peer_id, name);
+ }
+ else
+ {//compare certificates
+ QString strip_pem[4] = {
+ "-----BEGIN CERTIFICATE-----",
+ "-----END CERTIFICATE-----",
+ "-----BEGIN X509 CERTIFICATE-----",
+ "-----END X509 CERTIFICATE-----"
+ };
+ QString stripped_stored_cert = stored_cert;
+ QString stripped_received_cert = cert_pem;
+
+ for (int i = 0; i < 4; i++)
+ {
+ stripped_stored_cert.replace(strip_pem[i], "");
+ stripped_received_cert.replace(strip_pem[i], "");
+ }
+
+ QByteArray stored_DER, received_DER;
+
+ stored_DER = QByteArray::fromBase64(stripped_stored_cert.toAscii().constData());
+ received_DER = QByteArray::fromBase64(stripped_received_cert.toAscii().constData());
+
+ if (stored_DER.size() != received_DER.size() ||
+ memcmp(stored_DER.constData(), received_DER.constData(), stored_DER.size()) != 0)
+ {//put scary message here
+ //QMessageBox mbx;
+ //mbx.setText(tr("Certificate is forged!!!"));
+ //mbx.exec();
+ //ForgedCertDialog fcd(peer_id, cert_pem, stored_cert);
+ //fcd.exec();
+ *verified = false;
+ emit CertificateForged(peer_id, cert_pem, stored_cert);
+ }
+
+
+ }
+}
+
+void SkyNet::timerEvent(QTimerEvent *e)
+{
+if (self_aware)
+ {
+ QList<quint32> c2c_peer_ids = msgqueues.keys();
+
+ write_mutex.lock();
+
+ c2c_peer_ids = filter_existing_chans(c2c_peer_ids);
+
+ for (int i = 0; i < c2c_peer_ids.size(); i++)
+ {
+ int ret;
+
+ struct PICA_chaninfo *chan = NULL;
+
+ ret = PICA_create_channel(nodelink, c2c_peer_ids[i], &chan);
+
+ qDebug()<<"restoring channel to "<<c2c_peer_ids[i]<<" ret ="<<ret<<" in timer event\n";
+
+ }
+ write_mutex.unlock();
+ }
+else
+ {
+ killTimer(e->timerId());
+ Accounts::AccountRecord acc = this->CurrentAccount();
+ Join(acc);
+ }
+}
+
+void SkyNet::Join(Accounts::AccountRecord &accrec)
+{
+ QList<Nodes::NodeRecord> nodelist;
+ skynet_account = accrec;
+ nodelist = nodes.GetNodes();
+
+ if (nodelist.count()==0)
+ {
+ status = QObject::tr("No known Pica Pica nodes");
+ return;
+ }
+
+ for (int i=0;i<nodelist.count() && !self_aware;i++)
+ {//TODO FIXME сделать запуск потоков порциями, а не все сразу
+ threads.append(new NodeThread(nodelist[i],&self_aware,skynet_account,&nodelink, &write_mutex));
+ connect(threads.last(), SIGNAL(finished()),this,SLOT(nodethread_finished()));
+ connect(threads.last(), SIGNAL(NodeStatusChanged(QString,quint16,bool)), this, SLOT(node_status_changed(QString,quint16,bool)));
+ connect(threads.last(), SIGNAL(ConnectedToNode(QString,quint16,NodeThread*)), this, SLOT(nodethread_connected(QString,quint16,NodeThread*)));
+ }
+
+}
+
+void SkyNet::Exit()
+{
+ for (int i = 0; i< threads.count(); i++)
+ threads[i]->CloseThread();
+
+ self_aware = false;
+
+ killTimer(timer_id);
+}
+
+bool SkyNet::isSelfAware()
+{
+ return self_aware;
+}
+
+void SkyNet::SendMessage(quint32 to, QString msg)
+{
+int ret = PICA_OK;
+struct PICA_chaninfo *iptr;
+
+write_mutex.lock();//<<
+
+if ( (iptr = find_active_chan(to)) )
+{
+ //write_mutex.lock();//<<
+ ret = PICA_send_msg(iptr, msg.toUtf8().data(), msg.toUtf8().size());
+ //write_mutex.unlock();//>>
+
+ if (ret != PICA_OK)
+ emit UnableToDeliver(to, msg);
+
+ write_mutex.unlock();//>>
+ return;
+}
+
+
+ if (msgqueues.contains(to))
+ {
+ msgqueues[to].append(msg);
+ }
+ else
+ {
+ struct PICA_chaninfo *chan = NULL;
+
+ ret = PICA_create_channel(nodelink, to, &chan);
+
+ QList<QString> l;
+ l.append(msg);
+ msgqueues[to] = l;
+ }
+
+
+write_mutex.unlock();//>>
+
+ if (ret != PICA_OK)
+ emit UnableToDeliver(to, msg);
+
+}
+
+void SkyNet::flush_msgqueue(quint32 to)
+{
+ struct PICA_chaninfo *chan;
+ int ret;
+
+ if (self_aware && msgqueues.contains(to))
+ {
+ qDebug()<<"flushing "<<to<<" message queue\n";
+
+ if ((chan = find_active_chan(to)))
+ {
+ while( !msgqueues[to].empty() )
+ {
+ ret = PICA_send_msg(chan, msgqueues[to].first().toUtf8().data(), msgqueues[to].first().toUtf8().size());
+
+ if (ret != PICA_OK)
+ {
+ emit UnableToDeliver(to, msgqueues[to].first());
+ break;
+ }
+
+ msgqueues[to].removeFirst();
+
+ }
+
+ if (msgqueues[to].empty())
+ msgqueues.remove(to);
+ }
+ }
+}
+
+struct PICA_chaninfo * SkyNet::find_active_chan(quint32 peer_id)
+{
+if (!nodelink)
+ return NULL;
+
+struct PICA_chaninfo *iptr = nodelink->chan_list_head;
+
+while(iptr)
+ {
+ if (iptr->state == PICA_CHANSTATE_ACTIVE && iptr->peer_id == peer_id)
+ break;
+
+ iptr = iptr->next;
+ }
+
+return iptr;
+}
+
+QList<quint32> SkyNet::filter_existing_chans(QList<quint32> peer_ids)
+{
+ QList<quint32> ret = peer_ids;
+
+ if (!nodelink)
+ return ret;
+
+ struct PICA_chaninfo *iptr = nodelink->chan_list_head;
+
+ while(iptr)
+ {
+ if (peer_ids.contains(iptr->peer_id))
+ ret.removeOne(iptr->peer_id);
+
+ iptr = iptr->next;
+ }
+
+ return ret;
+}
+
+void SkyNet::emit_Delivered(quint32 to)
+{
+emit Delivered(to);
+}
+
+void SkyNet::emit_MessageReceived(quint32 from, QString msg)
+{
+emit MessageReceived(from, msg);
+}
+
+void SkyNet::emit_UnableToDeliver(quint32 to, QString msg)
+{
+emit UnableToDeliver(to, msg);
+}
+
+void SkyNet::emit_PeerCertificateReceived(quint32 peer_id, QString cert_pem, bool *verified)
+{
+ emit PeerCertificateReceived(peer_id, cert_pem, verified);
+}
+
+//callbacks
+
+//all callbacks are executed in separate thread, created in Nodethread instance, REMEMBER THAT !!!
+// write_mutex is locked
+
+void SkyNet::newmsg_cb(unsigned int peer_id, char *msgbuf, unsigned int nb, int type)
+{
+QString msg = QString::fromUtf8(msgbuf, nb);
+
+skynet->emit_MessageReceived(peer_id, msg);
+}
+
+void SkyNet::msgok_cb(unsigned int peer_id)
+{
+skynet->emit_Delivered(peer_id);
+}
+
+void SkyNet::channel_established_cb(unsigned int peer_id)
+{
+skynet->flush_msgqueue(peer_id);
+}
+
+void SkyNet::channel_failed(unsigned int peer_id)
+{
+ qDebug()<<"channel failed ("<<peer_id<<")\n";
+}
+
+int SkyNet::accept_cb(unsigned int caller_id)
+{
+return 1; //implement black list
+}
+
+void SkyNet::notfound_cb(unsigned int callee_id)
+{
+ qDebug()<<"not found ("<<callee_id<<")\n";
+
+ /*
+ if (skynet->msgqueues.contains(callee_id))
+ {
+ while ( ! skynet->msgqueues[callee_id].empty())
+ {
+ skynet->emit_UnableToDeliver(callee_id, skynet->msgqueues[callee_id].last());
+ skynet->msgqueues[callee_id].removeLast();
+ }
+ // msgqueues.remove(callee_id) was forgotten
+ }
+ */
+}
+
+void SkyNet::channel_closed_cb(unsigned int peer_id, int reason)
+{
+ qDebug()<<"channel closed ("<<peer_id<<")\n";
+
+}
+
+void SkyNet::nodelist_cb(int type, void *addr_bin, const char *addr_str, unsigned int port)
+{
+ Nodes::NodeRecord nr = {addr_str, port};
+ skynet->nodes.Add(nr);
+}
+
+int SkyNet::peer_cert_verify_cb(unsigned int peer_id, const char *cert_pem, unsigned int nb)
+{
+ bool verified = true;
+
+ skynet->emit_PeerCertificateReceived(peer_id, QString::fromAscii(cert_pem, nb), &verified);
+
+ if (!verified)
+ return 0;
+
+ return 1;
+}
diff --git a/pica-client/skynet.h b/pica-client/skynet.h
new file mode 100644
index 0000000..21d760c
--- /dev/null
+++ b/pica-client/skynet.h
@@ -0,0 +1,83 @@
+#ifndef SKYNET_H
+#define SKYNET_H
+
+#include "nodes.h"
+#include "nodethread.h"
+#include "../PICA_client.h"
+#include "accounts.h"
+#include <QObject>
+#include <QMap>
+
+
+class SkyNet : public QObject
+{
+ Q_OBJECT
+public:
+ SkyNet();
+ void Join(Accounts::AccountRecord &accrec);
+ bool isSelfAware();
+ QString& Status();
+ void Exit();
+
+ void SendMessage(quint32 to, QString msg);
+ Accounts::AccountRecord CurrentAccount() {return skynet_account;};
+
+signals:
+ void MessageReceived(quint32 from, QString msg);
+ void UnableToDeliver(quint32 to, QString msg);
+ void Delivered(quint32 to);
+ void BecameSelfAware();
+ void LostSelfAwareness();
+ void PeerCertificateReceived(quint32 peer_id, QString cert_pem, bool *verified);
+ void CertificateForged(quint32 peer_id, QString received_cert, QString stored_cert);
+
+private:
+ Nodes nodes;
+ QList<NodeThread*> threads;
+ bool self_aware;
+ QString status;
+ struct PICA_conninfo *nodelink;
+ Accounts::AccountRecord skynet_account;
+ QMutex write_mutex;
+ QMap<quint32, QList<QString> > msgqueues;
+ int timer_id;
+
+ void timerEvent(QTimerEvent * e);
+
+ void flush_msgqueue(quint32 to);
+ struct PICA_chaninfo *find_active_chan(quint32 peer_id);
+ QList<quint32> filter_existing_chans(QList<quint32> peer_ids);
+
+ void emit_MessageReceived(quint32 from, QString msg);
+ void emit_UnableToDeliver(quint32 to, QString msg);
+ void emit_Delivered(quint32 to);
+ void emit_PeerCertificateReceived(quint32 peer_id, QString cert_pem, bool *verified);
+
+ //получение сообщения.
+ static void newmsg_cb(unsigned int peer_id,char* msgbuf,unsigned int nb,int type);
+ //получение подтверждения о доставке сообщения
+ static void msgok_cb(unsigned int peer_id);
+ //создание канала с собеседником
+ static void channel_established_cb(unsigned int peer_id);
+ //создать канал не удалось
+ static void channel_failed(unsigned int peer_id);
+ //входящий запрос на создание канала от пользователя с номером caller_id
+ static int accept_cb(unsigned int caller_id);
+ //запрошенный пользователь не найден, в оффлайне или отказался от общения
+ static void notfound_cb(unsigned int callee_id);
+
+ static void channel_closed_cb(unsigned int peer_id, int reason);
+
+ static void nodelist_cb(int type, void *addr_bin, const char *addr_str, unsigned int port);
+
+ static int peer_cert_verify_cb(unsigned int peer_id, const char *cert_pem, unsigned int nb);
+
+private slots:
+ void nodethread_finished();
+ void nodethread_connected(QString addr, quint16 port, NodeThread *thread);
+ void node_status_changed(QString addr,quint16 port,bool alive);
+ void verify_peer_cert(quint32 peer_id, QString cert_pem, bool *verified);
+
+};
+
+#endif // SKYNET_H
diff --git a/tests/0337.pem b/tests/0337.pem
new file mode 100755
index 0000000..d485b65
--- /dev/null
+++ b/tests/0337.pem
@@ -0,0 +1,45 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 823 (0x337)
+ Signature Algorithm: md5WithRSAEncryption
+ Issuer: O=TESTNET, CN=TESTNET CA
+ Validity
+ Not Before: Nov 25 17:53:27 2008 GMT
+ Not After : Nov 25 17:53:27 2009 GMT
+ Subject: C=GE, O=TESTNET, CN=5#tester5
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:d8:de:11:ad:7e:09:f7:8a:96:04:7b:4f:87:4b:
+ 3c:44:d7:00:a6:7e:94:1f:33:93:ee:81:f5:20:b0:
+ b3:c3:8d:f6:01:76:44:e7:b0:e9:b5:14:77:d0:4f:
+ df:93:0a:b4:89:e8:bc:a4:e7:b5:61:50:54:d2:ea:
+ af:68:62:6a:6f:84:ab:d8:17:3d:de:6b:8d:13:9c:
+ aa:8d:f2:12:40:49:a5:d9:0b:6f:c7:ce:0b:9c:4f:
+ 89:b3:cb:5d:3f:6a:7c:3a:7d:04:ef:1d:45:df:e1:
+ e2:fa:75:0e:cd:5e:c2:e1:d7:56:db:9f:0d:db:7f:
+ 8f:fe:ad:79:e4:bc:78:69:e7
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: md5WithRSAEncryption
+ 86:37:80:6d:fe:8a:15:38:ed:6b:3e:1d:41:7f:f0:6a:6d:03:
+ f2:45:72:62:52:b1:52:4c:8f:e8:53:24:cf:34:98:13:27:33:
+ 3f:89:cd:c4:f4:d5:cc:ab:59:ad:b0:56:39:a1:92:82:25:8b:
+ 49:bf:36:89:32:86:e7:ad:10:7a:85:d9:c8:ac:50:b9:ae:e8:
+ 60:e6:d2:5e:be:c4:83:0e:e1:34:9c:cb:84:61:3a:59:80:f9:
+ 92:c2:5b:5f:aa:82:97:e1:c0:20:a2:01:15:e9:54:87:98:a5:
+ 05:b9:a7:b5:f8:0b:2c:ff:e7:01:f5:c1:07:ef:ff:e1:23:6f:
+ b2:da
+-----BEGIN CERTIFICATE-----
+MIIByjCCATMCAgM3MA0GCSqGSIb3DQEBBAUAMCcxEDAOBgNVBAoTB1RFU1RORVQx
+EzARBgNVBAMTClRFU1RORVQgQ0EwHhcNMDgxMTI1MTc1MzI3WhcNMDkxMTI1MTc1
+MzI3WjAzMQswCQYDVQQGEwJHRTEQMA4GA1UEChMHVEVTVE5FVDESMBAGA1UEAwwJ
+NSN0ZXN0ZXI1MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDY3hGtfgn3ipYE
+e0+HSzxE1wCmfpQfM5PugfUgsLPDjfYBdkTnsOm1FHfQT9+TCrSJ6Lyk57VhUFTS
+6q9oYmpvhKvYFz3ea40TnKqN8hJASaXZC2/HzgucT4mzy10/anw6fQTvHUXf4eL6
+dQ7NXsLh11bbnw3bf4/+rXnkvHhp5wIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAIY3
+gG3+ihU47Ws+HUF/8GptA/JFcmJSsVJMj+hTJM80mBMnMz+JzcT01cyrWa2wVjmh
+koIli0m/NokyhuetEHqF2cisULmu6GDm0l6+xIMO4TScy4RhOlmA+ZLCW1+qgpfh
+wCCiARXpVIeYpQW5p7X4Cyz/5wH1wQfv/+Ejb7La
+-----END CERTIFICATE-----
diff --git a/tests/0338.pem b/tests/0338.pem
new file mode 100755
index 0000000..ff2fac2
--- /dev/null
+++ b/tests/0338.pem
@@ -0,0 +1,45 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 824 (0x338)
+ Signature Algorithm: md5WithRSAEncryption
+ Issuer: O=TESTNET, CN=TESTNET CA
+ Validity
+ Not Before: Nov 25 18:08:24 2008 GMT
+ Not After : Nov 25 18:08:24 2009 GMT
+ Subject: O=TESTNET, CN=6#tester6
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:9e:22:94:99:93:d7:7e:d9:08:b3:e4:eb:54:c7:
+ 66:17:ee:e7:a1:d5:4c:e9:3f:36:1e:72:d7:87:56:
+ 1d:10:79:b9:46:20:11:ce:4d:d5:8d:15:5b:e5:58:
+ 0b:11:67:96:ba:56:44:e6:2f:b3:6f:02:f2:a0:22:
+ f6:fb:ae:22:04:9e:1f:0f:0b:a0:84:8d:94:4d:b3:
+ 12:00:78:bf:1a:a2:18:85:fc:45:61:37:66:df:90:
+ a9:01:33:a4:d8:a5:43:b8:82:f8:3d:2f:d7:f2:fd:
+ b0:0d:3b:fa:a0:2d:be:24:fc:fa:81:eb:26:59:3f:
+ fa:b6:57:64:1a:33:1b:3d:65
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: md5WithRSAEncryption
+ 6e:35:5f:f5:b8:fb:54:ba:b7:44:d1:7f:e4:72:32:9d:06:8f:
+ 8f:46:e3:e1:5a:f1:e7:e2:4f:b3:55:fb:4a:37:fa:6d:c2:cf:
+ 43:7c:67:12:bf:c2:3a:73:d2:60:40:cc:f2:38:a9:19:4b:bf:
+ 3c:58:3b:98:59:d1:05:1a:1d:ce:11:9e:51:b2:f9:99:eb:65:
+ 38:f1:b1:95:d5:ad:fa:a1:0b:5c:70:98:19:6b:19:5f:53:62:
+ 58:ad:2d:0f:ed:dd:04:94:ce:81:c6:25:53:4a:7f:28:e4:9d:
+ f1:6a:ce:c4:fa:2d:42:5c:8a:12:c3:b5:a8:c0:ca:bf:3b:00:
+ 3c:27
+-----BEGIN CERTIFICATE-----
+MIIBvTCCASYCAgM4MA0GCSqGSIb3DQEBBAUAMCcxEDAOBgNVBAoTB1RFU1RORVQx
+EzARBgNVBAMTClRFU1RORVQgQ0EwHhcNMDgxMTI1MTgwODI0WhcNMDkxMTI1MTgw
+ODI0WjAmMRAwDgYDVQQKEwdURVNUTkVUMRIwEAYDVQQDDAk2I3Rlc3RlcjYwgZ8w
+DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJ4ilJmT137ZCLPk61THZhfu56HVTOk/
+Nh5y14dWHRB5uUYgEc5N1Y0VW+VYCxFnlrpWROYvs28C8qAi9vuuIgSeHw8LoISN
+lE2zEgB4vxqiGIX8RWE3Zt+QqQEzpNilQ7iC+D0v1/L9sA07+qAtviT8+oHrJlk/
++rZXZBozGz1lAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAbjVf9bj7VLq3RNF/5HIy
+nQaPj0bj4Vrx5+JPs1X7Sjf6bcLPQ3xnEr/COnPSYEDM8jipGUu/PFg7mFnRBRod
+zhGeUbL5metlOPGxldWt+qELXHCYGWsZX1NiWK0tD+3dBJTOgcYlU0p/KOSd8WrO
+xPotQlyKEsO1qMDKvzsAPCc=
+-----END CERTIFICATE-----
diff --git a/tests/0339.pem b/tests/0339.pem
new file mode 100755
index 0000000..77d09ae
--- /dev/null
+++ b/tests/0339.pem
@@ -0,0 +1,45 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 825 (0x339)
+ Signature Algorithm: md5WithRSAEncryption
+ Issuer: O=TESTNET, CN=TESTNET CA
+ Validity
+ Not Before: Dec 23 22:48:58 2008 GMT
+ Not After : Dec 23 22:48:58 2009 GMT
+ Subject: O=TESTNET, CN=8#tester8
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:d8:30:82:43:97:15:a9:ce:c2:ed:dd:55:8e:26:
+ 5e:83:57:31:a8:e8:a7:63:0d:5e:6d:e1:bc:03:f6:
+ e1:c6:2b:c4:7e:d0:04:ee:a7:9f:9d:6d:a1:68:1c:
+ 31:c1:7f:70:0e:e3:51:48:46:d5:01:33:bc:f9:b1:
+ bc:1e:bb:ac:85:de:10:65:1d:55:da:51:b9:d4:c0:
+ 4d:07:1f:65:fe:8e:ab:7b:ef:d6:16:67:ab:62:6e:
+ 42:f9:98:a6:f2:02:a2:c9:4e:d5:db:fc:3a:cb:df:
+ 2b:75:7e:ae:55:8d:7d:6e:a1:4b:95:c4:76:c5:a6:
+ 87:6a:12:25:1a:dd:e1:be:a3
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: md5WithRSAEncryption
+ 6d:5c:44:a1:e9:98:f8:ed:e7:b9:d9:3f:77:64:de:03:76:4e:
+ 34:82:22:73:cb:f0:77:1b:b5:52:ff:72:64:1d:b7:cf:ad:b1:
+ 68:eb:51:50:ca:af:1b:4c:db:d6:3e:cf:b6:45:01:1c:8c:14:
+ f0:68:31:5e:38:8c:5a:49:69:0a:bd:9d:5d:8d:7a:a0:8e:03:
+ 73:86:79:ab:49:f6:4b:ff:96:12:a4:ee:cf:69:b5:33:21:03:
+ 2c:d2:58:31:33:9b:be:c1:a2:17:f9:06:16:f5:c0:07:e3:a5:
+ e5:e5:41:46:40:47:14:58:f9:33:91:08:2f:ae:63:96:47:0f:
+ 0a:82
+-----BEGIN CERTIFICATE-----
+MIIBvTCCASYCAgM5MA0GCSqGSIb3DQEBBAUAMCcxEDAOBgNVBAoTB1RFU1RORVQx
+EzARBgNVBAMTClRFU1RORVQgQ0EwHhcNMDgxMjIzMjI0ODU4WhcNMDkxMjIzMjI0
+ODU4WjAmMRAwDgYDVQQKEwdURVNUTkVUMRIwEAYDVQQDDAk4I3Rlc3RlcjgwgZ8w
+DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANgwgkOXFanOwu3dVY4mXoNXMajop2MN
+Xm3hvAP24cYrxH7QBO6nn51toWgcMcF/cA7jUUhG1QEzvPmxvB67rIXeEGUdVdpR
+udTATQcfZf6Oq3vv1hZnq2JuQvmYpvICoslO1dv8OsvfK3V+rlWNfW6hS5XEdsWm
+h2oSJRrd4b6jAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAbVxEoemY+O3nudk/d2Te
+A3ZONIIic8vwdxu1Uv9yZB23z62xaOtRUMqvG0zb1j7PtkUBHIwU8GgxXjiMWklp
+Cr2dXY16oI4Dc4Z5q0n2S/+WEqTuz2m1MyEDLNJYMTObvsGiF/kGFvXAB+Ol5eVB
+RkBHFFj5M5EIL65jlkcPCoI=
+-----END CERTIFICATE-----
diff --git a/tests/033B.pem b/tests/033B.pem
new file mode 100644
index 0000000..11d38b6
--- /dev/null
+++ b/tests/033B.pem
@@ -0,0 +1,45 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 827 (0x33b)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: O=Testing certificates, CN=TEST ROOT CERT
+ Validity
+ Not Before: Aug 1 23:51:12 2009 GMT
+ Not After : Dec 26 23:51:12 2010 GMT
+ Subject: O=TESTLAB, CN=10#tester10
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:fa:d3:3f:4b:11:b2:ec:e6:a9:be:68:22:9c:ee:
+ e3:ad:58:b1:bf:95:33:7b:0e:cc:3a:30:51:ad:be:
+ a2:af:c7:89:1d:70:4b:98:f4:e5:db:d8:34:09:30:
+ 85:0e:7d:6b:0e:bb:21:73:38:dc:cb:41:92:2a:5f:
+ b6:9f:53:80:a0:0f:a7:dd:86:17:02:ea:5a:fe:88:
+ df:c9:3e:72:20:e3:3d:3d:9b:60:99:8f:bf:3a:34:
+ 26:5d:07:e8:02:3c:32:ea:7d:48:ce:ee:1a:43:01:
+ 64:cc:55:81:6b:11:ec:61:1a:e6:07:be:06:b6:4a:
+ 7e:75:76:05:f1:d3:60:00:ff
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: sha1WithRSAEncryption
+ 00:1e:27:4c:bf:cb:95:f4:89:2c:b3:d2:52:6b:04:2f:87:91:
+ 07:b2:75:a7:00:7d:82:37:e8:a7:f8:6a:87:cb:27:76:11:05:
+ 0f:fb:25:f7:03:6e:60:44:6d:92:09:96:5f:f4:15:ce:26:37:
+ 8c:cc:36:92:49:1a:c9:5f:7b:1e:b0:3e:f5:bc:41:2e:0a:b0:
+ 6e:e1:5b:73:c6:26:f7:f3:8b:17:11:70:71:d5:5d:f4:32:7a:
+ 2a:ed:43:38:bf:5a:73:42:d6:f8:44:78:24:79:d5:ea:cf:cd:
+ 64:54:d4:df:7e:e3:05:9c:de:d9:8c:71:3a:62:27:30:cd:74:
+ 3c:15
+-----BEGIN CERTIFICATE-----
+MIIB0DCCATkCAgM7MA0GCSqGSIb3DQEBBQUAMDgxHTAbBgNVBAoTFFRlc3Rpbmcg
+Y2VydGlmaWNhdGVzMRcwFQYDVQQDEw5URVNUIFJPT1QgQ0VSVDAeFw0wOTA4MDEy
+MzUxMTJaFw0xMDEyMjYyMzUxMTJaMCgxEDAOBgNVBAoTB1RFU1RMQUIxFDASBgNV
+BAMMCzEwI3Rlc3RlcjEwMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD60z9L
+EbLs5qm+aCKc7uOtWLG/lTN7Dsw6MFGtvqKvx4kdcEuY9OXb2DQJMIUOfWsOuyFz
+ONzLQZIqX7afU4CgD6fdhhcC6lr+iN/JPnIg4z09m2CZj786NCZdB+gCPDLqfUjO
+7hpDAWTMVYFrEexhGuYHvga2Sn51dgXx02AA/wIDAQABMA0GCSqGSIb3DQEBBQUA
+A4GBAAAeJ0y/y5X0iSyz0lJrBC+HkQeydacAfYI36Kf4aofLJ3YRBQ/7JfcDbmBE
+bZIJll/0Fc4mN4zMNpJJGslfex6wPvW8QS4KsG7hW3PGJvfzixcRcHHVXfQyeirt
+Qzi/WnNC1vhEeCR51erPzWRU1N9+4wWc3tmMcTpiJzDNdDwV
+-----END CERTIFICATE-----
diff --git a/tests/033C.pem b/tests/033C.pem
new file mode 100644
index 0000000..d8c86d4
--- /dev/null
+++ b/tests/033C.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIB2zCCAUQCAgM8MA0GCSqGSIb3DQEBBQUAMDgxHTAbBgNVBAoTFFRlc3Rpbmcg
+Y2VydGlmaWNhdGVzMRcwFQYDVQQDEw5URVNUIFJPT1QgQ0VSVDAeFw0wOTA4MDMw
+NDQ1MDlaFw0xMDEyMjgwNDQ1MDlaMDMxGzAZBgNVBAoTElRFU1RFUiBDRVJUSUZJ
+Q0FURTEUMBIGA1UEAwwLMTEjdGVzdGVyMTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0A
+MIGJAoGBAJQ/Jxi48pGxbV0Tt8hUZVfBq2IrTUf+2TZVf0QWKHpVnWFkdEQQVAvZ
+LJI0H9j0IJva/5vaa7X5gmmXP6fubkmQVAwzZVYRiLq87wvwErZBgG1cwEbmfE90
+xrcPSuOPUpm120BbcSg1xEYiSHHCR2mTT4D/d2PQao9/4lC2qk5NAgMBAAEwDQYJ
+KoZIhvcNAQEFBQADgYEAh7dzS6D0Xe7XMSxBJAStcgd5GgyFoUr4lT22KowY1UPM
+SuHCfTv9S8B3MvmnRedflPyMGdzcAM2ak8kH0hOImWESeUHGWLs6222R1XxCX2QB
+jB+RBxU4HlCZaCzOrHtsvykcjgCUEgbDIOH4Qg7RacV8GtoF+p0upjHNrqoaito=
+-----END CERTIFICATE-----
diff --git a/tests/033D.pem b/tests/033D.pem
new file mode 100644
index 0000000..1c11dc2
--- /dev/null
+++ b/tests/033D.pem
@@ -0,0 +1,147 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 829 (0x33d)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=GE, ST=Georgia, CN=Pica Pica Test CA
+ Validity
+ Not Before: Mar 6 20:40:52 2011 GMT
+ Not After : Apr 7 20:40:52 2021 GMT
+ Subject: ST=Georgia, CN=12#\xD1\x82\xD0\xB5\xD1\x81\xD1\x82\xD0\xB5\xD1\x8012
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (4096 bit)
+ Modulus (4096 bit):
+ 00:92:bc:3b:f1:a7:33:44:ea:9a:a2:90:f5:a2:ec:
+ 30:41:83:4d:63:2a:28:00:9e:bb:37:f6:05:e8:84:
+ e2:d0:9b:1e:cf:db:49:6f:bc:f1:71:2a:62:ce:6b:
+ aa:d1:c8:aa:75:79:eb:e0:0b:ef:12:19:8b:44:d8:
+ dd:71:ca:ef:6d:6e:db:38:18:e0:56:6a:2c:66:76:
+ 86:73:fa:97:06:2e:8e:d8:bf:5f:b5:eb:65:4c:a8:
+ fd:6b:b5:3e:8f:db:e4:0f:2a:e6:9e:a1:3e:55:a0:
+ 74:d6:0d:55:ca:8f:bc:39:75:fb:29:f3:4d:10:7a:
+ b8:c9:e5:4d:bf:8b:b6:7d:c6:26:e5:12:2d:94:fb:
+ 87:1f:da:cb:c3:c8:0e:20:64:0f:e8:d8:4e:8d:1a:
+ 51:76:b0:6a:fc:25:b8:bf:5c:7a:b4:05:ce:5e:52:
+ bb:e9:1f:4d:b8:20:02:80:74:94:3c:92:de:f0:4b:
+ 98:6e:d0:93:94:b8:ff:79:c1:22:86:81:b9:d6:64:
+ 8c:cf:91:20:7c:f8:89:88:f0:04:63:86:55:f0:ed:
+ b9:fa:c8:b6:1c:5b:1d:e8:7d:16:84:90:fc:21:f2:
+ ce:b7:ad:f3:1b:16:04:3c:1d:d2:0f:8c:90:2d:20:
+ e8:93:b3:a9:3b:91:47:f9:cb:32:c8:4d:00:fb:ce:
+ a0:8d:e2:79:61:2a:20:ab:3e:c3:4b:3f:ea:c2:c2:
+ d0:78:48:8c:62:92:02:6d:6e:0c:a9:5e:52:c8:b1:
+ 3a:e8:0c:2e:4b:53:db:b2:f6:c1:86:96:32:11:34:
+ 88:ae:9a:a0:f5:9c:15:77:26:11:8c:30:be:67:d4:
+ ac:f4:44:f3:28:f2:01:35:69:d4:72:e0:d8:bc:b5:
+ f1:11:70:39:7a:04:68:d6:8e:e2:52:2e:ff:07:26:
+ 27:72:8e:c3:c6:54:6b:cc:ea:e1:dc:74:4d:7c:f8:
+ d7:0e:02:18:80:48:a7:cb:78:30:00:59:61:27:9b:
+ 9c:74:d0:7c:ea:56:f9:78:25:d8:3c:38:f3:20:2d:
+ b5:dc:71:7b:2c:5c:76:eb:0e:07:33:f1:03:76:98:
+ 4b:c2:02:c7:1c:b9:7d:c3:55:4e:9d:f8:b8:f7:99:
+ a3:d2:ea:f0:21:77:a3:35:bc:09:a8:7e:af:6a:b7:
+ 10:0b:a6:c8:50:ea:52:6f:c7:c3:83:8a:7e:7f:22:
+ 22:cd:40:d9:0a:33:0d:68:b3:b2:c8:84:7e:47:8c:
+ 23:0a:b1:f0:b9:6b:92:b8:7e:a9:80:92:06:7f:8a:
+ 4b:9e:1a:22:24:d2:98:38:38:92:ab:b1:8f:b1:a3:
+ 1d:f6:e0:4c:21:44:d5:81:8f:98:62:68:f2:c6:7c:
+ 53:c8:07
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: sha1WithRSAEncryption
+ 68:55:c8:31:fc:df:a8:30:ec:b5:cf:88:26:38:e5:39:34:85:
+ 08:4f:c3:f0:17:ca:d4:1a:62:5d:8e:98:ac:1d:b7:16:a3:74:
+ 78:1f:e6:e2:f6:12:1e:14:1d:e3:86:68:52:2c:11:80:41:3e:
+ ed:2e:be:23:53:40:d8:8e:17:20:af:7f:e3:20:7b:f4:2c:b2:
+ 84:93:3c:31:19:2a:70:b4:e7:a3:f7:dd:19:45:f8:5d:c2:da:
+ 1a:56:22:48:26:4e:d2:cc:be:60:ba:00:c3:3c:c4:77:56:8e:
+ 1d:33:7d:0b:c1:6f:bc:61:5a:2f:a4:19:91:3a:5b:d2:d9:9e:
+ 7e:87:6a:79:80:f4:69:66:a0:77:61:d3:33:de:c1:1f:09:e3:
+ 3b:9e:b1:c3:c8:3d:e5:ae:76:b8:7d:73:4e:2b:ca:b2:e7:dc:
+ 01:5a:fa:b1:0b:e3:0d:af:2a:65:86:66:a3:0d:9d:7a:bc:f1:
+ 55:f0:24:45:a0:ed:01:f0:b4:79:cb:13:b6:48:4d:12:8b:ed:
+ bf:96:5b:b8:89:4d:15:96:75:1c:2b:91:0a:ff:43:6a:c0:8a:
+ 1c:fd:da:25:f2:a5:c4:83:2f:36:6a:3a:50:72:2a:e2:bd:31:
+ c4:fb:56:5b:b5:b1:25:d3:7e:25:35:b9:08:c1:cf:4d:5f:29:
+ 86:a5:1e:a9:9b:6b:14:75:63:fe:fd:47:24:df:10:2d:1a:3e:
+ f5:28:f4:0f:80:6c:d3:37:d3:66:9d:08:dc:c4:30:7a:76:8d:
+ 32:b3:a1:ee:c5:e9:8d:dd:93:87:c6:91:8b:94:af:7f:a2:b3:
+ b5:91:53:2c:c6:b7:2a:05:a1:74:b2:5c:12:80:43:cc:1a:6b:
+ 6c:f5:66:d7:60:b2:04:e5:cf:e2:59:2e:44:5b:c9:48:b4:d3:
+ 4d:76:15:c7:85:2d:38:de:64:b3:03:7d:7d:82:8a:5f:a4:04:
+ ed:f3:21:35:4d:7e:de:a0:08:f8:b0:c5:9e:a0:d5:a9:a4:bd:
+ ac:c0:9a:cd:c4:21:75:52:cc:0c:53:87:b7:68:f8:6c:c0:49:
+ 0b:30:fd:20:6e:05:de:56:55:30:d8:ce:f9:62:60:50:af:5d:
+ 59:fa:d0:f5:76:e1:42:50:7c:59:8f:b6:a4:b9:de:3d:72:f9:
+ d7:86:11:1f:f3:d0:b4:c9:30:b1:3e:37:04:5d:aa:09:06:53:
+ 3a:67:be:1d:8a:95:99:d7:ee:4a:69:9f:7a:a2:52:65:b0:87:
+ 89:2f:e2:dc:47:af:9d:4b:d0:0b:29:1b:d6:1d:95:a7:44:18:
+ 99:c1:31:58:d7:96:6c:68:56:78:14:4a:27:ad:79:2c:c4:f7:
+ 72:ae:b3:63:71:e1:10:f2:36:4e:0e:0b:3d:05:18:71:0b:3d:
+ 2d:44:a9:48:03:83:7c:72:51:5d:78:55:9d:f2:8c:32:56:d1:
+ 60:51:d1:53:8a:c0:56:e2:39:15:c3:c3:fb:57:e4:5f:3d:1c:
+ b5:62:e2:e3:da:ea:fe:b3:54:24:7e:44:55:24:56:29:91:2f:
+ 56:ce:ef:f7:da:f1:ad:04:48:92:94:cb:3e:45:3f:34:bf:78:
+ 0a:f6:42:bd:2d:fc:c0:c1:a6:c3:00:9a:29:7a:c0:b7:06:23:
+ 3b:28:44:fd:e3:6b:b7:0e:aa:39:1f:20:aa:22:b1:56:0b:d1:
+ 67:e6:8a:95:cb:f6:76:9d:87:1e:01:0e:dd:0c:d9:13:3b:ca:
+ 54:0d:f1:c4:5a:49:05:ba:d5:ad:38:12:2e:81:7d:6f:c3:12:
+ 34:6a:02:28:b2:8f:b0:76:02:98:ab:a8:a4:f5:97:d7:5f:84:
+ 94:87:f7:35:c0:ab:53:01:98:1c:63:35:eb:f5:bc:6f:6f:68:
+ 7f:ba:5b:b2:32:fb:11:15:6b:1e:ae:8a:07:cd:43:4e:b1:4c:
+ e2:4c:ab:46:be:8f:76:6f:40:8b:7a:f3:43:7c:3e:8b:7c:2b:
+ 90:6d:d1:ac:11:4c:69:5d:ea:2a:78:46:bd:f2:5f:9e:f0:26:
+ d4:8f:63:4f:fd:69:5b:ce:52:bf:91:0a:3a:42:bd:c1:66:00:
+ fb:49:38:9b:ba:b1:b1:b2:36:7b:e9:e7:c4:de:45:1e:46:6d:
+ 05:26:8a:31:cc:45:dc:bc:ab:b2:4c:40:3c:e3:d8:48:c9:ea:
+ 31:36:85:6b:ce:3a:de:f8:50:4c:57:de:10:74:6f:c7:69:3c:
+ f4:90:98:b1:6e:7d:44:d4:58:e4:8e:89:a8:96:50:a5:f9:20:
+ 5c:77:6f:7f:12:c9:e6:93:0c:0c:96:c2:1a:87:87:ca:44:c9:
+ 5a:e2:c6:b3:9d:65:b6:af:69:75:5c:1f:2e:a9:76:85:af:df:
+ ba:a4:d7:be:1d:96:de:bf:19:fe:46:6a:9e:8c:1e:0a:f2:19:
+ 53:e2:0a:74:84:3c:24:53:95:fb:4a:66:5e:4a:e9:e7:ef:dd:
+ eb:99:bb:66:a2:26:2f:ef:0a:00:9a:8b:d8:c4:c1:d8:d5:7c:
+ 46:c0:aa:2d:c1:c4:3c:31:ee:6e:6a:54:27:a0:e2:67:76:b9:
+ 2b:55:33:a7:01:06:7b:0c:94:4e:72:d3:11:63:63:ae:a5:b6:
+ a5:cc:81:9b:c8:70:5a:25:c0:2c:20:23:d5:5f:9e:b0:21:5d:
+ 64:59:67:22:ee:19:7a:c1:05:69:03:f0:64:22:dc:af:25:46:
+ 39:a0:0e:0e:ba:f8:86:55:cd:44:15:0a:be:b0:99:9f
+-----BEGIN CERTIFICATE-----
+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
+-----END CERTIFICATE-----
diff --git a/tests/033E.pem b/tests/033E.pem
new file mode 100644
index 0000000..cb66a01
--- /dev/null
+++ b/tests/033E.pem
@@ -0,0 +1,149 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 830 (0x33e)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=GE, ST=Georgia, CN=Pica Pica Test CA
+ Validity
+ Not Before: Mar 6 20:45:25 2011 GMT
+ Not After : Apr 7 20:45:25 2021 GMT
+ Subject: C=GE, ST=Georgia, O=\xD1\x82\xD0\xB5\xD1\x81\xD1\x82\xD0\xBE\xD0\xB2\xD0\xB0\xD1\x8F \xD0\xBB\xD0\xB0\xD0\xB1\xD0\xBE\xD1\x80\xD0\xB0\xD1\x82\xD0\xBE\xD1\x80\xD0\xB8\xD1\x8F, CN=13#\xE1\x83\xA2\xE1\x83\x94\xE1\x83\xA1\xE1\x83\xA2\xE1\x83\x94\xE1\x83\xA0\xE1\x83\x98N13
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (4096 bit)
+ Modulus (4096 bit):
+ 00:ce:32:c4:c9:fc:3e:9d:da:94:a8:8d:b6:f0:1a:
+ 45:4b:8d:14:51:86:bc:c4:05:d3:3c:d3:5f:17:e3:
+ 60:7e:71:63:51:7c:f4:5c:23:9b:d4:61:c7:49:93:
+ 04:c2:11:d5:a6:95:25:00:14:f2:c2:b1:03:5a:39:
+ 31:00:85:7f:7f:35:25:e2:7d:02:41:92:cc:10:12:
+ a8:6e:ce:15:d5:26:17:c7:44:7a:f1:b4:37:01:57:
+ f9:c9:ad:9c:31:9d:27:a2:e1:15:e9:a9:d0:bb:4c:
+ a9:2d:91:99:4c:42:a1:fd:36:66:1a:17:dd:0c:96:
+ 51:ca:d0:16:83:45:6b:28:8b:89:f1:5d:3b:e1:55:
+ 18:e2:a5:2b:b6:c5:ac:a9:5f:f7:0f:9c:3a:5c:b0:
+ 4a:37:67:9e:79:46:dc:7e:10:6e:af:fd:82:3c:9a:
+ 19:ad:2d:9c:b4:b8:b8:61:6b:6a:53:62:82:97:04:
+ 16:98:f2:ee:f4:e1:2d:25:39:d8:37:e9:f4:b2:1e:
+ 33:22:c5:5f:56:11:3b:4c:34:ef:df:1f:ff:3c:65:
+ 79:15:8c:53:64:ad:d9:a1:38:e2:80:ab:a0:2f:ac:
+ d0:26:3a:b0:cc:cd:28:92:59:69:4a:bd:66:e7:4b:
+ 15:db:a9:8c:af:18:e6:d6:9d:b8:be:47:88:9c:b7:
+ 75:c8:6e:bd:01:12:23:93:ce:37:86:01:fc:b1:ff:
+ d5:54:38:bf:ff:ac:aa:50:d4:4f:bf:0b:86:0f:08:
+ bf:1e:4b:8a:3f:cb:3b:21:3b:5a:0d:39:dd:fe:f1:
+ ed:38:d1:56:fe:8e:27:a0:f2:ab:96:e7:07:5b:49:
+ 67:0d:e3:b4:bf:2c:35:55:15:e0:25:38:49:89:79:
+ 71:6c:12:0b:53:ab:8a:b5:73:3f:4b:3f:38:c4:63:
+ 76:c9:3f:db:34:70:fd:ba:a7:a6:7f:e1:8f:c5:1e:
+ e0:59:1c:1a:3f:d7:3b:e6:a6:97:03:d2:e1:bf:d9:
+ 6c:b5:54:04:4f:0b:22:d9:43:c5:71:b7:47:df:a3:
+ 5f:c1:f4:15:03:ad:5a:25:af:0c:9b:ca:14:f3:ec:
+ e1:c4:37:50:64:70:10:e9:9f:c7:35:88:d3:93:49:
+ ce:18:35:69:ce:70:e3:4d:f8:04:0e:e1:88:d8:c3:
+ f5:0c:e3:20:32:e8:4d:b5:67:22:6e:bc:38:6d:54:
+ 65:73:a7:e4:83:fc:2c:dc:88:27:ef:08:98:9a:31:
+ 3e:61:90:87:55:e4:a0:28:a7:13:aa:b9:4e:3a:b7:
+ 31:71:6e:6e:b9:e0:f1:c1:b7:38:3d:3b:f4:fb:8e:
+ 6d:f8:ce:8b:52:e2:9b:2b:7c:c1:db:5e:3b:86:47:
+ e4:18:15
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: sha1WithRSAEncryption
+ 56:f0:e1:9d:66:e3:a1:05:c5:2e:73:89:32:a2:48:05:4e:cd:
+ 31:2e:32:70:09:4e:73:b1:c5:2e:c7:cf:11:a3:76:6b:b2:d9:
+ 95:c5:69:66:40:a2:e2:a2:91:06:bc:62:38:f2:30:81:9f:fe:
+ ea:7b:27:9a:81:8e:0d:ba:6e:fa:72:3b:a1:d7:33:25:98:e0:
+ 74:31:fe:fa:bf:a6:14:8f:f2:10:a9:b9:c4:63:d9:3c:4f:c1:
+ cb:6f:8d:7d:57:3a:b4:99:b2:4d:58:9f:60:3f:bf:82:2e:5a:
+ c2:0e:66:83:cb:66:30:1e:41:67:1b:6d:1b:d5:34:ca:00:f6:
+ 71:69:68:1c:4e:7e:0d:1a:14:b3:ec:61:a2:9c:98:f6:17:f0:
+ 6f:b6:64:f7:b6:88:4a:63:86:c1:b3:16:40:ef:80:22:a9:ae:
+ 28:4e:f1:48:1a:3b:14:a9:b5:ce:4d:7a:ab:56:13:cc:1c:86:
+ 1a:b1:d9:9b:ae:93:ce:08:e6:b7:d1:5c:bd:7f:19:90:b1:39:
+ 61:2d:06:2b:a8:b5:07:dd:d9:f3:10:7e:9f:b8:9b:38:e6:7b:
+ c6:e3:f4:ba:1b:9d:a3:1d:42:06:53:0d:22:9a:a7:68:ac:25:
+ 17:b7:e8:be:42:e8:86:2b:b6:68:51:47:86:13:7e:83:57:aa:
+ 6b:66:35:31:ad:ac:ce:2c:bf:8c:ad:92:2e:41:9c:a9:7a:63:
+ 1a:2c:85:29:da:b3:23:54:de:cb:23:0c:1d:b1:23:b6:4c:fd:
+ c2:29:e3:69:50:93:6a:cc:b0:9f:f8:b8:5c:80:61:b3:a8:0d:
+ 48:33:b8:7f:04:94:ea:23:35:e0:29:10:16:a6:b4:52:87:34:
+ dd:5b:0e:d2:c0:03:65:48:ed:4c:fb:d1:97:04:de:5f:b3:d6:
+ 78:44:41:95:7a:4e:fd:4f:12:83:c3:e5:b8:95:76:57:83:8d:
+ a1:96:1d:35:b6:4c:25:3e:1b:66:07:46:38:22:6d:c1:6a:21:
+ 68:31:e1:29:a0:ee:b4:dd:a0:48:03:ce:72:aa:2a:e5:a0:ec:
+ d6:a6:ca:1e:a9:fc:2f:35:e5:3d:f8:e6:f4:d4:cf:ed:4b:f0:
+ 65:36:f4:3c:ef:32:c8:17:18:18:62:4a:06:61:92:59:80:3e:
+ c5:c0:1e:4a:37:fa:70:68:c0:62:e0:ee:4b:51:ec:87:02:e5:
+ 2f:2b:48:a5:72:f2:91:c8:fd:10:4b:a5:6f:ea:93:01:af:db:
+ 05:3a:f7:7c:07:53:98:da:c5:c0:7d:df:45:7e:3e:09:54:b9:
+ 77:20:85:b9:41:35:7d:29:f7:c9:f6:f6:4f:db:bd:04:03:8c:
+ 04:5c:35:7f:7c:5d:b6:6d:7a:3e:ef:6c:4d:44:b5:24:0d:80:
+ fe:e4:23:e2:ec:3b:22:41:8a:9b:c5:c5:f3:d3:47:ef:89:e9:
+ 64:e1:96:e6:59:0e:06:ba:65:98:2a:04:05:e7:1a:7b:8c:ea:
+ 16:e0:31:a3:33:76:62:f6:33:87:20:57:85:9c:b5:05:cc:7b:
+ 31:a4:cc:4c:92:47:14:bb:80:45:90:b4:b4:9e:17:09:52:0e:
+ 82:bd:6e:41:53:6f:cb:23:3f:76:6c:c2:4b:65:53:bb:f1:8d:
+ b7:61:d5:71:85:0b:4e:d0:4d:f8:59:48:b1:39:23:d1:53:87:
+ c6:f9:e4:64:09:b4:30:42:bd:67:60:b9:77:3c:57:90:96:db:
+ f1:ef:fa:33:4c:28:85:5b:eb:32:be:ed:cc:73:f7:4e:9c:08:
+ 86:40:b0:fe:dd:0d:48:89:22:e5:b8:4f:ab:e2:08:43:08:02:
+ a7:8a:2a:56:40:6d:53:b7:3e:7d:66:6c:dc:03:4c:b6:81:41:
+ d7:b6:9d:82:b4:ba:04:44:f2:3a:c6:f7:aa:29:3a:4e:81:5b:
+ 77:b7:c8:f0:fb:64:79:30:f6:87:55:12:d6:a2:d8:b3:44:bf:
+ 71:2e:52:dd:b2:3b:d6:f8:7a:c5:24:bc:3c:76:82:8c:0b:90:
+ 9b:1f:a1:4a:68:a8:1b:1d:5b:34:d0:e3:30:2a:2a:8a:35:dd:
+ ad:ec:2d:53:4e:cc:57:47:ff:16:ad:af:be:75:06:4f:dd:e4:
+ 15:b1:8a:77:fb:bd:43:2f:45:63:a4:4f:53:ea:5f:09:43:ca:
+ 27:3d:47:55:96:c7:1f:d1:df:1b:1b:fe:bc:54:af:ac:10:6f:
+ 4b:ff:df:47:a9:d3:e7:44:85:8c:7c:77:97:11:78:40:e0:ce:
+ 1a:ca:7a:98:20:ff:36:7f:e1:37:a2:f2:f1:c4:a9:4f:68:1f:
+ e6:91:fc:9d:32:61:6a:59:9a:55:09:07:e9:b1:6e:4f:22:ad:
+ d9:1b:83:25:be:11:e3:54:dc:a5:f0:fb:74:5a:62:67:4f:6b:
+ bb:26:fb:61:ae:76:fb:2c:26:3a:d3:1e:bf:3b:c8:bb:99:78:
+ 62:76:b2:76:6a:1b:91:25:c7:ee:5c:cb:51:6f:e8:24:ba:78:
+ ec:3f:c3:56:90:9f:fb:0c:d7:b6:59:28:7b:49:93:f4:3f:d5:
+ 91:37:e5:ac:a9:d0:0b:93:6c:1b:bd:7b:27:11:6f:40:82:1d:
+ 0d:83:4c:f9:20:27:6e:b9:a0:9b:36:ef:db:12:fc:7c:58:f5:
+ a4:bc:86:1d:66:4a:a1:40:13:1f:36:f1:4e:01:f5:02:95:69:
+ 47:34:3d:43:18:b2:0d:ba:f3:83:b1:27:d6:17:cf:ca
+-----BEGIN CERTIFICATE-----
+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
+sg2684OxJ9YXz8o=
+-----END CERTIFICATE-----
diff --git a/tests/033F.pem b/tests/033F.pem
new file mode 100644
index 0000000..398a75c
--- /dev/null
+++ b/tests/033F.pem
@@ -0,0 +1,149 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 831 (0x33f)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=GE, ST=Georgia, CN=Pica Pica Test CA
+ Validity
+ Not Before: May 25 21:19:01 2012 GMT
+ Not After : Jun 27 21:19:01 2022 GMT
+ Subject: C=GE, ST=Georgia, O=\xD1\x82\xD0\xB5\xD1\x81\xD1\x82\xD0\xBE\xD0\xB2\xD0\xB0\xD1\x8F \xD0\xBB\xD0\xB0\xD0\xB1\xD0\xBE\xD1\x80\xD0\xB0\xD1\x82\xD0\xBE\xD1\x80\xD0\xB8\xD1\x8F, CN=13#\xE1\x83\xA2\xE1\x83\x94\xE1\x83\xA1\xE1\x83\xA2\xE1\x83\x94\xE1\x83\xA0\xE1\x83\x98N13
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:a5:cf:04:3d:4c:c4:55:83:b4:de:d6:ac:8c:50:
+ bd:5b:16:99:ca:ee:f6:ed:9f:ca:0d:f8:4e:94:b5:
+ 56:dc:db:af:cf:af:10:7b:07:b9:b0:80:e0:66:5b:
+ d7:78:9b:cb:84:f7:4e:8c:31:e8:b4:3d:eb:dd:c9:
+ e0:20:22:9a:9c:ae:f3:35:2a:cd:66:6f:c5:bd:9e:
+ 75:a5:a3:a2:94:5b:dc:40:12:7a:bc:0d:7d:c6:89:
+ 55:15:eb:56:d6:5d:e7:54:29:ce:31:b9:30:a4:d9:
+ ee:da:72:c6:96:be:80:b4:1e:7c:7c:6e:6a:7f:7a:
+ 10:66:ed:a8:79:52:f2:3e:43:ca:97:8e:88:03:92:
+ 9e:fa:62:fd:f8:52:39:eb:20:09:71:ea:01:fc:97:
+ 6d:61:5e:8f:9d:44:6d:33:fc:de:01:df:87:42:ab:
+ b1:0a:99:e8:3a:06:67:ce:3d:06:68:90:60:8f:18:
+ 79:ec:52:35:2a:1e:1d:05:75:e9:c0:33:62:15:8e:
+ 3a:3c:e9:ec:d0:0c:4c:63:d9:82:27:46:71:2b:91:
+ 8c:e3:12:ce:75:35:52:44:a4:2b:23:65:8c:19:cf:
+ f1:37:8d:fc:a7:a8:98:0c:4a:77:82:70:ee:fe:06:
+ c5:b0:db:25:fb:b0:79:76:ec:5f:e0:3a:05:80:cb:
+ b0:17:61:18:06:6c:0c:9f:0a:cc:f1:b7:41:74:8f:
+ de:e6:3a:0c:ad:c3:ea:7d:15:b0:f1:57:61:cd:f4:
+ 91:e3:bd:e3:aa:f2:59:96:0d:1f:67:2a:50:c9:12:
+ 9e:11:3e:bf:2c:b8:4b:f7:d5:1a:ba:64:17:49:e8:
+ d7:cc:72:28:c0:1c:47:3e:c0:f7:b6:f6:7f:f1:58:
+ d5:90:db:a4:20:94:9e:8f:4c:9d:95:1f:65:98:ac:
+ c9:e8:d7:81:86:e1:bd:4d:c4:e9:c1:54:e2:92:2d:
+ fd:6b:0e:02:45:b9:a0:48:7c:74:39:ff:09:c4:ca:
+ d3:d6:22:b6:e3:b2:5a:93:82:c1:4c:db:89:41:f3:
+ 08:f1:1c:2a:dd:a7:52:be:08:d2:e0:79:a8:61:bf:
+ 3a:5d:4f:3d:e7:28:df:71:db:d9:97:5e:ac:3f:c1:
+ dd:2f:bd:9b:2c:50:69:f2:cb:1b:2c:63:86:81:2d:
+ 28:fb:15:40:85:39:d2:01:13:0f:26:bb:e5:e7:a4:
+ e9:7c:1e:02:4c:ef:2b:dc:7f:0c:48:b5:f1:ca:55:
+ 8c:59:70:24:01:3a:31:21:53:59:94:d6:e6:5c:c9:
+ 14:41:2b:f2:78:95:6c:eb:ac:eb:0d:05:38:8f:5f:
+ 93:57:0a:fc:04:d4:68:e0:89:72:ac:4d:37:c8:e6:
+ 6d:72:bd
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: sha1WithRSAEncryption
+ 9d:73:26:a1:0a:7c:84:42:d8:27:2f:8d:00:6a:97:21:2b:2c:
+ d5:7a:dc:59:24:cb:ce:4a:c5:c1:94:82:1e:e0:06:1b:76:cf:
+ c9:81:5c:a5:32:0d:cf:c1:25:27:c9:6c:5c:d7:b7:28:ec:1b:
+ 87:e8:1d:6e:60:c9:5f:48:e2:9a:a8:0f:ee:9d:c2:a3:b8:af:
+ a1:cc:10:97:0e:b5:c4:11:62:67:c5:ea:eb:84:01:6d:33:61:
+ 84:ee:51:05:a8:1e:c8:a2:60:c0:e8:23:c1:80:26:00:03:6d:
+ fb:77:60:67:cb:c9:db:f4:e0:ac:83:87:93:25:ce:ea:7a:61:
+ d3:72:4c:b9:fa:62:bc:42:87:57:4c:58:6a:72:3b:79:24:91:
+ d3:31:ae:7a:04:7e:df:e5:8b:75:2a:41:de:45:17:d3:12:1b:
+ 91:fd:85:cc:c2:66:b2:77:b8:fd:c9:51:9c:78:05:85:e6:11:
+ a4:52:f9:8a:52:96:bd:ad:58:51:8d:53:ee:f4:a2:80:95:4f:
+ f7:02:24:9d:8e:7c:c8:d9:dc:87:eb:d5:2f:6b:12:9c:04:31:
+ df:a4:f6:b1:b3:86:49:c3:28:0f:df:d5:64:fa:b9:25:42:e8:
+ 25:a3:27:d8:09:d5:02:ab:31:7c:ad:c1:1e:af:91:51:64:0d:
+ 35:28:df:e2:2d:20:6c:04:5c:5c:de:7e:8d:8a:19:54:c1:c3:
+ 20:b3:80:12:e9:68:53:2d:05:72:f0:c0:a7:31:70:29:03:f1:
+ ef:57:40:8d:94:dd:ee:4d:2a:e0:29:6b:1e:82:e0:8a:9a:06:
+ 62:df:34:06:14:e7:b0:ea:7e:a8:0b:1a:20:64:83:58:dd:22:
+ f4:fe:6d:2a:8b:1a:24:e7:03:41:5f:bf:68:0b:8a:74:4a:38:
+ 49:a3:c3:e7:e9:5c:c7:78:95:23:c0:cd:2e:5e:41:ce:d5:aa:
+ 11:e4:80:84:53:24:00:5a:53:a4:06:22:fb:3d:a1:7c:95:a4:
+ 69:d7:23:23:94:2e:7f:92:c2:8a:d7:5c:46:bc:9c:43:d0:2d:
+ 57:43:9d:5e:34:a3:e3:68:dd:47:5f:ec:a2:97:3d:22:cd:01:
+ 05:2d:ee:04:c6:3d:df:c2:6c:e3:19:de:0d:3a:fb:e1:31:64:
+ 5b:60:7c:ae:5f:53:6a:fd:d9:a1:c0:58:0b:4c:32:97:57:fd:
+ 4b:a2:cc:0d:6f:0c:cf:b3:5c:9b:be:52:2e:08:35:a9:d1:14:
+ 8c:8a:1f:23:02:c2:f2:c4:9b:e7:72:bb:8d:e9:7d:c2:e5:9b:
+ ac:8f:85:3d:86:d2:46:a8:eb:a3:2f:b9:54:1e:57:03:bc:dc:
+ 44:4a:c2:c5:f3:9e:2c:01:75:e4:08:f2:c2:e6:55:2e:38:4e:
+ 49:72:79:ca:52:5c:8f:df:41:ae:d2:63:5a:1b:f5:2c:19:d7:
+ 09:c5:a2:aa:27:2a:b7:eb:cc:eb:92:74:d5:ba:ab:a5:e6:77:
+ 83:31:ee:de:4e:0d:1d:72:1d:22:66:47:d8:f1:b8:9a:b9:b3:
+ b8:ea:94:d6:2e:8b:4e:84:57:9d:71:cc:6c:8b:d1:73:9d:eb:
+ f8:7c:2a:c0:16:59:b1:14:31:b1:24:0b:6d:bf:f1:2e:7c:f8:
+ af:8e:b4:1b:25:5d:b3:52:3d:33:45:54:13:a0:86:ae:5d:c1:
+ 81:f9:e4:48:a3:12:16:b6:e3:22:e1:ec:3f:e3:fb:9a:ca:54:
+ c6:02:ea:7b:58:1e:2b:9b:f6:8a:15:9e:c3:ad:b3:ed:f8:0b:
+ c0:6f:b9:95:ea:ea:4a:25:fe:5e:9d:9e:4c:f5:8d:b2:63:d3:
+ 98:88:1d:22:ce:ea:12:89:fd:31:85:5e:54:a6:3a:56:4a:cf:
+ c1:e2:70:ed:9f:bd:9d:e8:ea:ac:a2:77:bc:fb:63:cf:83:d0:
+ c4:83:6a:8f:a5:b2:a6:78:ae:60:c7:06:55:d6:7d:3d:5a:5d:
+ 88:4b:9f:87:bd:25:b6:1a:4a:18:28:a1:36:c0:ab:5b:87:0b:
+ b3:26:68:6a:12:6e:ba:1b:d5:98:e7:b7:0e:6b:22:35:de:ca:
+ 09:84:69:bd:7f:0b:fa:0b:2e:12:60:48:37:5d:ba:c1:67:04:
+ 93:40:95:fc:fa:40:c8:79:fe:7a:2d:ff:a8:3d:80:96:19:10:
+ 36:eb:6f:30:93:b7:c4:77:29:51:c3:99:8a:e0:50:00:b0:d5:
+ d6:ee:33:b8:a1:b4:eb:d4:35:35:66:bd:2f:2d:64:d7:67:c6:
+ 49:b5:59:77:83:28:08:56:0e:68:e8:e8:35:7d:43:b2:b8:c7:
+ b4:e6:8b:df:14:f0:a2:67:b9:b2:97:29:b5:e3:e5:46:39:ec:
+ 59:35:90:1b:1f:67:c0:fc:68:8e:25:83:67:73:8f:47:24:60:
+ bf:3d:21:06:25:e0:69:8c:7f:7d:5e:b5:4d:ab:cc:37:8f:65:
+ 51:9c:6d:7f:31:eb:12:09:82:da:32:26:98:77:6f:50:fa:5e:
+ 0c:dd:76:73:fa:14:e2:cf:81:e2:5a:a7:b5:67:f7:cd:73:b6:
+ e7:e0:27:10:b6:64:ef:92:7f:df:e4:cd:2b:89:02:27:a4:d5:
+ 29:f5:62:29:56:80:2e:53:a1:e7:b9:6c:a2:02:36:c4:59:62:
+ d2:d2:7e:d5:8c:8d:c8:93:e5:cd:b6:4f:24:30:e3:0e:06:94:
+ 25:50:f5:d0:a4:ae:24:07:4c:f5:b0:e6:54:58:fd:46
+-----BEGIN CERTIFICATE-----
+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
+riQHTPWw5lRY/UY=
+-----END CERTIFICATE-----
diff --git a/tests/033a.pem b/tests/033a.pem
new file mode 100755
index 0000000..f3091f9
--- /dev/null
+++ b/tests/033a.pem
@@ -0,0 +1,45 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 826 (0x33a)
+ Signature Algorithm: md5WithRSAEncryption
+ Issuer: O=TESTNET, CN=TESTNET CA
+ Validity
+ Not Before: Dec 23 22:49:35 2008 GMT
+ Not After : Dec 23 22:49:35 2009 GMT
+ Subject: O=TESTNET, CN=9#tester9
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:b0:d2:ba:8f:cb:8d:f3:5e:a9:05:e9:22:a0:60:
+ a8:3c:6b:c2:71:d9:f4:eb:85:d6:fd:5d:7a:8a:b4:
+ e3:1c:83:57:a7:77:56:c9:0c:20:4c:33:d9:40:92:
+ 64:f6:a2:8c:a9:77:1d:fe:b2:b9:ae:30:c8:b2:47:
+ d4:2e:2f:52:30:d8:14:e3:33:8a:47:27:68:47:4e:
+ 9c:c0:72:08:98:27:c4:d1:40:eb:97:8f:e7:75:2a:
+ f3:26:ea:7f:d4:01:3d:92:2f:e0:45:b3:97:b0:ed:
+ 19:b2:5b:f8:49:52:38:3a:c3:33:cc:c0:15:42:2b:
+ dc:f9:cb:73:94:1b:23:92:cf
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: md5WithRSAEncryption
+ 5a:17:8d:3c:f4:82:2e:1e:de:37:f7:21:49:2f:65:ca:88:c9:
+ 7e:cf:a4:92:6a:25:d7:1b:3d:35:b1:88:ca:41:6a:a7:d2:ea:
+ 45:53:39:b0:eb:99:ed:ab:10:46:71:0a:61:a4:a2:6f:9a:c1:
+ 2c:87:9e:c3:0a:b6:36:10:e9:91:e8:25:6b:da:9a:ec:09:55:
+ ea:51:6c:c9:07:21:b1:e6:83:33:1d:3a:91:4f:26:bd:d6:29:
+ e9:72:0f:72:51:31:c4:2a:29:92:ca:bb:5e:73:5c:f9:4d:19:
+ 57:d0:95:ab:bc:f0:30:6c:9f:bd:5e:a7:4d:16:0f:b3:9e:20:
+ 79:16
+-----BEGIN CERTIFICATE-----
+MIIBvTCCASYCAgM6MA0GCSqGSIb3DQEBBAUAMCcxEDAOBgNVBAoTB1RFU1RORVQx
+EzARBgNVBAMTClRFU1RORVQgQ0EwHhcNMDgxMjIzMjI0OTM1WhcNMDkxMjIzMjI0
+OTM1WjAmMRAwDgYDVQQKEwdURVNUTkVUMRIwEAYDVQQDDAk5I3Rlc3RlcjkwgZ8w
+DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALDSuo/LjfNeqQXpIqBgqDxrwnHZ9OuF
+1v1deoq04xyDV6d3VskMIEwz2UCSZPaijKl3Hf6yua4wyLJH1C4vUjDYFOMzikcn
+aEdOnMByCJgnxNFA65eP53Uq8ybqf9QBPZIv4EWzl7DtGbJb+ElSODrDM8zAFUIr
+3PnLc5QbI5LPAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAWheNPPSCLh7eN/chSS9l
+yojJfs+kkmol1xs9NbGIykFqp9LqRVM5sOuZ7asQRnEKYaSib5rBLIeewwq2NhDp
+kegla9qa7AlV6lFsyQchseaDMx06kU8mvdYp6XIPclExxCopksq7XnNc+U0ZV9CV
+q7zwMGyfvV6nTRYPs54geRY=
+-----END CERTIFICATE-----
diff --git a/tests/64kb.txt b/tests/64kb.txt
new file mode 100755
index 0000000..73aed9d
--- /dev/null
+++ b/tests/64kb.txt
@@ -0,0 +1,307 @@
+64KB_START-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-lksadddddddddddddddddjfsjdfjoaijffihbwieughwiuoehgggweghnegiowehriouuufgwueeeeeeeeeeeeeefgioasghsooooooooooofl;askdjffffff;sadlllllllllllllllllllllllllllfjjjjjjjjjjjjjjjjjjjjjjjjjjjjsaoojfoiu89390283092309203203
+-789789788888888888888888888888888899999997689996777855566666 \ No newline at end of file
diff --git a/tests/Makefile.win b/tests/Makefile.win
new file mode 100755
index 0000000..f3ecdc5
--- /dev/null
+++ b/tests/Makefile.win
@@ -0,0 +1,36 @@
+# Project: test_client_win
+# Makefile created by Dev-C++ 4.9.9.2
+
+CPP = g++.exe
+CC = gcc.exe
+WINDRES = windres.exe
+RES =
+OBJ = test_client.o ../CACA_client.o ../CACA_msgproc.o $(RES)
+LINKOBJ = test_client.o ../CACA_client.o ../CACA_msgproc.o $(RES)
+LIBS = -L"D:/Dev-Cpp/lib" ../../home_mdv_root_jr/Downloads/openssl-0.9.8h/openssl-0.9.8h/lib_a_win/libssl.a ../../../Dev-Cpp/lib/libgdi32.a ../../../Dev-Cpp/lib/libws2_32.a ../../home_mdv_root_jr/Downloads/openssl-0.9.8h/openssl-0.9.8h/lib_a_win/libcrypto.a ../../../Dev-Cpp/lib/libgdi32.a ../../../Dev-Cpp/lib/libws2_32.a
+INCS = -I"D:/Dev-Cpp/include" -I"D:/pract/home_mdv_root_jr/Downloads/openssl-0.9.8h/openssl-0.9.8h/include"
+CXXINCS = -I"D:/Dev-Cpp/lib/gcc/mingw32/3.4.2/include" -I"D:/Dev-Cpp/include/c++/3.4.2/backward" -I"D:/Dev-Cpp/include/c++/3.4.2/mingw32" -I"D:/Dev-Cpp/include/c++/3.4.2" -I"D:/Dev-Cpp/include" -I"D:/pract/home_mdv_root_jr/Downloads/openssl-0.9.8h/openssl-0.9.8h/include"
+BIN = test_client_win.exe
+CXXFLAGS = $(CXXINCS)
+CFLAGS = $(INCS)
+RM = rm -f
+
+.PHONY: all all-before all-after clean clean-custom
+
+all: all-before test_client_win.exe all-after
+
+
+clean: clean-custom
+ ${RM} $(OBJ) $(BIN)
+
+$(BIN): $(OBJ)
+ $(CC) $(LINKOBJ) -o "test_client_win.exe" $(LIBS)
+
+test_client.o: test_client.c
+ $(CC) -c test_client.c -o test_client.o $(CFLAGS)
+
+../CACA_client.o: ../CACA_client.c
+ $(CC) -c ../CACA_client.c -o ../CACA_client.o $(CFLAGS)
+
+../CACA_msgproc.o: ../CACA_msgproc.c
+ $(CC) -c ../CACA_msgproc.c -o ../CACA_msgproc.o $(CFLAGS)
diff --git a/tests/add_newconn b/tests/add_newconn
new file mode 100755
index 0000000..1e25567
--- /dev/null
+++ b/tests/add_newconn
Binary files differ
diff --git a/tests/build.txt b/tests/build.txt
new file mode 100755
index 0000000..3a3b3b1
--- /dev/null
+++ b/tests/build.txt
@@ -0,0 +1 @@
+gcc -o add_newconn test_addnewconn.c ../CACA_node.c -l ssl -l crypto -I /mnt/win_c3/pract/home_mdv_root_jr/Downloads/openssl-0.9.8h/openssl-0.9.8h/include/ -L ../../home_mdv_root_jr/Downloads/openssl-0.9.8h/openssl-0.9.8h/
diff --git a/tests/ca/ca_config.txt b/tests/ca/ca_config.txt
new file mode 100755
index 0000000..bcf4e9b
--- /dev/null
+++ b/tests/ca/ca_config.txt
@@ -0,0 +1,36 @@
+
+
+[ ca ]
+default_ca = CA_default # The default ca section
+
+[ CA_default ]
+
+dir = ./ # top dir
+database = $dir/testnet_cert_index_db.txt # index file.
+new_certs_dir = $dir/../ # new certs dir
+
+#certificate = $dir/testnetCA.pem # The CA cert
+certificate = $dir/picapica_test_CA.pem
+serial = $dir/serial # serial no file
+#private_key = $dir/privkey_testnetCA.pem # CA private key
+private_key = $dir/picapica_test_CA_pkey.pem # CA private key
+RANDFILE = /dev/urandom # random number file
+
+default_days = 3685 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = sha1 # md to use
+
+policy = policy_any # default policy
+email_in_dn = no # Don't add the email into cert DN
+
+name_opt = ca_default # Subject name display option
+cert_opt = ca_default # Certificate display option
+copy_extensions = none # Don't copy extensions from request
+
+[ policy_any ]
+countryName = optional
+stateOrProvinceName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
diff --git a/tests/ca/newtestca.pem b/tests/ca/newtestca.pem
new file mode 100644
index 0000000..9bba2c9
--- /dev/null
+++ b/tests/ca/newtestca.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICiTCCAfKgAwIBAgIJAJACtwPaX6NqMA0GCSqGSIb3DQEBBQUAMDgxHTAbBgNV
+BAoTFFRlc3RpbmcgY2VydGlmaWNhdGVzMRcwFQYDVQQDEw5URVNUIFJPT1QgQ0VS
+VDAeFw0wOTA4MDEyMzI4NThaFw0xMDEyMjYyMzI4NThaMDgxHTAbBgNVBAoTFFRl
+c3RpbmcgY2VydGlmaWNhdGVzMRcwFQYDVQQDEw5URVNUIFJPT1QgQ0VSVDCBnzAN
+BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1YFmjCqUUt6qJkqJgAiZV1p+xdYKB75J
+iLTSwUVD21ebh2dwshYtVhYHLTrNZE3yLvsUrc/EVu4525Xz08XDyJnMtSSroeoY
+MbPMrdJ31JxOr3XOsq0O98WY8ZcZRJ6vZ4LcYZQAgkQXnHnIDFTQocf3VxXp69dq
+XNhY53axjkkCAwEAAaOBmjCBlzAdBgNVHQ4EFgQUz1SvyKy3WjTGqbpKa791Jz20
+GacwaAYDVR0jBGEwX4AUz1SvyKy3WjTGqbpKa791Jz20GaehPKQ6MDgxHTAbBgNV
+BAoTFFRlc3RpbmcgY2VydGlmaWNhdGVzMRcwFQYDVQQDEw5URVNUIFJPT1QgQ0VS
+VIIJAJACtwPaX6NqMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAwjjG
+qoE1mzNumupkMozPkZHeNUp9FL+8iKKyC6RTJ3hA5KXgOayiX9i82BeG+OSox1qs
+t1u6ygx253+m9+TZmaHqeJc1gtHOJKINN3Tu6rWYu6wCjT+f7exu0o9q/1xGB1Nr
+lBPHPKTQAQxbvuoRGNKfjEXlVHXwFd+BobKAqjw=
+-----END CERTIFICATE-----
diff --git a/tests/ca/newtestca_pkey.pem b/tests/ca/newtestca_pkey.pem
new file mode 100644
index 0000000..488164a
--- /dev/null
+++ b/tests/ca/newtestca_pkey.pem
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,7454C45A7135853F
+
+ejRxgOk3mXWPWHMut1kXTVKKsu7sitXJscvU2vAEPPU//Yd1lu8vc6v2n7jcSifT
+WnYifBO+UISmVkfVA9vBjSMP4gR93cs7JzljzwYBpzXiW8RDKVFXBqZjEfsquiRA
+oQuk8ZQJe3Gd8LWTMziSO+vXGRpmNikeUgFDhdb+hXkDk9c4fIA7U6mFe548bWCE
+C/E6Z2jw8adYmLYHT3dcuE3qHoQMDJ6mJVUBhuEY4XiVp2eVEq/ls0N0CBSraBN8
+p7g5php94rrk6wWvKc58rNNoIGutGRNzLuRd5B6c7ojS/IfmsHITBA/6YfcqnNoK
+1DRrkXHE5R2x+v2IEDYO8ir+OWMF73GSFkcwNimkyIcgLeTEoYn95z6g1jNN4cFm
+5H3jbAyjqdNfV8EC3ettdYYW4vtsz1P9oBdDIQh6vmWzJjCKrtkeTRdTolWkJT6O
+rnYVSjwRTGpV98XuDMprew7aHU4FnQdvrKGQ/ZiDSyxkzzW2vvUdpULL1XeOz1/H
+cSPfWhhPDZ5XtOtxbExPOKYQqagaqNwB4VK+S11/mqsoW8tyS7rjwiRFqhmWn/Hf
+AMMuFtf4+zx9W12LcCdFYux6cd342rujhzQ+VEoURZjN/41vOemGWDGMw26y50e3
+mYAyMcZG+wV02ahO2HezH84oG0PPVDtRBDHZox3aMh/FZKQ83vfg1pVr02zQ82c4
+4KdsX+F+0tqFScZcD5ylelcQk5IR0IPLvHSpfiCotcycmKaFBV68SGyM1B0hbo6/
+GU3RhLFC154nAkhr/7ewCCLl/eACuCA6HH00yTK68pLcG281hbHr3A==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/ca/picapica_test_CA.pem b/tests/ca/picapica_test_CA.pem
new file mode 100644
index 0000000..f11826f
--- /dev/null
+++ b/tests/ca/picapica_test_CA.pem
@@ -0,0 +1,54 @@
+-----BEGIN CERTIFICATE-----
+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
+qStgK39Nj5myPko=
+-----END CERTIFICATE-----
diff --git a/tests/ca/picapica_test_CA_pkey.pem b/tests/ca/picapica_test_CA_pkey.pem
new file mode 100644
index 0000000..d908370
--- /dev/null
+++ b/tests/ca/picapica_test_CA_pkey.pem
@@ -0,0 +1,102 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,6B353E4D75711879
+
+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
+-----END RSA PRIVATE KEY-----
diff --git a/tests/ca/privkey_testnetCA.pem b/tests/ca/privkey_testnetCA.pem
new file mode 100755
index 0000000..3c1eefd
--- /dev/null
+++ b/tests/ca/privkey_testnetCA.pem
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,3668506F4CD68874
+
+tRhztkNOy7lYKl3Pa980tEMHzrhi4NmYHMBgqkz89Nvgcb7xxBhoFA3TL01p+r8X
+jJxnuJV6RtfX1HLcvJ627wB1rcelqFmUauBsfRN0cGCPdUv/P85uLRAW4XuL5HPt
+HB9Yks8YC946elhsGSJI9+3SsQRiDFqj6uFyaJ4AGrUio3vQa+hE3cRIxCQ+EXHJ
+S4WyCuWm3zdSkcavzfucrPzKpFtQ/b84dHcp44ON+4TePCSE1ztJjcrG9+CG16Qn
+j6IgJ0O2Xd5aTZIRKyBB1k4mCWnlLxZdVvQ07Zx9sj/M3q1bpopO7+Yb0bMllb3x
+X/O/T65bS+d+XpvSK3JyncLIGqsV92uNUYuTnGwr8hEHfdFa7BsgOivbzPK1trWD
+1u3L+uVHPzFeErcgXPncl4xrm4SqcRLMDEfIttyZT6xOhctEf/O+0limL6Ho7L5g
+zb6HBprS8KyRgVsEOxfWZtvFw/H5ZpCGzyUNoyqRbbemaGYaiMDGW6/tqtgLVAbs
+cr3OfDlIcdn05YujhtUP4GQ+7N+mdZKBSag8PFfajwteuFUMOtx3jWZFM6i5N1eM
+GQUO6QfFcgvI4jEWM+Ub5wdUOOozxO4ci+f0TECJ1zyI3CyDimfoUn6qxUharj+Y
+qMuCW5Qyf4qRJUHB4hl3C7uGIoAIqyNQ1JIwlIl0G3qHOY4llwLJFfSAMxBMk4aP
+5UIg8oU3/XgL7YGwqRMW8qrlIHsx+z8irTE0DLCKZULpcfO1tTUKgjyYGTy6R3vd
+o4e0DxdNLfBLE8+8j3RdgEV9/56RLsTyPY0afBEPBs1wI2E4m5bQ9g==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/ca/serial b/tests/ca/serial
new file mode 100755
index 0000000..8a9ded8
--- /dev/null
+++ b/tests/ca/serial
@@ -0,0 +1 @@
+0340
diff --git a/tests/ca/serial.old b/tests/ca/serial.old
new file mode 100755
index 0000000..e5b67e0
--- /dev/null
+++ b/tests/ca/serial.old
@@ -0,0 +1 @@
+033E
diff --git a/tests/ca/testnetCA.pem b/tests/ca/testnetCA.pem
new file mode 100755
index 0000000..81a4e87
--- /dev/null
+++ b/tests/ca/testnetCA.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICVjCCAb+gAwIBAgIJAIwFS1tZBlNfMA0GCSqGSIb3DQEBBQUAMCcxEDAOBgNV
+BAoTB1RFU1RORVQxEzARBgNVBAMTClRFU1RORVQgQ0EwHhcNMDgxMTI1MTcxMzQ1
+WhcNMDgxMjI1MTcxMzQ1WjAnMRAwDgYDVQQKEwdURVNUTkVUMRMwEQYDVQQDEwpU
+RVNUTkVUIENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2Jn0QVBOoOtoH
+vr8+HZZZqmPZnlG0Rrb1pNUEzScMIm48hDChMEifTWtAr3NHnL9mzkL08BQKnrH5
+VYPkkh5NLFarxanE9sACuI3K6m2UvNK8X0ZPkLt4bQ6A6L7hHD63LSG5fvouGhAQ
+CVgxastn7/qs9RKIRp5GpZTj6um+8wIDAQABo4GJMIGGMB0GA1UdDgQWBBSiFul4
+NNvxHsJ0hDiv7A+FWnSsXTBXBgNVHSMEUDBOgBSiFul4NNvxHsJ0hDiv7A+FWnSs
+XaErpCkwJzEQMA4GA1UEChMHVEVTVE5FVDETMBEGA1UEAxMKVEVTVE5FVCBDQYIJ
+AIwFS1tZBlNfMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAK9AeX2Hv
+DBUWKAF7RI/an2GOOTECLgeDIgzGLjDWFo4CAhrvYSpiyblbAadSQclSWgQalpUH
+OQbAVqB4A8LmXvF/Niixu5QsmG6bCQlrT89khYOQ2GwEnrDpDCB+5t41gZNHmuMX
+r1urVCbUPDo71Hf/TjsPMh/QpY4pAI1BGTc=
+-----END CERTIFICATE-----
diff --git a/tests/ca/testnet_cert_index_db.txt b/tests/ca/testnet_cert_index_db.txt
new file mode 100755
index 0000000..abf6631
--- /dev/null
+++ b/tests/ca/testnet_cert_index_db.txt
@@ -0,0 +1,9 @@
+V 091125175327Z 0337 unknown /C=GE/O=TESTNET/CN=5#tester5
+V 091125180824Z 0338 unknown /O=TESTNET/CN=6#tester6
+V 091223224858Z 0339 unknown /O=TESTNET/CN=8#tester8
+V 091223224935Z 033A unknown /O=TESTNET/CN=9#tester9
+V 101226235112Z 033B unknown /O=TESTLAB/CN=10#tester10
+V 101228044509Z 033C unknown /O=TESTER CERTIFICATE/CN=11#tester11
+V 210407204052Z 033D unknown /ST=Georgia/CN=12#\xD1\x82\xD0\xB5\xD1\x81\xD1\x82\xD0\xB5\xD1\x8012
+V 210407204525Z 033E unknown /C=GE/ST=Georgia/O=\xD1\x82\xD0\xB5\xD1\x81\xD1\x82\xD0\xBE\xD0\xB2\xD0\xB0\xD1\x8F \xD0\xBB\xD0\xB0\xD0\xB1\xD0\xBE\xD1\x80\xD0\xB0\xD1\x82\xD0\xBE\xD1\x80\xD0\xB8\xD1\x8F/CN=13#\xE1\x83\xA2\xE1\x83\x94\xE1\x83\xA1\xE1\x83\xA2\xE1\x83\x94\xE1\x83\xA0\xE1\x83\x98N13
+V 220627211901Z 033F unknown /C=GE/ST=Georgia/O=\xD1\x82\xD0\xB5\xD1\x81\xD1\x82\xD0\xBE\xD0\xB2\xD0\xB0\xD1\x8F \xD0\xBB\xD0\xB0\xD0\xB1\xD0\xBE\xD1\x80\xD0\xB0\xD1\x82\xD0\xBE\xD1\x80\xD0\xB8\xD1\x8F/CN=13#\xE1\x83\xA2\xE1\x83\x94\xE1\x83\xA1\xE1\x83\xA2\xE1\x83\x94\xE1\x83\xA0\xE1\x83\x98N13
diff --git a/tests/ca/testnet_cert_index_db.txt.attr b/tests/ca/testnet_cert_index_db.txt.attr
new file mode 100755
index 0000000..3a7e39e
--- /dev/null
+++ b/tests/ca/testnet_cert_index_db.txt.attr
@@ -0,0 +1 @@
+unique_subject = no
diff --git a/tests/ca/testnet_cert_index_db.txt.attr.old b/tests/ca/testnet_cert_index_db.txt.attr.old
new file mode 100755
index 0000000..3a7e39e
--- /dev/null
+++ b/tests/ca/testnet_cert_index_db.txt.attr.old
@@ -0,0 +1 @@
+unique_subject = no
diff --git a/tests/ca/testnet_cert_index_db.txt.old b/tests/ca/testnet_cert_index_db.txt.old
new file mode 100755
index 0000000..180835d
--- /dev/null
+++ b/tests/ca/testnet_cert_index_db.txt.old
@@ -0,0 +1,7 @@
+V 091125175327Z 0337 unknown /C=GE/O=TESTNET/CN=5#tester5
+V 091125180824Z 0338 unknown /O=TESTNET/CN=6#tester6
+V 091223224858Z 0339 unknown /O=TESTNET/CN=8#tester8
+V 091223224935Z 033A unknown /O=TESTNET/CN=9#tester9
+V 101226235112Z 033B unknown /O=TESTLAB/CN=10#tester10
+V 101228044509Z 033C unknown /O=TESTER CERTIFICATE/CN=11#tester11
+V 210407204052Z 033D unknown /ST=Georgia/CN=12#\xD1\x82\xD0\xB5\xD1\x81\xD1\x82\xD0\xB5\xD1\x8012
diff --git a/tests/cert_pkey.pem b/tests/cert_pkey.pem
new file mode 100755
index 0000000..94dd0d4
--- /dev/null
+++ b/tests/cert_pkey.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIICiTCCAfKgAwIBAgIJAJBNaow2UqG9MA0GCSqGSIb3DQEBBQUAMDgxEDAOBgNV
+BAoTB1RFU1RORVQxCzAJBgNVBAsTAkNBMRcwFQYDVQQDDA4xI9GC0LXRgdGC0LXR
+gDAeFw0wODEwMTkyMzQ2MjhaFw0wODExMTgyMzQ2MjhaMDgxEDAOBgNVBAoTB1RF
+U1RORVQxCzAJBgNVBAsTAkNBMRcwFQYDVQQDDA4xI9GC0LXRgdGC0LXRgDCBnzAN
+BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0R7D4aOP/36NBKLaLSP4bvXdXYCRLXxZ
+6zflIpFQEItxwmom8uwhwf1y3PEgHf8qiXfrfbwQNJ5pZaIXomkLA5syZABkG30D
+yKuGaR+/a64C+1WWd+wmt49GK9ZUALdRNiVNS6H9DpDUain+fq1a1MLoLS91zELu
+6z+brEy3ds0CAwEAAaOBmjCBlzAdBgNVHQ4EFgQUFXiV3280OEg3ydwiNi1KS01L
+DDMwaAYDVR0jBGEwX4AUFXiV3280OEg3ydwiNi1KS01LDDOhPKQ6MDgxEDAOBgNV
+BAoTB1RFU1RORVQxCzAJBgNVBAsTAkNBMRcwFQYDVQQDDA4xI9GC0LXRgdGC0LXR
+gIIJAJBNaow2UqG9MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAdxVj
+2yqNr0qwx5V0kdOwMVDUUqV9zuDT3MiOMePzB1f7cDxxNiFWrR5MubDE0BRWDGzr
+OmCW3Omze9mVmm8/ngtXCXID9K4VmqDr0rHtzRpEFWIFfCn5PKFZQ3ethudAnykN
+fsCu7sIM8qV0bwNuzX0O/FXjhZVcOymeVDl326s=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,1150D7177CE04947
+
+S2mgnilS3+mJ6wi0aGT0blqVlB4o9m3mGO+MiaeVk34H0xZnuQzgM9VUlNjKepRx
+3izhCUoYJJFc0Ku/LgBqjdLnRs84u+TrutnuKLjhwjotlp0YgP7DFGaG4MJp6VC5
+BNAm4+ogAeaKNbsJGKB3gv1Rc3ENNDqLBqDHk1EGVgV3gCO1n1VX5EKwGvpRJl4A
+52HNusEUFLU2c4LVin8hPVh8TuWMnqptZs1+6kTlxUFAany1UpUfhY8A4wMbX7Af
+FoGhoLrmatqJMdoDkVcjpxf32xD6G4JCoC8DbJBA5cHoODeI9Zi1qpEhDu08uQk8
+qlyb2eKc69MGzJmNnTLZrg9bjlxTvERPx9NwYKdH2euZW1+NgC6tHdYIBiq1sNPv
+3ftkOXDFVnWRXSRddM2sKKdXuSrfckGM5BW8BlNHrJrRiJUTdfhrKExlLk4QYf2m
+0WDcEoTHqJQOI9/K6k7gSBq9aO2UuRpUUmcVYn11HIvy1xyVLqkQ7EOOG7uWypQD
+dpYGgTZJc5JmjNUUf92ViKNLi1R9KH2wVS7JqjSOBT8Lc3Ya6ET0vzRim5DcWq6R
+ZbeWB/G+2HKwgmvecJt0KSIQclOqOaKWT0JzhtYScWCaRQ0KoMV33n++G/YYn6qv
+RbJ3LQFCz2P/luVLXTq64whFhxt7W5kIqeS9zBv/DlbNZ0ZIPPOk+pHQRyTfz7HD
+Yd4VS0yP/rPA40VNV9C8DhW5cTjiex41LMV2j/XIy/Gi/nbQLu/TbGEOFLMhqvG2
+XTybvKiB9pNr/kQyl+lDjMgfCmNRU1MWCwMqsS3Gbiltn8dJoDf7Ew==
+-----END RSA PRIVATE KEY-----
+
diff --git a/tests/cert_pkey_3.pem b/tests/cert_pkey_3.pem
new file mode 100755
index 0000000..b5feb4a
--- /dev/null
+++ b/tests/cert_pkey_3.pem
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIICUzCCAbygAwIBAgIJAJDyXELw2qhuMA0GCSqGSIb3DQEBBQUAMCYxEDAOBgNV
+BAoTB1RFU1RORVQxEjAQBgNVBAMMCTMjdGVzdGVyMzAeFw0wODExMjQyMjQ5MDla
+Fw0wODEyMjQyMjQ5MDlaMCYxEDAOBgNVBAoTB1RFU1RORVQxEjAQBgNVBAMMCTMj
+dGVzdGVyMzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA62eJqbk5GQfsp7FC
+SRddaqOepcEp8V5QgJp2wOnLv6W1PpO51Rn0behsTpDXdRoH4Suh7v0KRURhUW4q
+ipX8cNJpC5+u0lS9QFs1hndXWOZu9uJc9MNWN8IVsHEQRCuaK5Ak2xVaW3H6Pcqb
+ui6a8xSX5B90iugchlOIbz7gm3ECAwEAAaOBiDCBhTAdBgNVHQ4EFgQUEV4pVOpG
+egXduClZQvo1/Qp8kxIwVgYDVR0jBE8wTYAUEV4pVOpGegXduClZQvo1/Qp8kxKh
+KqQoMCYxEDAOBgNVBAoTB1RFU1RORVQxEjAQBgNVBAMMCTMjdGVzdGVyM4IJAJDy
+XELw2qhuMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAaYUntGSAdIHT
+kn760cIVExbL4RHyHS52p3BEoaIKoKmQS6yZXRGpg/Nf4EFR0JMMlRYd910U5p52
+7+iEsDIQpN2KccbXCcgLPX0Tc+CBIy8gUHav543bNpGxBYXSx+fQcg6qfwLHPzns
+HBlCox2a+iavbgz5q7QFEltZteUrhZw=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,72D7A6C4E5B1381E
+
+VPdWzksaFY3pRP6iTVv/yGJULx8/2wm+9bTLLVh/a5h4KiGi7TXs9VpoOsbUtsOw
+iDJgau7nvqsMk9k7EUVwuCTWo1O14NAn6Q5tnTq+S3bnmHHxsJz9lBtewFmJp485
+NUoDczLvZdZ5ErD2h8pldRzCZpk4WvYfrpG5H+QrG4QQt+6Ur0/3iFV+tCn9vt3A
+mc6Nh5uX3MzYrV8vco+vSPOBryZK7c8zE6eqlwo1liOtp5+YDY3C/8Ne/uqXNe7I
+M2IcuVjxdW+ZdQLXIWoacHOnOGwl044WcBfXfN0TrcJbr5NpRgUqxSD8sXpdmKuP
+9KwrQwQaNZFa7OhMWkk7n1xgttKiWHmXjC8v99Lgd1GQ79VlE1uan4kchNMfpLJQ
+XlyjJolXICxG7qFSZYRqkLbpWtMY9mUu9qLUCn0DpTMO/eGIclFS51DQj1IusGil
+WG+YIZ5oIDTPG78i6xgIOG5CIyklusoGJlssFeMgLXSCSFLXJACb0XzfdECgD21T
+gKVTskA0qgf32VewBHbsV27Qc4qKE5HuJjEXRekeqn9h+YJgnqzW3o6xTXehqbgG
+f0qZSzE8tF9qiOXjXGxGqsoS9+7XohQ/U0+fVmfcnDJaCiMtaCtm5XATuwkz96ZI
+Lzj3CEgEFPi8oA/WwMFcn+bMWiH+Rla5ps4MZB1fb1ZBrHCjVBvX9QaKDbGQbFaG
+bT2mGElmTd/K5aDEN8iJ9pIrJdon8ifdqFj7wGjBu/2Y8TMdSU0+m0qjK6RqRXpk
+RwB8ORHTwamC63/Gf73BUBYZB+OLC9PcDfoS7uCX9AHXuFqlRs+itA==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/cert_pkey_n2.pem b/tests/cert_pkey_n2.pem
new file mode 100755
index 0000000..6f26745
--- /dev/null
+++ b/tests/cert_pkey_n2.pem
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIICUzCCAbygAwIBAgIJAIdo5URyPPpNMA0GCSqGSIb3DQEBBQUAMCYxEDAOBgNV
+BAoTB1RFU1RORVQxEjAQBgNVBAMMCTIjdGVzdGVyMjAeFw0wODExMTgxMjM5MjRa
+Fw0wODEyMTgxMjM5MjRaMCYxEDAOBgNVBAoTB1RFU1RORVQxEjAQBgNVBAMMCTIj
+dGVzdGVyMjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAkauUtDyMh+zf9VV1
+K1wjLpz3+5nHzvVckJuFmB0X7leUUJ3Hw45qMFkf270EKMoXuJHsy09gWIHWwciE
+uit/hXeD4k+MjgxL/CqvotCVIORlCwu9d+07d2DmawySjHaHgEgz/mm4Bym5oo6Y
+tyB8is6D1fGJY2ysg2d3L2S5QMsCAwEAAaOBiDCBhTAdBgNVHQ4EFgQUEFoG9GRa
+F6tGN8LrUmmtwiWoF3MwVgYDVR0jBE8wTYAUEFoG9GRaF6tGN8LrUmmtwiWoF3Oh
+KqQoMCYxEDAOBgNVBAoTB1RFU1RORVQxEjAQBgNVBAMMCTIjdGVzdGVyMoIJAIdo
+5URyPPpNMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAOjHi8b+QQWyn
+jR44HcCpYp8U8F5Pbxu8IUgBoGOqZxGYUS894WXUtLH48TbP74toB/edLRrXBdCq
+6D+yqCkZgx8ObcibV1j4xmvBMPsrBbzWa1g9nueVqM6/oVfm5gA0jdMdsvVUuz8G
+1s+AwyTQw63Kf8vToWu+U5A2a01sfXM=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,B68E22013A5B5F7A
+
+DO7bFosxptJyxfn8B1DYICoPgvPTfQmL5/n20JQEel1llHY/pS24rQIx/1rEq2DH
+r+ekGxAjzV3psCqhZ1SSr3ch36sXBEHPD7pr0LaamW2VxQKwLsVuDCZO4fBkfLuj
+14NUxVg5oKm2q0lY38psEQUqZDoQ6ji1hGR1BGh+s4EferFmDJuPUgo0KOfjHOzT
+xVY+m5LkuPvP3Zc7LNrOYootc6L0Z5+q1/ObX78eoWKEIfS5uDEO72qGBnnszNgz
+AidGcG0v8skaZogogjEkrdCKtbeFzUNcN9+5PhBHWNba5Xvru4GU9h2XCHFKxIx/
+8qImd0zbUhn/8QgyhyaQWpYvpzMrxIazfxAOboHBqumLDXkUhI7f2dt2YO6iYcSH
+AsHnobDK68bcLzHsZrMyvzlT2LBNN+ZGwlspYLTtj/7AhocSOmj016WDqEE6WmNy
+kRXp7yTl7dMi++U713QldnRQs7WdKe4Jz/kG4uKO+eSvbwHTcfja9RkFRqrr7XEs
+V1IzzVWhl2wkSRmQ6TKHZVR9mmZaqIliEb8QG1u8M0KJjgpqJlNbmsLpWYISoY3n
+sZNQOfyTODifNf0l4g97GXbfk4uZZLhhzzVRCqXWgHZEKoStbh0d+sJQKcXg6IdZ
+uO2RJc8QjeE5Kn8dPAY048dSP5uNRdFme8oZ1KePz/wiHljqm0oEj71Bt0bdW/es
+LJSKjtyXc0NpRdf5V9q+sY48VK292Whfoql0cyEr6RyAy/numL9xbjNW/lHp0VQM
+2ZY+6BlOpp9Um5hs0ORz5xH3EAaGhVhLWRmcqCWPjIqduj7Ls85tVQ==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/create_certs_commands.txt b/tests/create_certs_commands.txt
new file mode 100644
index 0000000..97fbbcd
--- /dev/null
+++ b/tests/create_certs_commands.txt
@@ -0,0 +1,6 @@
+
+openssl req -out picapica_test_CA.pem -newkey rsa:8192 -keyout picapica_test_CA_pkey.pem -x509 -days 3680 -utf8
+
+openssl req -out tester13req.pem -newkey rsa:4096 -keyout tester13pkey.pem -new -days 3680 -utf8
+
+openssl ca -config ca_config.txt -in tester13req.pem -out tester13cert.pem -utf8
diff --git a/tests/dh2048.pem b/tests/dh2048.pem
new file mode 100644
index 0000000..ea0fa4c
--- /dev/null
+++ b/tests/dh2048.pem
@@ -0,0 +1,8 @@
+-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEA4SNgEbmfDYLh6IHkDIBJUHSUu9NkvpAnT6s5yJOKvKYzulZH/ENv
+EUckfrOZA+aWapy/Jz1ogJMXDwWb8Vz/TzeClZQ8wGFsATwkl/YLcBjn/TL0SxKP
+fU28+LVczvtzv5zctcWrrZzzrkxS3v3Ap5GBEJ5Us18ZLhs1RM1iLSK1DdgAu7Z3
+3u4f/YtRq9FyQqOgg5skFsYuqYXW0PbEzBWX4AkR/uEgWTZGH+xJIarZvb+cmPSv
+DtygutDStxEGAiifEaXjgvMUI+ayZ5qn/azIBO9/2Zwekng36ZsC+CgW5kZJzCrX
+4SEijqbODgzAMK2Z1SGt+QRllzhjj/oC4wIBAg==
+-----END DH PARAMETERS-----
diff --git a/tests/dh512.pem b/tests/dh512.pem
new file mode 100644
index 0000000..c072c78
--- /dev/null
+++ b/tests/dh512.pem
@@ -0,0 +1,4 @@
+-----BEGIN DH PARAMETERS-----
+MEYCQQCTMDVkfkH8eEae0Y87LWzpuOjuAh29cC215AI+nS1Ymlswn0Mo1IjbhzIN
+Bh0VxjknSpZaNjdFFgago0tDADuLAgEC
+-----END DH PARAMETERS-----
diff --git a/tests/fdssize b/tests/fdssize
new file mode 100755
index 0000000..96be1de
--- /dev/null
+++ b/tests/fdssize
Binary files differ
diff --git a/tests/fdssize.c b/tests/fdssize.c
new file mode 100755
index 0000000..56562e7
--- /dev/null
+++ b/tests/fdssize.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+#include <sys/select.h>
+
+
+int main()
+{
+ printf("FD_SETSIZE=%u\n",FD_SETSIZE);
+ return 0;
+}
diff --git a/tests/getid b/tests/getid
new file mode 100755
index 0000000..93e127c
--- /dev/null
+++ b/tests/getid
Binary files differ
diff --git a/tests/nodeaddrlist_test.txt b/tests/nodeaddrlist_test.txt
new file mode 100644
index 0000000..65cf026
--- /dev/null
+++ b/tests/nodeaddrlist_test.txt
@@ -0,0 +1,5 @@
+::1 5859 0
+aaa.mydns.com 5000 1
+localhost 5859 0
+veryverylongdomainname.aaaaaaaaaaaaaaaaaaaaaaaaaaa.com 65000 0
+
diff --git a/tests/privkey_tester4.pem b/tests/privkey_tester4.pem
new file mode 100755
index 0000000..4afa942
--- /dev/null
+++ b/tests/privkey_tester4.pem
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,EF3C10295057B32F
+
+NzBcTqDlCq575j/EOEZSd/lEcni2Z8FU5LTNyiRZpTPofKwXXmKUBdZ0j8VQPgdc
+eY6ZnubMqfGAFXYKU9yyB8TmSOI0rCFlRcaCzef8l5Qm2Kk3XDvWxYeZBNdZAF7/
+dYygU+5h92+jtEWLeVA4AbkW9FhZsaUhjH6G4Z4xw1Vpdx1SkLcPIE676D654x46
+HynqQWFi7HDP6/Oyw+3NkLQDihzyYtkSz8G9VEgLZwwHB3SpBgHJBzbzljzlhBUP
+7s5vftTZvGMDEqgota2VrIcVKO7Vo8c6HbiSOGMuyHZyz2xQ10ryB2pX9tZEEapU
+ukos+fLMeE3Wv32dXAEiX9H8G1ctiMO/4oMP+/Oy2kgGQUwxlB3Z21PO5Vh4ZU+j
+F/eXTYnjSBHLfQu7eZSkx9Xp76L1WwhDjvhsW4Vl766fwaWzeOMT1T5/vSRcMJ6U
+Cdtf9cWJtr0LuJmpI1ZS4QcPC3hzB6XtIIAFx0naZS3TEjeBNw988vbXOp/cKp4k
+U142CC9eVNSci/rW/NZRiKm1ylDq0Sq6SIy8gcPdAnGfipTLUKaWOVG8eAiU5yqg
+NZ1UALAvSwpA6936s94lDwbiZH985xEmHHwR3P16SXUYYcpa7jJLcK6XKNuIbbuI
+L3SX3IIrj+XLFUqRyG/z8PK0vMOllk/1FguHLKtA8r4JRUahxFIHx5xllqYQW4EH
+Df71iYd0QmN7uzgXesnixh2DmC+TogMWRsYIfjuuuWQnfQoHFxSCyGnq1Eb8pipS
+tHV5SHc7vaWSrRXltJx6EPljvOsadKiEtelHqqbgX6E=
+-----END RSA PRIVATE KEY-----
diff --git a/tests/save_nodelist.txt b/tests/save_nodelist.txt
new file mode 100644
index 0000000..604604d
--- /dev/null
+++ b/tests/save_nodelist.txt
@@ -0,0 +1,4 @@
+::1 5859 0
+aaa.mydns.com 5000 1
+localhost 5859 0
+veryverylongdomainname.aaaaaaaaaaaaaaaaaaaaaaaaaaa.com 65000 0
diff --git a/tests/test_addnewconn.c b/tests/test_addnewconn.c
new file mode 100755
index 0000000..a8acbfd
--- /dev/null
+++ b/tests/test_addnewconn.c
@@ -0,0 +1,42 @@
+#include <stdio.h>
+ #include "../PICA_node.h"
+
+struct newconn* add_newconn(struct newconn *ncs,int *pos);
+
+struct newconn t1[64];
+
+
+int main()
+{
+int tpos=0,i;
+struct newconn *nc;
+
+printf(" t1=%X\n",t1);
+
+nc=add_newconn(t1,&tpos);
+printf("1) nc=%X tpos=%i\n",nc,tpos);
+
+tpos=0;
+t1[0].sck=1;
+
+nc=add_newconn(t1,&tpos);
+printf("2) nc=%X tpos=%i\n",nc,tpos);
+
+tpos=64;
+nc=add_newconn(t1,&tpos);
+printf("3) nc=%X tpos=%i\n",nc,tpos);
+
+for (i=0;i<64;i++)
+{
+t1[i].sck=65536;
+t1[i].tmst=100+i;
+}
+
+t1[63].tmst=5;
+
+nc=add_newconn(t1,&tpos);
+printf("4) nc=%X tpos=%i\n",nc,tpos);
+
+
+return 0;
+}
diff --git a/tests/test_addrconv.c b/tests/test_addrconv.c
new file mode 100644
index 0000000..a5e4a15
--- /dev/null
+++ b/tests/test_addrconv.c
@@ -0,0 +1,23 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+
+#include <stdio.h>
+
+int main()
+{
+int ret;
+char buf[256];
+
+
+gets(buf);
+
+
+ret=inet_aton(buf,(struct in_addr*)(buf+128));
+
+printf("inet_aton()=%i\n",ret);
+
+return 0;
+} \ No newline at end of file
diff --git a/tests/test_client b/tests/test_client
new file mode 100755
index 0000000..e3723b6
--- /dev/null
+++ b/tests/test_client
Binary files differ
diff --git a/tests/test_client.c b/tests/test_client.c
new file mode 100755
index 0000000..89e0c8c
--- /dev/null
+++ b/tests/test_client.c
@@ -0,0 +1,234 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../PICA_client.h"
+
+
+struct sockaddr_in a;
+struct PICA_conninfo *c;
+struct PICA_chaninfo *chn;
+
+int _outgoing_chnl=0;
+
+unsigned char buf[64*1024];
+
+int accept_cb(unsigned int id)
+{
+printf("accept_cb: %u\n",id);
+return 1;
+}
+
+//получение сообщения.
+void newmsg_cb(unsigned int peer_id,char* msgbuf,unsigned int nb,int type)
+{
+memcpy(buf,msgbuf,nb);
+buf[nb]=0;
+printf(" %u: ",peer_id);
+puts(buf);
+}
+//получение подтверждения о доставке сообщения
+void msgok_cb(unsigned int peer_id)
+{
+puts("[V]");
+}
+//создание канала с собеседником
+void channel_established_cb(unsigned int peer_id)
+{
+puts("channel_established_cb");
+}
+
+void channel_failed_cb(unsigned int peer_id)
+{
+printf("Failed to create channel to %u\n",peer_id);
+chn=0;
+}
+
+//входящий запрос на создание канала от пользователя с номером caller_id
+//<<int accept_cb(unsigned int caller_id);
+//запрошенный пользователь не найден, в оффлайне или отказался от общения
+void notfound_cb(unsigned int callee_id)
+{
+puts("notfound_cb");
+}
+
+void channel_closed_cb(unsigned int peer_id, int reason)
+{
+printf("channel_closed_cb( peer_id = %u, reason = %i)\n", peer_id, reason);
+}
+
+void nodelist_cb(int type, void *addr_bin, const char *addr_str, unsigned int port)
+{
+switch(type)
+ {
+ case 0xA0:
+ printf("node (IP %s port %u)\n", addr_str, port);
+ break;
+
+ case 0xA1:
+ printf("node (IPv6 %s port %u)", addr_str, port);
+ break;
+
+ case 0xA2:
+ printf("node (hostname %s port %u)\n", addr_str, port);
+ break;
+
+ default:
+ puts("unknown node address type");
+ return;
+ }
+}
+
+int peer_cert_verify_cb(unsigned int peer_id, const char *cert_pem, unsigned int nb)
+{
+ printf("peer's certificate (%u): \n%.*s\n", peer_id, nb,cert_pem);
+ return 1;
+}
+
+struct PICA_client_callbacks cbs = {
+ newmsg_cb,
+ msgok_cb,
+ channel_established_cb,
+ channel_failed_cb,
+ accept_cb,
+ notfound_cb,
+ channel_closed_cb,
+ nodelist_cb,
+ peer_cert_verify_cb
+};
+
+int main(int argc,char** argv)
+{
+int ret;
+unsigned int peer_id;
+
+if (argc<3)
+ {
+ puts("usage: test_client address cert_filename [peer id]");
+ return 0;
+ }
+
+puts(SSLeay_version(SSLEAY_VERSION));
+printf("Calling PICA_init...\n");
+
+PICA_client_init(&cbs);
+
+// printf("setting address...\n");
+//
+// memset(&a,0,sizeof(a));
+//
+// a.sin_family = AF_INET;
+// a.sin_addr.s_addr = inet_addr (argv[1]);//("127.0.0.1");
+// a.sin_port = htons(51914);
+
+
+printf("making connection...\n");
+
+//PICA_new_connection(const char *nodeaddr, unsigned int port, const char *CA_file, const char *cert_file, const char *pkey_file, const char* password, struct PICA_conninfo **ci)
+ret=PICA_new_connection(argv[1], 51914,"trusted_CA.pem",argv[2],argv[2],NULL,&c);
+
+ERR_print_errors_fp(stdout);
+
+if (ret==PICA_OK)
+printf("PICA_OK Connected to server...\n");
+else
+{
+printf("PICA_new_connection: ret=%i\n",ret);
+return 1;
+}
+
+if (argc==4)
+ {
+ peer_id=atoi(argv[3]);
+ printf("Creating channel to %u...\n",peer_id);
+
+ ret=PICA_create_channel(c,peer_id,&chn);
+ //sleep(17);//timeout test
+ if (ret!=PICA_OK)
+ {
+ printf("error: ret=%i\n",ret);
+ return 1;
+ }
+
+ }
+
+do
+ {
+ if (chn && chn->state==PICA_CHANSTATE_ACTIVE)
+ {
+ unsigned int msglen;
+ gets(buf);
+ msglen=strlen(buf);
+
+ ret=PICA_send_msg(chn,buf,msglen);
+
+ if (ret!=PICA_OK)
+ printf("PICA_send_msg ret=%i\n",ret);
+ }
+ }
+while(PICA_read(c,100000) == PICA_OK && PICA_write(c) == PICA_OK);
+
+/*
+puts("Enter peer_id to talk with or . for waiting incoming sessions:");
+gets(buf);
+if (buf[0]=='.')
+ {
+ unsigned int br;
+ ret=PICA_read_conn(c,accept_cb,&chn);
+
+ if (ret==PICA_NEWCHAN)
+ {
+ puts("PICA_NEWCHAN!");
+ }
+ else
+ printf("read_conn error: ret=%i\n",ret);
+
+ ret=PICA_read_msg(chn,buf,&br);
+ switch(ret)
+ {
+ case PICA_MSGUTF8:
+ printf("%u (%u bytes): ",c->id,br);
+ puts(buf);
+ break;
+ case PICA_MSGOK:
+ puts("[v]");
+ break;
+ default:
+ printf("PICA_read_msg ret=%i\n",ret);
+ }
+ }
+else
+ {
+ peer_id=atoi(buf);
+ printf("Creating channel to %u...\n",peer_id);
+
+ ret=PICA_create_channel(c,peer_id,&chn,1);
+
+ if (ret==PICA_OK)
+ {
+ unsigned int msglen;
+ puts("Channel established...");
+ gets(buf);
+ msglen=strlen(buf);
+
+ ret=PICA_send_msg(chn,buf,msglen);
+ if (ret!=PICA_OK)
+ printf("PICA_send_msg ret=%i\n",ret);
+ }
+ else
+ printf("error: ret=%i\n",ret);
+ }
+
+*/
+sleep(10);
+
+if (chn)
+ {
+ puts("--------");
+ PICA_close_channel(chn);
+ }
+
+PICA_close_connection(c);
+
+return 0;
+}
diff --git a/tests/test_client_win.dev b/tests/test_client_win.dev
new file mode 100755
index 0000000..fa7db72
--- /dev/null
+++ b/tests/test_client_win.dev
@@ -0,0 +1,79 @@
+[Project]
+FileName=test_client_win.dev
+Name=test_client_win
+UnitCount=3
+Type=1
+Ver=1
+ObjFiles=
+Includes=D:\pract\home_mdv_root_jr\Downloads\openssl-0.9.8h\openssl-0.9.8h\include
+Libs=
+PrivateResource=
+ResourceIncludes=
+MakeIncludes=
+Compiler=
+CppCompiler=
+Linker=../../home_mdv_root_jr/Downloads/openssl-0.9.8h/openssl-0.9.8h/lib_a_win/libssl.a_@@_../../../Dev-Cpp/lib/libgdi32.a_@@_../../../Dev-Cpp/lib/libws2_32.a_@@_../../home_mdv_root_jr/Downloads/openssl-0.9.8h/openssl-0.9.8h/lib_a_win/libcrypto.a_@@_../../../Dev-Cpp/lib/libgdi32.a_@@_../../../Dev-Cpp/lib/libws2_32.a_@@_
+IsCpp=0
+Icon=
+ExeOutput=..\tests
+ObjectOutput=
+OverrideOutput=1
+OverrideOutputName=test_client_win.exe
+HostApplication=
+Folders=
+CommandLine=
+UseCustomMakefile=0
+CustomMakefile=
+IncludeVersionInfo=0
+SupportXPThemes=0
+CompilerSet=0
+CompilerSettings=0000000000000000000000
+
+[Unit1]
+FileName=test_client.c
+CompileCpp=0
+Folder=test_client_win
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c test_client.c ../CACA_client.c -o test_client.o $(CFLAGS)
+
+[VersionInfo]
+Major=0
+Minor=1
+Release=1
+Build=1
+LanguageID=1033
+CharsetID=1252
+CompanyName=
+FileVersion=
+FileDescription=Developed using the Dev-C++ IDE
+InternalName=
+LegalCopyright=
+LegalTrademarks=
+OriginalFilename=
+ProductName=
+ProductVersion=
+AutoIncBuildNr=0
+
+[Unit2]
+FileName=..\CACA_client.c
+CompileCpp=0
+Folder=test_client_win
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit3]
+FileName=..\CACA_msgproc.c
+CompileCpp=0
+Folder=test_client_win
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
diff --git a/tests/test_client_win.exe b/tests/test_client_win.exe
new file mode 100755
index 0000000..d37b15c
--- /dev/null
+++ b/tests/test_client_win.exe
Binary files differ
diff --git a/tests/test_client_win.layout b/tests/test_client_win.layout
new file mode 100755
index 0000000..0f3e415
--- /dev/null
+++ b/tests/test_client_win.layout
@@ -0,0 +1,20 @@
+[Editor_0]
+CursorCol=1
+CursorRow=5
+TopLine=11
+LeftChar=1
+Open=1
+Top=0
+[Editors]
+Order=0,-1
+Focused=-1
+[Editor_1]
+Open=1
+Top=1
+CursorCol=27
+CursorRow=1114
+TopLine=1111
+LeftChar=1
+[Editor_2]
+Open=0
+Top=0
diff --git a/tests/test_getidfromsubstr.c b/tests/test_getidfromsubstr.c
new file mode 100755
index 0000000..2e33a67
--- /dev/null
+++ b/tests/test_getidfromsubstr.c
@@ -0,0 +1,43 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+
+int get_id_fromsubjstr(char* DN_str,unsigned int* id)
+{
+ char* tmp1;
+ char* tmp2;
+
+ tmp1=strstr(DN_str,"/CN=");
+
+ if (!tmp1)
+ return 0;
+
+ tmp2=strchr(tmp1,'#');
+
+ if (!tmp2)
+ return 0;
+
+ *tmp2=0;
+
+ tmp1+=4;
+
+ if (tmp1==tmp2)
+ return 0;
+
+ *id=(unsigned int)strtol(tmp1,0,10);
+
+ *tmp2='#';
+ return 1;
+}
+
+int main()
+{
+ int ret;unsigned int id;
+ char dnstr[]="/O=TESTNET/OU=CA/CN=#\xD1\x82\xD0\xB5\xD1\x81\xD1\x82\xD0\xB5\xD1\x80";
+
+ ret=get_id_fromsubjstr(dnstr,&id);
+ printf("dnstr=%s, ret=%i, id=%u \n",dnstr,ret,id);
+
+return 0;
+}
diff --git a/tests/test_msgproc b/tests/test_msgproc
new file mode 100755
index 0000000..01c6614
--- /dev/null
+++ b/tests/test_msgproc
Binary files differ
diff --git a/tests/test_msgproc.c b/tests/test_msgproc.c
new file mode 100755
index 0000000..497d862
--- /dev/null
+++ b/tests/test_msgproc.c
@@ -0,0 +1,18 @@
+#include <stdio.h>
+
+#include "../PICA_msgproc.h"
+
+ unsigned int _PICA_msginfo_arr_size=1;
+
+ struct PICA_msginfo _PICA_msginfo_arr[1]={0xCF,PICA_MSG_VAR_SIZE,3};
+
+char tstpck[12]={0xCF,0xCF,0x03,0x01,1,0,0,0,0,0,0,0};
+
+int main()
+{
+printf("nb=1 ret=%u\n",PICA_msggetsize(tstpck,1));
+printf("nb=4 ret=%u\n",PICA_msggetsize(tstpck,4));
+printf("nb=12 ret=%X\n",PICA_msggetsize(tstpck,12));
+
+return 0;
+} \ No newline at end of file
diff --git a/tests/test_nodeaddr b/tests/test_nodeaddr
new file mode 100755
index 0000000..65cdcd2
--- /dev/null
+++ b/tests/test_nodeaddr
Binary files differ
diff --git a/tests/test_nodeaddr.c b/tests/test_nodeaddr.c
new file mode 100644
index 0000000..882dbf5
--- /dev/null
+++ b/tests/test_nodeaddr.c
@@ -0,0 +1,28 @@
+#include "../PICA_nodeaddrlist.h"
+#include <stdio.h>
+
+
+struct PICA_nodeaddr *head=0,*end=0;
+
+int main(int argc,char* argv[])
+{
+unsigned int N,ret;
+if (argc==1)
+ {
+ puts("test_nodeaddrlist load_list_filename [save_list_filename]");
+ }
+else
+ {
+ puts(argv[1]);
+ N=PICA_nodeaddrlist_load(argv[1],&head,&end);
+ printf("N=%u\n",N);
+
+ if (argc==3)
+ {
+ ret=PICA_nodeaddrlist_save(argv[2],head);
+ printf("save=%i to %s \n",ret,argv[2]);
+ }
+ }
+
+return 0;
+}
diff --git a/tests/test_randseed.c b/tests/test_randseed.c
new file mode 100755
index 0000000..a6318d6
--- /dev/null
+++ b/tests/test_randseed.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define DEBUG_RAND
+
+#include "..\PICA_rand_seed.h"
+
+int main(int argc, char *argv[])
+{
+ PICA_rand_seed();
+
+ system("PAUSE");
+ return 0;
+}
diff --git a/tests/test_randseed.dev b/tests/test_randseed.dev
new file mode 100755
index 0000000..11d4c85
--- /dev/null
+++ b/tests/test_randseed.dev
@@ -0,0 +1,69 @@
+[Project]
+FileName=test_randseed.dev
+Name=test_randseed
+UnitCount=2
+Type=1
+Ver=1
+ObjFiles=
+Includes=
+Libs=
+PrivateResource=
+ResourceIncludes=
+MakeIncludes=
+Compiler=
+CppCompiler=
+Linker=../../home_mdv_root_jr/Downloads/openssl-0.9.8h/openssl-0.9.8h/libcrypto.a_@@_../../../Dev-Cpp/lib/libgdi32.a_@@_
+IsCpp=0
+Icon=
+ExeOutput=
+ObjectOutput=
+OverrideOutput=0
+OverrideOutputName=test_randseed.exe
+HostApplication=
+Folders=
+CommandLine=
+UseCustomMakefile=0
+CustomMakefile=
+IncludeVersionInfo=0
+SupportXPThemes=0
+CompilerSet=0
+CompilerSettings=0000000000000000000000
+
+[Unit1]
+FileName=test_randseed.c
+CompileCpp=0
+Folder=
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[VersionInfo]
+Major=0
+Minor=1
+Release=1
+Build=1
+LanguageID=1033
+CharsetID=1252
+CompanyName=
+FileVersion=
+FileDescription=Developed using the Dev-C++ IDE
+InternalName=
+LegalCopyright=
+LegalTrademarks=
+OriginalFilename=
+ProductName=
+ProductVersion=
+AutoIncBuildNr=0
+
+[Unit2]
+FileName=..\CACA_rand_seed.c
+CompileCpp=0
+Folder=test_randseed
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
diff --git a/tests/test_randseed.exe b/tests/test_randseed.exe
new file mode 100755
index 0000000..f081ca8
--- /dev/null
+++ b/tests/test_randseed.exe
Binary files differ
diff --git a/tests/tester10_pkey.pem b/tests/tester10_pkey.pem
new file mode 100644
index 0000000..0cc0871
--- /dev/null
+++ b/tests/tester10_pkey.pem
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,32AB002331BE4176
+
+MUrtioO6Xuctc37EiL1kA/Y8Ge5wB+WNZP0zohAi8najzbgjeaCM3QBZFQp/TCOp
+FrMd2FSjdGBdSzWjv9UiAUb/XAcGHPuwONGXPAokkt3d/T/szFsPhYfHz5B2qCmM
+kzrjIEwVm8UVxpbSJypRCQN/YGS94GZJ9DrtRNfH4Uks5sGu9PdDYWlPuMK1HGdy
+QgCSDAquSzg6feDgIfhy2oBt5m539nx1yYUzwnsfm7sixTUoohb+fMpFw5AerFEs
+z3BOs+DlIWzqT1kHfuENYGANMsBbkD+IIW/qBxifBk6wdY8YLUrhbfVuFeAlUxU2
+Uhxrs5mqyXv7FqJbjL734NbkzetCAyyNu1G9d/lHibm2OTMMmLBECpcEMOB6B6vX
+9ejceszRmEsAk9cZrKEnTJH+Z5c003Z7+AknB8j5Dre5pmCIqIhdE9R2JT0PN1Kv
+YoYkHKW6lT4EhHblr0STMG8oAJQ4U/CH489GMJ25Y2FGx4kD9U8EQerJxoFdDeti
+Ft8kpY78SRf4boVJ3P2mWU4dMGJotAvlDPbfIwBcd4l2r/M3uOTvUwEC9iJ0WpdC
+2twSfXZ7AuO2ZsKs+cYflKPaxBTbCjscKV4h+TxpNN2xCxLa0eEtjYoxxUbjBAv0
++z04Zc1JxEbA4tjmjudiPCFGVuc9bVBKsSlRqAyWNMipIksWr4Ke1XPo2xQh2Lny
+Do1+HuipzcYCqt8JX1RMdVEAdIxkBpmspzbHfbEe7fs1NnPPAJ6YUCzey412O/IC
+nxWG4DGPk6+73ND35wogClgU6EhuBGfULBAa0dhvQCEVejGgyr9VNQ==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tester10cert.pem b/tests/tester10cert.pem
new file mode 100644
index 0000000..4ef0829
--- /dev/null
+++ b/tests/tester10cert.pem
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIB0DCCATkCAgM7MA0GCSqGSIb3DQEBBQUAMDgxHTAbBgNVBAoTFFRlc3Rpbmcg
+Y2VydGlmaWNhdGVzMRcwFQYDVQQDEw5URVNUIFJPT1QgQ0VSVDAeFw0wOTA4MDEy
+MzUxMTJaFw0xMDEyMjYyMzUxMTJaMCgxEDAOBgNVBAoTB1RFU1RMQUIxFDASBgNV
+BAMMCzEwI3Rlc3RlcjEwMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD60z9L
+EbLs5qm+aCKc7uOtWLG/lTN7Dsw6MFGtvqKvx4kdcEuY9OXb2DQJMIUOfWsOuyFz
+ONzLQZIqX7afU4CgD6fdhhcC6lr+iN/JPnIg4z09m2CZj786NCZdB+gCPDLqfUjO
+7hpDAWTMVYFrEexhGuYHvga2Sn51dgXx02AA/wIDAQABMA0GCSqGSIb3DQEBBQUA
+A4GBAAAeJ0y/y5X0iSyz0lJrBC+HkQeydacAfYI36Kf4aofLJ3YRBQ/7JfcDbmBE
+bZIJll/0Fc4mN4zMNpJJGslfex6wPvW8QS4KsG7hW3PGJvfzixcRcHHVXfQyeirt
+Qzi/WnNC1vhEeCR51erPzWRU1N9+4wWc3tmMcTpiJzDNdDwV
+-----END CERTIFICATE-----
+
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,32AB002331BE4176
+
+MUrtioO6Xuctc37EiL1kA/Y8Ge5wB+WNZP0zohAi8najzbgjeaCM3QBZFQp/TCOp
+FrMd2FSjdGBdSzWjv9UiAUb/XAcGHPuwONGXPAokkt3d/T/szFsPhYfHz5B2qCmM
+kzrjIEwVm8UVxpbSJypRCQN/YGS94GZJ9DrtRNfH4Uks5sGu9PdDYWlPuMK1HGdy
+QgCSDAquSzg6feDgIfhy2oBt5m539nx1yYUzwnsfm7sixTUoohb+fMpFw5AerFEs
+z3BOs+DlIWzqT1kHfuENYGANMsBbkD+IIW/qBxifBk6wdY8YLUrhbfVuFeAlUxU2
+Uhxrs5mqyXv7FqJbjL734NbkzetCAyyNu1G9d/lHibm2OTMMmLBECpcEMOB6B6vX
+9ejceszRmEsAk9cZrKEnTJH+Z5c003Z7+AknB8j5Dre5pmCIqIhdE9R2JT0PN1Kv
+YoYkHKW6lT4EhHblr0STMG8oAJQ4U/CH489GMJ25Y2FGx4kD9U8EQerJxoFdDeti
+Ft8kpY78SRf4boVJ3P2mWU4dMGJotAvlDPbfIwBcd4l2r/M3uOTvUwEC9iJ0WpdC
+2twSfXZ7AuO2ZsKs+cYflKPaxBTbCjscKV4h+TxpNN2xCxLa0eEtjYoxxUbjBAv0
++z04Zc1JxEbA4tjmjudiPCFGVuc9bVBKsSlRqAyWNMipIksWr4Ke1XPo2xQh2Lny
+Do1+HuipzcYCqt8JX1RMdVEAdIxkBpmspzbHfbEe7fs1NnPPAJ6YUCzey412O/IC
+nxWG4DGPk6+73ND35wogClgU6EhuBGfULBAa0dhvQCEVejGgyr9VNQ==
+-----END RSA PRIVATE KEY-----
+
diff --git a/tests/tester11_pkey.pem b/tests/tester11_pkey.pem
new file mode 100644
index 0000000..6cd6fbe
--- /dev/null
+++ b/tests/tester11_pkey.pem
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,57E31D0A675E913A
+
+S4v09hyzG23Ouv8+3dqtt0k3mi7xn4YslHD27gDSuwvty11i38ui+dv7hNe5Y1mt
+1gb3PIFwBqiGYToNamdwI2ITNpvR7dVyC++J1IUAa1D8IlYhNY/EQI3k/P5MZbQl
+WViGGLIn7EWTVwavmdYInOj+jGf8J44Hu1SOdeftQGq95nbdDCxSbemo7bigO5r/
+aCoCLV6iasbi/JiJNuB/Kq96R1KeZVbka1rKuZIXL5+nqPP/UesCW8/+r84Xdx/P
+7GDN7x0XgwQwxu7/AJtLF1ID08kkoyL6PiijIN2zRk4vP5iMWjpFdlVgjpKngoo1
++nMzEGpViJcyaVtiVn5XeJPPEGevupuk+UZQmidcerCEF6S6SM29Xhw4VOuEce8R
+XnXZdwPBaVps25/Pf0rzZ5NkSQf9fXY0BiT8dMBtD1c6IgvpOlGgVIyKgAEUFyMj
+rUH3fYTkZ6SfFoSXZSmvsXGzHcYLQhcsrSzdEKZ+V3WMPSlKXuYPbvDZqwp32KhJ
+ajxPJuAr47p7Qp/Nrn0pESK8KN+fXeA8blDqu9YKlJSZ+81WreFh5RtRBTRjyAq7
+zP7pEwZAQQTch7jY3IklAzBffPj+WOV1Xo+VU4dSVdbP5vukwKxEMnIaECZHw9N6
+sE2fD5FFOCltPgwVXO6d79nyKYxmS8wxmH7aGwpzJ6DYS2y0TWwkp96PS+3zlBZb
+vW06RxxTolEBDkC75JAo2l+kjd8MjQfDkW2FbtB5TN9o6M/mmRJ5nRlUdlU8/SJL
+05zhsHTPb+Cpk6ajefNAb/8r2p6MLr5QnRHe7CoIXFOWHt1F7qYjBg==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tester11cert.pem b/tests/tester11cert.pem
new file mode 100644
index 0000000..6f58dd4
--- /dev/null
+++ b/tests/tester11cert.pem
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIB2zCCAUQCAgM8MA0GCSqGSIb3DQEBBQUAMDgxHTAbBgNVBAoTFFRlc3Rpbmcg
+Y2VydGlmaWNhdGVzMRcwFQYDVQQDEw5URVNUIFJPT1QgQ0VSVDAeFw0wOTA4MDMw
+NDQ1MDlaFw0xMDEyMjgwNDQ1MDlaMDMxGzAZBgNVBAoTElRFU1RFUiBDRVJUSUZJ
+Q0FURTEUMBIGA1UEAwwLMTEjdGVzdGVyMTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0A
+MIGJAoGBAJQ/Jxi48pGxbV0Tt8hUZVfBq2IrTUf+2TZVf0QWKHpVnWFkdEQQVAvZ
+LJI0H9j0IJva/5vaa7X5gmmXP6fubkmQVAwzZVYRiLq87wvwErZBgG1cwEbmfE90
+xrcPSuOPUpm120BbcSg1xEYiSHHCR2mTT4D/d2PQao9/4lC2qk5NAgMBAAEwDQYJ
+KoZIhvcNAQEFBQADgYEAh7dzS6D0Xe7XMSxBJAStcgd5GgyFoUr4lT22KowY1UPM
+SuHCfTv9S8B3MvmnRedflPyMGdzcAM2ak8kH0hOImWESeUHGWLs6222R1XxCX2QB
+jB+RBxU4HlCZaCzOrHtsvykcjgCUEgbDIOH4Qg7RacV8GtoF+p0upjHNrqoaito=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,57E31D0A675E913A
+
+S4v09hyzG23Ouv8+3dqtt0k3mi7xn4YslHD27gDSuwvty11i38ui+dv7hNe5Y1mt
+1gb3PIFwBqiGYToNamdwI2ITNpvR7dVyC++J1IUAa1D8IlYhNY/EQI3k/P5MZbQl
+WViGGLIn7EWTVwavmdYInOj+jGf8J44Hu1SOdeftQGq95nbdDCxSbemo7bigO5r/
+aCoCLV6iasbi/JiJNuB/Kq96R1KeZVbka1rKuZIXL5+nqPP/UesCW8/+r84Xdx/P
+7GDN7x0XgwQwxu7/AJtLF1ID08kkoyL6PiijIN2zRk4vP5iMWjpFdlVgjpKngoo1
++nMzEGpViJcyaVtiVn5XeJPPEGevupuk+UZQmidcerCEF6S6SM29Xhw4VOuEce8R
+XnXZdwPBaVps25/Pf0rzZ5NkSQf9fXY0BiT8dMBtD1c6IgvpOlGgVIyKgAEUFyMj
+rUH3fYTkZ6SfFoSXZSmvsXGzHcYLQhcsrSzdEKZ+V3WMPSlKXuYPbvDZqwp32KhJ
+ajxPJuAr47p7Qp/Nrn0pESK8KN+fXeA8blDqu9YKlJSZ+81WreFh5RtRBTRjyAq7
+zP7pEwZAQQTch7jY3IklAzBffPj+WOV1Xo+VU4dSVdbP5vukwKxEMnIaECZHw9N6
+sE2fD5FFOCltPgwVXO6d79nyKYxmS8wxmH7aGwpzJ6DYS2y0TWwkp96PS+3zlBZb
+vW06RxxTolEBDkC75JAo2l+kjd8MjQfDkW2FbtB5TN9o6M/mmRJ5nRlUdlU8/SJL
+05zhsHTPb+Cpk6ajefNAb/8r2p6MLr5QnRHe7CoIXFOWHt1F7qYjBg==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tester12cert.pem b/tests/tester12cert.pem
new file mode 100644
index 0000000..b859436
--- /dev/null
+++ b/tests/tester12cert.pem
@@ -0,0 +1,96 @@
+
+-----BEGIN CERTIFICATE-----
+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
+-----END CERTIFICATE-----
+
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,D674B78762D889A4
+
+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
+-----END RSA PRIVATE KEY-----
+
diff --git a/tests/tester12pkey.pem b/tests/tester12pkey.pem
new file mode 100644
index 0000000..76e5a0d
--- /dev/null
+++ b/tests/tester12pkey.pem
@@ -0,0 +1,54 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,D674B78762D889A4
+
+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
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tester13cert.pem b/tests/tester13cert.pem
new file mode 100644
index 0000000..09f907c
--- /dev/null
+++ b/tests/tester13cert.pem
@@ -0,0 +1,98 @@
+
+-----BEGIN CERTIFICATE-----
+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
+sg2684OxJ9YXz8o=
+-----END CERTIFICATE-----
+
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,B5083979655159C0
+
+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
+-----END RSA PRIVATE KEY-----
+
diff --git a/tests/tester13cert_forged.pem b/tests/tester13cert_forged.pem
new file mode 100644
index 0000000..f12fa59
--- /dev/null
+++ b/tests/tester13cert_forged.pem
@@ -0,0 +1,203 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 831 (0x33f)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=GE, ST=Georgia, CN=Pica Pica Test CA
+ Validity
+ Not Before: May 25 21:19:01 2012 GMT
+ Not After : Jun 27 21:19:01 2022 GMT
+ Subject: C=GE, ST=Georgia, O=\xD1\x82\xD0\xB5\xD1\x81\xD1\x82\xD0\xBE\xD0\xB2\xD0\xB0\xD1\x8F \xD0\xBB\xD0\xB0\xD0\xB1\xD0\xBE\xD1\x80\xD0\xB0\xD1\x82\xD0\xBE\xD1\x80\xD0\xB8\xD1\x8F, CN=13#\xE1\x83\xA2\xE1\x83\x94\xE1\x83\xA1\xE1\x83\xA2\xE1\x83\x94\xE1\x83\xA0\xE1\x83\x98N13
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:a5:cf:04:3d:4c:c4:55:83:b4:de:d6:ac:8c:50:
+ bd:5b:16:99:ca:ee:f6:ed:9f:ca:0d:f8:4e:94:b5:
+ 56:dc:db:af:cf:af:10:7b:07:b9:b0:80:e0:66:5b:
+ d7:78:9b:cb:84:f7:4e:8c:31:e8:b4:3d:eb:dd:c9:
+ e0:20:22:9a:9c:ae:f3:35:2a:cd:66:6f:c5:bd:9e:
+ 75:a5:a3:a2:94:5b:dc:40:12:7a:bc:0d:7d:c6:89:
+ 55:15:eb:56:d6:5d:e7:54:29:ce:31:b9:30:a4:d9:
+ ee:da:72:c6:96:be:80:b4:1e:7c:7c:6e:6a:7f:7a:
+ 10:66:ed:a8:79:52:f2:3e:43:ca:97:8e:88:03:92:
+ 9e:fa:62:fd:f8:52:39:eb:20:09:71:ea:01:fc:97:
+ 6d:61:5e:8f:9d:44:6d:33:fc:de:01:df:87:42:ab:
+ b1:0a:99:e8:3a:06:67:ce:3d:06:68:90:60:8f:18:
+ 79:ec:52:35:2a:1e:1d:05:75:e9:c0:33:62:15:8e:
+ 3a:3c:e9:ec:d0:0c:4c:63:d9:82:27:46:71:2b:91:
+ 8c:e3:12:ce:75:35:52:44:a4:2b:23:65:8c:19:cf:
+ f1:37:8d:fc:a7:a8:98:0c:4a:77:82:70:ee:fe:06:
+ c5:b0:db:25:fb:b0:79:76:ec:5f:e0:3a:05:80:cb:
+ b0:17:61:18:06:6c:0c:9f:0a:cc:f1:b7:41:74:8f:
+ de:e6:3a:0c:ad:c3:ea:7d:15:b0:f1:57:61:cd:f4:
+ 91:e3:bd:e3:aa:f2:59:96:0d:1f:67:2a:50:c9:12:
+ 9e:11:3e:bf:2c:b8:4b:f7:d5:1a:ba:64:17:49:e8:
+ d7:cc:72:28:c0:1c:47:3e:c0:f7:b6:f6:7f:f1:58:
+ d5:90:db:a4:20:94:9e:8f:4c:9d:95:1f:65:98:ac:
+ c9:e8:d7:81:86:e1:bd:4d:c4:e9:c1:54:e2:92:2d:
+ fd:6b:0e:02:45:b9:a0:48:7c:74:39:ff:09:c4:ca:
+ d3:d6:22:b6:e3:b2:5a:93:82:c1:4c:db:89:41:f3:
+ 08:f1:1c:2a:dd:a7:52:be:08:d2:e0:79:a8:61:bf:
+ 3a:5d:4f:3d:e7:28:df:71:db:d9:97:5e:ac:3f:c1:
+ dd:2f:bd:9b:2c:50:69:f2:cb:1b:2c:63:86:81:2d:
+ 28:fb:15:40:85:39:d2:01:13:0f:26:bb:e5:e7:a4:
+ e9:7c:1e:02:4c:ef:2b:dc:7f:0c:48:b5:f1:ca:55:
+ 8c:59:70:24:01:3a:31:21:53:59:94:d6:e6:5c:c9:
+ 14:41:2b:f2:78:95:6c:eb:ac:eb:0d:05:38:8f:5f:
+ 93:57:0a:fc:04:d4:68:e0:89:72:ac:4d:37:c8:e6:
+ 6d:72:bd
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: sha1WithRSAEncryption
+ 9d:73:26:a1:0a:7c:84:42:d8:27:2f:8d:00:6a:97:21:2b:2c:
+ d5:7a:dc:59:24:cb:ce:4a:c5:c1:94:82:1e:e0:06:1b:76:cf:
+ c9:81:5c:a5:32:0d:cf:c1:25:27:c9:6c:5c:d7:b7:28:ec:1b:
+ 87:e8:1d:6e:60:c9:5f:48:e2:9a:a8:0f:ee:9d:c2:a3:b8:af:
+ a1:cc:10:97:0e:b5:c4:11:62:67:c5:ea:eb:84:01:6d:33:61:
+ 84:ee:51:05:a8:1e:c8:a2:60:c0:e8:23:c1:80:26:00:03:6d:
+ fb:77:60:67:cb:c9:db:f4:e0:ac:83:87:93:25:ce:ea:7a:61:
+ d3:72:4c:b9:fa:62:bc:42:87:57:4c:58:6a:72:3b:79:24:91:
+ d3:31:ae:7a:04:7e:df:e5:8b:75:2a:41:de:45:17:d3:12:1b:
+ 91:fd:85:cc:c2:66:b2:77:b8:fd:c9:51:9c:78:05:85:e6:11:
+ a4:52:f9:8a:52:96:bd:ad:58:51:8d:53:ee:f4:a2:80:95:4f:
+ f7:02:24:9d:8e:7c:c8:d9:dc:87:eb:d5:2f:6b:12:9c:04:31:
+ df:a4:f6:b1:b3:86:49:c3:28:0f:df:d5:64:fa:b9:25:42:e8:
+ 25:a3:27:d8:09:d5:02:ab:31:7c:ad:c1:1e:af:91:51:64:0d:
+ 35:28:df:e2:2d:20:6c:04:5c:5c:de:7e:8d:8a:19:54:c1:c3:
+ 20:b3:80:12:e9:68:53:2d:05:72:f0:c0:a7:31:70:29:03:f1:
+ ef:57:40:8d:94:dd:ee:4d:2a:e0:29:6b:1e:82:e0:8a:9a:06:
+ 62:df:34:06:14:e7:b0:ea:7e:a8:0b:1a:20:64:83:58:dd:22:
+ f4:fe:6d:2a:8b:1a:24:e7:03:41:5f:bf:68:0b:8a:74:4a:38:
+ 49:a3:c3:e7:e9:5c:c7:78:95:23:c0:cd:2e:5e:41:ce:d5:aa:
+ 11:e4:80:84:53:24:00:5a:53:a4:06:22:fb:3d:a1:7c:95:a4:
+ 69:d7:23:23:94:2e:7f:92:c2:8a:d7:5c:46:bc:9c:43:d0:2d:
+ 57:43:9d:5e:34:a3:e3:68:dd:47:5f:ec:a2:97:3d:22:cd:01:
+ 05:2d:ee:04:c6:3d:df:c2:6c:e3:19:de:0d:3a:fb:e1:31:64:
+ 5b:60:7c:ae:5f:53:6a:fd:d9:a1:c0:58:0b:4c:32:97:57:fd:
+ 4b:a2:cc:0d:6f:0c:cf:b3:5c:9b:be:52:2e:08:35:a9:d1:14:
+ 8c:8a:1f:23:02:c2:f2:c4:9b:e7:72:bb:8d:e9:7d:c2:e5:9b:
+ ac:8f:85:3d:86:d2:46:a8:eb:a3:2f:b9:54:1e:57:03:bc:dc:
+ 44:4a:c2:c5:f3:9e:2c:01:75:e4:08:f2:c2:e6:55:2e:38:4e:
+ 49:72:79:ca:52:5c:8f:df:41:ae:d2:63:5a:1b:f5:2c:19:d7:
+ 09:c5:a2:aa:27:2a:b7:eb:cc:eb:92:74:d5:ba:ab:a5:e6:77:
+ 83:31:ee:de:4e:0d:1d:72:1d:22:66:47:d8:f1:b8:9a:b9:b3:
+ b8:ea:94:d6:2e:8b:4e:84:57:9d:71:cc:6c:8b:d1:73:9d:eb:
+ f8:7c:2a:c0:16:59:b1:14:31:b1:24:0b:6d:bf:f1:2e:7c:f8:
+ af:8e:b4:1b:25:5d:b3:52:3d:33:45:54:13:a0:86:ae:5d:c1:
+ 81:f9:e4:48:a3:12:16:b6:e3:22:e1:ec:3f:e3:fb:9a:ca:54:
+ c6:02:ea:7b:58:1e:2b:9b:f6:8a:15:9e:c3:ad:b3:ed:f8:0b:
+ c0:6f:b9:95:ea:ea:4a:25:fe:5e:9d:9e:4c:f5:8d:b2:63:d3:
+ 98:88:1d:22:ce:ea:12:89:fd:31:85:5e:54:a6:3a:56:4a:cf:
+ c1:e2:70:ed:9f:bd:9d:e8:ea:ac:a2:77:bc:fb:63:cf:83:d0:
+ c4:83:6a:8f:a5:b2:a6:78:ae:60:c7:06:55:d6:7d:3d:5a:5d:
+ 88:4b:9f:87:bd:25:b6:1a:4a:18:28:a1:36:c0:ab:5b:87:0b:
+ b3:26:68:6a:12:6e:ba:1b:d5:98:e7:b7:0e:6b:22:35:de:ca:
+ 09:84:69:bd:7f:0b:fa:0b:2e:12:60:48:37:5d:ba:c1:67:04:
+ 93:40:95:fc:fa:40:c8:79:fe:7a:2d:ff:a8:3d:80:96:19:10:
+ 36:eb:6f:30:93:b7:c4:77:29:51:c3:99:8a:e0:50:00:b0:d5:
+ d6:ee:33:b8:a1:b4:eb:d4:35:35:66:bd:2f:2d:64:d7:67:c6:
+ 49:b5:59:77:83:28:08:56:0e:68:e8:e8:35:7d:43:b2:b8:c7:
+ b4:e6:8b:df:14:f0:a2:67:b9:b2:97:29:b5:e3:e5:46:39:ec:
+ 59:35:90:1b:1f:67:c0:fc:68:8e:25:83:67:73:8f:47:24:60:
+ bf:3d:21:06:25:e0:69:8c:7f:7d:5e:b5:4d:ab:cc:37:8f:65:
+ 51:9c:6d:7f:31:eb:12:09:82:da:32:26:98:77:6f:50:fa:5e:
+ 0c:dd:76:73:fa:14:e2:cf:81:e2:5a:a7:b5:67:f7:cd:73:b6:
+ e7:e0:27:10:b6:64:ef:92:7f:df:e4:cd:2b:89:02:27:a4:d5:
+ 29:f5:62:29:56:80:2e:53:a1:e7:b9:6c:a2:02:36:c4:59:62:
+ d2:d2:7e:d5:8c:8d:c8:93:e5:cd:b6:4f:24:30:e3:0e:06:94:
+ 25:50:f5:d0:a4:ae:24:07:4c:f5:b0:e6:54:58:fd:46
+-----BEGIN CERTIFICATE-----
+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
+riQHTPWw5lRY/UY=
+-----END CERTIFICATE-----
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+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
+0XY=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/tests/tester13pkey.pem b/tests/tester13pkey.pem
new file mode 100644
index 0000000..6b98e9f
--- /dev/null
+++ b/tests/tester13pkey.pem
@@ -0,0 +1,54 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,B5083979655159C0
+
+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
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tester13pkey_forged.pem b/tests/tester13pkey_forged.pem
new file mode 100644
index 0000000..65c13dd
--- /dev/null
+++ b/tests/tester13pkey_forged.pem
@@ -0,0 +1,54 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+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
+0XY=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/tests/tester13req_forged.pem b/tests/tester13req_forged.pem
new file mode 100644
index 0000000..bd5aca5
--- /dev/null
+++ b/tests/tester13req_forged.pem
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIEvDCCAqQCAQAwdzELMAkGA1UEBhMCR0UxEDAOBgNVBAgMB0dlb3JnaWExMDAu
+BgNVBAoMJ9GC0LXRgdGC0L7QstCw0Y8g0LvQsNCx0L7RgNCw0YLQvtGA0LjRjzEk
+MCIGA1UEAwwbMTMj4YOi4YOU4YOh4YOi4YOU4YOg4YOYTjEzMIICIjANBgkqhkiG
+9w0BAQEFAAOCAg8AMIICCgKCAgEApc8EPUzEVYO03tasjFC9WxaZyu727Z/KDfhO
+lLVW3Nuvz68Qewe5sIDgZlvXeJvLhPdOjDHotD3r3cngICKanK7zNSrNZm/FvZ51
+paOilFvcQBJ6vA19xolVFetW1l3nVCnOMbkwpNnu2nLGlr6AtB58fG5qf3oQZu2o
+eVLyPkPKl46IA5Ke+mL9+FI56yAJceoB/JdtYV6PnURtM/zeAd+HQquxCpnoOgZn
+zj0GaJBgjxh57FI1Kh4dBXXpwDNiFY46POns0AxMY9mCJ0ZxK5GM4xLOdTVSRKQr
+I2WMGc/xN438p6iYDEp3gnDu/gbFsNsl+7B5duxf4DoFgMuwF2EYBmwMnwrM8bdB
+dI/e5joMrcPqfRWw8VdhzfSR473jqvJZlg0fZypQyRKeET6/LLhL99UaumQXSejX
+zHIowBxHPsD3tvZ/8VjVkNukIJSej0ydlR9lmKzJ6NeBhuG9TcTpwVTiki39aw4C
+RbmgSHx0Of8JxMrT1iK247Jak4LBTNuJQfMI8Rwq3adSvgjS4HmoYb86XU895yjf
+cdvZl16sP8HdL72bLFBp8ssbLGOGgS0o+xVAhTnSARMPJrvl56TpfB4CTO8r3H8M
+SLXxylWMWXAkAToxIVNZlNbmXMkUQSvyeJVs66zrDQU4j1+TVwr8BNRo4IlyrE03
+yOZtcr0CAwEAAaAAMA0GCSqGSIb3DQEBBQUAA4ICAQAranMcWfYvfQUPXdkYvbrB
+/KtxGADovgdgIoARFDusMowc0rmi6uhekpH867oISYaBTLIdMm8eELSO13ba37wI
+xngrdwQ14vOBiddd4FpMAqqwO+yHAQ9qE0UzwhmNacug0XsQ/1aI5ooel7I9wNe6
+Kg4gFqZLouIRtPXQkC6bVU7wHu1tEz6crCOvBVoVBInvfIZ9rV5yAaWiSNllwnBs
+U9y1H9VMTaMbDdy2Gs8SyCBmPgJIQclpvFFbkebl2+GoDKlU2yA1Z30WW1tTRq4g
+Ln/0sRhuXMQG0NfB968vn1jerxf27L/+ER0LL5eo5XYI98ZYVILWXbJFOUS1lCo4
+PT7wtjl53WAl21weEaQvlF0NWiLczvFmOdt+dyQTUUs9M4DgGNqztYWspW9Vo5nq
+0Fed9HUzbkhRWN0ep8svi+Nh0jZMiFNMPRSDaqm34QIQ0G1UE+MMhS5EBLVg/f5A
+VolVsZjUTquiKToMbWf5Ze1KHMg7nWLN5Q2SJf2lEYv7alHuh0Zak9c2MfkjwU2f
+/X8JCyTwWmPu1zl4U1IDZGTV++PRgM3By0+Y7J1EMkT9SdAjcV4sD6RbWZ4OtOhS
+ceQuxuIql/j3ohxD52C21mV/Avno02o6D9gMJEiktMTKzY+2oh/chLXJN9WyptZe
+Y9+Qv61rSeIvIEj/ziyDVQ==
+-----END CERTIFICATE REQUEST-----
diff --git a/tests/tester5_privkey.pem b/tests/tester5_privkey.pem
new file mode 100755
index 0000000..41f5052
--- /dev/null
+++ b/tests/tester5_privkey.pem
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,067FF34585314EFA
+
+Aj5z7qY0YHfcr0jjm35asHoYAK9MWaAP9bCZg21Ftm+n7h1O9rHAVGBgZnaoKYPO
+LygpIRamsayrp1bvX5LAJozCvhOGFFghfM+dxaKmREeAVArQea/8RiYLQdnC86+2
+qoHbfuJ7cuHLHfPdavORd+I7017m0xAhSe47mZ+h2U+gjKrDUrvJLq8AES3DCjxq
+0YgW5ruDuEyNavS1KRE8fNmqmz33slDlhASxtN+VllRr1n1x7Lnb0S99SOpIRgL3
+M4sYnCfSFXRZt2o36EzM2ba299f7MuxM3lIvhirZObB/VGB7EuAK4lqH07b2jDTc
+UHVDuFsplJZNo5h+CP2Uzg/C4MHzQSu1ArYS+pg0qwUE1ElkMb8ZlUBuJh466qlg
+YqmtruCZZ/Vhg0EFBTg7ZxdiaXDa98YSWucV8eZBgnOmaCNc6ii384QNKnM5sUMH
+cRaNksDFjgyrNwI6dz4zK7pPBjJrxB2AePCTpYJAHIR48Bl3bh8BxI6h6nfuBvhb
+Q2/Iz1Rap+mgW5sNJ7XzFlogbaO1tpqo8iSmn2T3G7djuIAtu6iGuyiA19+nyLdN
+pBQKzDVY8ypHWxlc1x5kLc51uhfEfGWUf7UgXOpVHnIIXvm4GVv62q2h9vrELvxQ
+ZCJJH2dBjYn7yR5Bw40/ooxNJw/963DNL8SZoa2UNFBNBXcLnQAElz91BtgvEOjX
+6vMcsVusizL8QL06BI5UbWCFhz6Y0hqEnQjoWJMscca+jKl89EozT7PtdLP9TJuN
+iWCHjKz6xt12bwXbLXGisZvmq4YAqw5eawVCdHn2+IFwmM5fI0KOIg==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tester5cert.pem b/tests/tester5cert.pem
new file mode 100755
index 0000000..461e9e8
--- /dev/null
+++ b/tests/tester5cert.pem
@@ -0,0 +1,63 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 823 (0x337)
+ Signature Algorithm: md5WithRSAEncryption
+ Issuer: O=TESTNET, CN=TESTNET CA
+ Validity
+ Not Before: Nov 25 17:53:27 2008 GMT
+ Not After : Nov 25 17:53:27 2009 GMT
+ Subject: C=GE, O=TESTNET, CN=5#tester5
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:d8:de:11:ad:7e:09:f7:8a:96:04:7b:4f:87:4b:
+ 3c:44:d7:00:a6:7e:94:1f:33:93:ee:81:f5:20:b0:
+ b3:c3:8d:f6:01:76:44:e7:b0:e9:b5:14:77:d0:4f:
+ df:93:0a:b4:89:e8:bc:a4:e7:b5:61:50:54:d2:ea:
+ af:68:62:6a:6f:84:ab:d8:17:3d:de:6b:8d:13:9c:
+ aa:8d:f2:12:40:49:a5:d9:0b:6f:c7:ce:0b:9c:4f:
+ 89:b3:cb:5d:3f:6a:7c:3a:7d:04:ef:1d:45:df:e1:
+ e2:fa:75:0e:cd:5e:c2:e1:d7:56:db:9f:0d:db:7f:
+ 8f:fe:ad:79:e4:bc:78:69:e7
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: md5WithRSAEncryption
+ 86:37:80:6d:fe:8a:15:38:ed:6b:3e:1d:41:7f:f0:6a:6d:03:
+ f2:45:72:62:52:b1:52:4c:8f:e8:53:24:cf:34:98:13:27:33:
+ 3f:89:cd:c4:f4:d5:cc:ab:59:ad:b0:56:39:a1:92:82:25:8b:
+ 49:bf:36:89:32:86:e7:ad:10:7a:85:d9:c8:ac:50:b9:ae:e8:
+ 60:e6:d2:5e:be:c4:83:0e:e1:34:9c:cb:84:61:3a:59:80:f9:
+ 92:c2:5b:5f:aa:82:97:e1:c0:20:a2:01:15:e9:54:87:98:a5:
+ 05:b9:a7:b5:f8:0b:2c:ff:e7:01:f5:c1:07:ef:ff:e1:23:6f:
+ b2:da
+-----BEGIN CERTIFICATE-----
+MIIByjCCATMCAgM3MA0GCSqGSIb3DQEBBAUAMCcxEDAOBgNVBAoTB1RFU1RORVQx
+EzARBgNVBAMTClRFU1RORVQgQ0EwHhcNMDgxMTI1MTc1MzI3WhcNMDkxMTI1MTc1
+MzI3WjAzMQswCQYDVQQGEwJHRTEQMA4GA1UEChMHVEVTVE5FVDESMBAGA1UEAwwJ
+NSN0ZXN0ZXI1MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDY3hGtfgn3ipYE
+e0+HSzxE1wCmfpQfM5PugfUgsLPDjfYBdkTnsOm1FHfQT9+TCrSJ6Lyk57VhUFTS
+6q9oYmpvhKvYFz3ea40TnKqN8hJASaXZC2/HzgucT4mzy10/anw6fQTvHUXf4eL6
+dQ7NXsLh11bbnw3bf4/+rXnkvHhp5wIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAIY3
+gG3+ihU47Ws+HUF/8GptA/JFcmJSsVJMj+hTJM80mBMnMz+JzcT01cyrWa2wVjmh
+koIli0m/NokyhuetEHqF2cisULmu6GDm0l6+xIMO4TScy4RhOlmA+ZLCW1+qgpfh
+wCCiARXpVIeYpQW5p7X4Cyz/5wH1wQfv/+Ejb7La
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,067FF34585314EFA
+
+Aj5z7qY0YHfcr0jjm35asHoYAK9MWaAP9bCZg21Ftm+n7h1O9rHAVGBgZnaoKYPO
+LygpIRamsayrp1bvX5LAJozCvhOGFFghfM+dxaKmREeAVArQea/8RiYLQdnC86+2
+qoHbfuJ7cuHLHfPdavORd+I7017m0xAhSe47mZ+h2U+gjKrDUrvJLq8AES3DCjxq
+0YgW5ruDuEyNavS1KRE8fNmqmz33slDlhASxtN+VllRr1n1x7Lnb0S99SOpIRgL3
+M4sYnCfSFXRZt2o36EzM2ba299f7MuxM3lIvhirZObB/VGB7EuAK4lqH07b2jDTc
+UHVDuFsplJZNo5h+CP2Uzg/C4MHzQSu1ArYS+pg0qwUE1ElkMb8ZlUBuJh466qlg
+YqmtruCZZ/Vhg0EFBTg7ZxdiaXDa98YSWucV8eZBgnOmaCNc6ii384QNKnM5sUMH
+cRaNksDFjgyrNwI6dz4zK7pPBjJrxB2AePCTpYJAHIR48Bl3bh8BxI6h6nfuBvhb
+Q2/Iz1Rap+mgW5sNJ7XzFlogbaO1tpqo8iSmn2T3G7djuIAtu6iGuyiA19+nyLdN
+pBQKzDVY8ypHWxlc1x5kLc51uhfEfGWUf7UgXOpVHnIIXvm4GVv62q2h9vrELvxQ
+ZCJJH2dBjYn7yR5Bw40/ooxNJw/963DNL8SZoa2UNFBNBXcLnQAElz91BtgvEOjX
+6vMcsVusizL8QL06BI5UbWCFhz6Y0hqEnQjoWJMscca+jKl89EozT7PtdLP9TJuN
+iWCHjKz6xt12bwXbLXGisZvmq4YAqw5eawVCdHn2+IFwmM5fI0KOIg==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tester6_privkey.pem b/tests/tester6_privkey.pem
new file mode 100755
index 0000000..a11dc8c
--- /dev/null
+++ b/tests/tester6_privkey.pem
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,DEC8C14F74DCBA84
+
+K5rfKk5EwWPz+3tUYvrLiSICvO2B/liKB9Ew5QrNrNHO7ZPNls64ksEXruApa0/l
+yqVI0p6YDo+E564p4ZeI0RkrgFESUfSXJEFJbk2a/6AnlwpXbEuINSl3dCJMOpvm
+4lwhqb2UFwxLqfSjcQfPN5FaBhayQ86m95nwZ07omQVxCJaZPxG+WQawXIdh4bzU
+VnDjOK6AIuYQ7GgX/8Vh1r20Ix/UwACKyzFj+P+47Pr4FHQ3yRR0DmWCl4N0OQUh
+WJGlTQbrTr/3gY6gNQk3hHQEobq5XDBwfEWCiuZKJZo9AKb2Px892LeFft7zpF3r
+f30u+yOKhyg5Cyt30H+u4/7LcNwYf0zwtxuNgzZT+iD1DH1ZA8KkpGTH0V0ZLWhP
+XlAR7zAyPQ5XllR9cMo+4JEvaAeyDEkpCa36Brh4LmoZPjEwqPwUdbiSGhtMewkO
+EDoF4AZPPzBEf1oicgIxxDQnJLxeekN7TQ51JqOb6rW6mQsFFkwsfwcd1h2mJYmX
+ZYBgvg9e85TdyD17dr54B5f2yQGBLtnWwmN0YuH0e94Xbrak3PY18ZhM9JXSqF6g
+fBP6lCjSRN9dJSGvcPpRFbd8LJB9NsmeNYxumNw2P9Wso2W9XhEELRDHtslDN+fR
+3pdlVklZKIthxSeNlPYP8OFSHPQ0I4jw8Q32PgfX9ThPmlrGa/tXjWyp1aDaNmya
+Wwm+BKw/TTfTSu/XytZi2m1H6g/xc7oUtPw9XqNmw8v2xJVa2X4mFlHp6Uwv9v/J
+p1yPTwv/qfhVajzi1pJEFqP2r0nJ6jxTHcLqdLLXsJnKsDoDmz3KJA==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tester6cert.pem b/tests/tester6cert.pem
new file mode 100755
index 0000000..d268587
--- /dev/null
+++ b/tests/tester6cert.pem
@@ -0,0 +1,63 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 824 (0x338)
+ Signature Algorithm: md5WithRSAEncryption
+ Issuer: O=TESTNET, CN=TESTNET CA
+ Validity
+ Not Before: Nov 25 18:08:24 2008 GMT
+ Not After : Nov 25 18:08:24 2009 GMT
+ Subject: O=TESTNET, CN=6#tester6
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:9e:22:94:99:93:d7:7e:d9:08:b3:e4:eb:54:c7:
+ 66:17:ee:e7:a1:d5:4c:e9:3f:36:1e:72:d7:87:56:
+ 1d:10:79:b9:46:20:11:ce:4d:d5:8d:15:5b:e5:58:
+ 0b:11:67:96:ba:56:44:e6:2f:b3:6f:02:f2:a0:22:
+ f6:fb:ae:22:04:9e:1f:0f:0b:a0:84:8d:94:4d:b3:
+ 12:00:78:bf:1a:a2:18:85:fc:45:61:37:66:df:90:
+ a9:01:33:a4:d8:a5:43:b8:82:f8:3d:2f:d7:f2:fd:
+ b0:0d:3b:fa:a0:2d:be:24:fc:fa:81:eb:26:59:3f:
+ fa:b6:57:64:1a:33:1b:3d:65
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: md5WithRSAEncryption
+ 6e:35:5f:f5:b8:fb:54:ba:b7:44:d1:7f:e4:72:32:9d:06:8f:
+ 8f:46:e3:e1:5a:f1:e7:e2:4f:b3:55:fb:4a:37:fa:6d:c2:cf:
+ 43:7c:67:12:bf:c2:3a:73:d2:60:40:cc:f2:38:a9:19:4b:bf:
+ 3c:58:3b:98:59:d1:05:1a:1d:ce:11:9e:51:b2:f9:99:eb:65:
+ 38:f1:b1:95:d5:ad:fa:a1:0b:5c:70:98:19:6b:19:5f:53:62:
+ 58:ad:2d:0f:ed:dd:04:94:ce:81:c6:25:53:4a:7f:28:e4:9d:
+ f1:6a:ce:c4:fa:2d:42:5c:8a:12:c3:b5:a8:c0:ca:bf:3b:00:
+ 3c:27
+-----BEGIN CERTIFICATE-----
+MIIBvTCCASYCAgM4MA0GCSqGSIb3DQEBBAUAMCcxEDAOBgNVBAoTB1RFU1RORVQx
+EzARBgNVBAMTClRFU1RORVQgQ0EwHhcNMDgxMTI1MTgwODI0WhcNMDkxMTI1MTgw
+ODI0WjAmMRAwDgYDVQQKEwdURVNUTkVUMRIwEAYDVQQDDAk2I3Rlc3RlcjYwgZ8w
+DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJ4ilJmT137ZCLPk61THZhfu56HVTOk/
+Nh5y14dWHRB5uUYgEc5N1Y0VW+VYCxFnlrpWROYvs28C8qAi9vuuIgSeHw8LoISN
+lE2zEgB4vxqiGIX8RWE3Zt+QqQEzpNilQ7iC+D0v1/L9sA07+qAtviT8+oHrJlk/
++rZXZBozGz1lAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAbjVf9bj7VLq3RNF/5HIy
+nQaPj0bj4Vrx5+JPs1X7Sjf6bcLPQ3xnEr/COnPSYEDM8jipGUu/PFg7mFnRBRod
+zhGeUbL5metlOPGxldWt+qELXHCYGWsZX1NiWK0tD+3dBJTOgcYlU0p/KOSd8WrO
+xPotQlyKEsO1qMDKvzsAPCc=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,DEC8C14F74DCBA84
+
+K5rfKk5EwWPz+3tUYvrLiSICvO2B/liKB9Ew5QrNrNHO7ZPNls64ksEXruApa0/l
+yqVI0p6YDo+E564p4ZeI0RkrgFESUfSXJEFJbk2a/6AnlwpXbEuINSl3dCJMOpvm
+4lwhqb2UFwxLqfSjcQfPN5FaBhayQ86m95nwZ07omQVxCJaZPxG+WQawXIdh4bzU
+VnDjOK6AIuYQ7GgX/8Vh1r20Ix/UwACKyzFj+P+47Pr4FHQ3yRR0DmWCl4N0OQUh
+WJGlTQbrTr/3gY6gNQk3hHQEobq5XDBwfEWCiuZKJZo9AKb2Px892LeFft7zpF3r
+f30u+yOKhyg5Cyt30H+u4/7LcNwYf0zwtxuNgzZT+iD1DH1ZA8KkpGTH0V0ZLWhP
+XlAR7zAyPQ5XllR9cMo+4JEvaAeyDEkpCa36Brh4LmoZPjEwqPwUdbiSGhtMewkO
+EDoF4AZPPzBEf1oicgIxxDQnJLxeekN7TQ51JqOb6rW6mQsFFkwsfwcd1h2mJYmX
+ZYBgvg9e85TdyD17dr54B5f2yQGBLtnWwmN0YuH0e94Xbrak3PY18ZhM9JXSqF6g
+fBP6lCjSRN9dJSGvcPpRFbd8LJB9NsmeNYxumNw2P9Wso2W9XhEELRDHtslDN+fR
+3pdlVklZKIthxSeNlPYP8OFSHPQ0I4jw8Q32PgfX9ThPmlrGa/tXjWyp1aDaNmya
+Wwm+BKw/TTfTSu/XytZi2m1H6g/xc7oUtPw9XqNmw8v2xJVa2X4mFlHp6Uwv9v/J
+p1yPTwv/qfhVajzi1pJEFqP2r0nJ6jxTHcLqdLLXsJnKsDoDmz3KJA==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tester6req.pem b/tests/tester6req.pem
new file mode 100755
index 0000000..f81e157
--- /dev/null
+++ b/tests/tester6req.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBZTCBzwIBADAmMRAwDgYDVQQKEwdURVNUTkVUMRIwEAYDVQQDDAk2I3Rlc3Rl
+cjYwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJ4ilJmT137ZCLPk61THZhfu
+56HVTOk/Nh5y14dWHRB5uUYgEc5N1Y0VW+VYCxFnlrpWROYvs28C8qAi9vuuIgSe
+Hw8LoISNlE2zEgB4vxqiGIX8RWE3Zt+QqQEzpNilQ7iC+D0v1/L9sA07+qAtviT8
++oHrJlk/+rZXZBozGz1lAgMBAAGgADANBgkqhkiG9w0BAQUFAAOBgQCSbYxYVg12
+A4zz+lUw3ruj6i8PaXSC+euQ5TsfsHpBnBlUoZEPEnyyhn2q38bdLg03O+VcFFcY
+AwFBF5kb55Q1F2BgipdiQVJ/lYHNM73Oj7XrBOTezHf0RvhKEocFoevEB/eXD12u
+7jAxgKD/q7tjOwiETmQ8phnSUzrqc05LJg==
+-----END CERTIFICATE REQUEST-----
diff --git a/tests/tester8_privkey.pem b/tests/tester8_privkey.pem
new file mode 100755
index 0000000..01bba5a
--- /dev/null
+++ b/tests/tester8_privkey.pem
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,6D6F87F691984AE7
+
+dejRxz88M3Njbhy8XMIjpQdVFxnfgj8l5B+pMPjsu9rYq+yt3J0Ax2L55ngcZDgN
+XbFM/iGhuxwFhZ5+J0dZek8pfNcbHCcbSPkpofxitw6FfVH/Nx/fTDh7scfHvezG
+/870bZnxYKPfOO2HD53g4k1v3RHcNmoajAzxOiaZhYwY0ODAu8ZqSyziCc1ItSWZ
+VFbNFn1y/duhSeCIVqrYFuJkTJLxLEz+VbZyM6aIJiwUUHZe4K1IPCdE3on0FXUY
+0NU9OCI1T2dzEBznGyVF1fHN37NAT1esrZTQhRDOIJZ80hTgZPcPC/S/1Ay7xLzo
+zm0DaeUPggBCiPn6IuJ625UfryWlk0Da6eejNtjo62xLvn5QbGzenMYYruPMhXiK
+QoNSJ0Ug5gz3AfF5cX44Nl0TPj/pZQKoPXskub3B/Vjmzt9Ainhw0HS9agdILftX
+Rhvd7d3tgEFV/m0TGtfFsYaeoyIUtSz/cl6HmdturGEveH4RFl0NHKgdq+wN3zx3
++3yrK0NsFcTGB2E6unzdKd2+0/2kwqOgXpL88x4+hV6zIIMIl/wiaX8cO5rgKuXz
+ZURmRvbO1u3mNGgXy6ZsJ6rFM7hUZJwvzRQPtveSyADIXYgHlkbBFJXpZD0e6LN+
+rHq6RraPEs77dFxWvM4cQP2Fj828sxQZ8mM/zoQQH13D9Hs3Pr0v/O+HQB792STP
+zCCLcnTut03BXRkmEP3NH8Ux6H6hyWYRhrGKxozj3l5qTeii8Tc3w2TbJhLdlZpc
+flJ7gT60yrOs+2t1G4/K75ufhguAkeXgOG7l77Hva1zEvAK2Ar6kpQ==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tester8cert.pem b/tests/tester8cert.pem
new file mode 100755
index 0000000..1fd3ed8
--- /dev/null
+++ b/tests/tester8cert.pem
@@ -0,0 +1,63 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 825 (0x339)
+ Signature Algorithm: md5WithRSAEncryption
+ Issuer: O=TESTNET, CN=TESTNET CA
+ Validity
+ Not Before: Dec 23 22:48:58 2008 GMT
+ Not After : Dec 23 22:48:58 2009 GMT
+ Subject: O=TESTNET, CN=8#tester8
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:d8:30:82:43:97:15:a9:ce:c2:ed:dd:55:8e:26:
+ 5e:83:57:31:a8:e8:a7:63:0d:5e:6d:e1:bc:03:f6:
+ e1:c6:2b:c4:7e:d0:04:ee:a7:9f:9d:6d:a1:68:1c:
+ 31:c1:7f:70:0e:e3:51:48:46:d5:01:33:bc:f9:b1:
+ bc:1e:bb:ac:85:de:10:65:1d:55:da:51:b9:d4:c0:
+ 4d:07:1f:65:fe:8e:ab:7b:ef:d6:16:67:ab:62:6e:
+ 42:f9:98:a6:f2:02:a2:c9:4e:d5:db:fc:3a:cb:df:
+ 2b:75:7e:ae:55:8d:7d:6e:a1:4b:95:c4:76:c5:a6:
+ 87:6a:12:25:1a:dd:e1:be:a3
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: md5WithRSAEncryption
+ 6d:5c:44:a1:e9:98:f8:ed:e7:b9:d9:3f:77:64:de:03:76:4e:
+ 34:82:22:73:cb:f0:77:1b:b5:52:ff:72:64:1d:b7:cf:ad:b1:
+ 68:eb:51:50:ca:af:1b:4c:db:d6:3e:cf:b6:45:01:1c:8c:14:
+ f0:68:31:5e:38:8c:5a:49:69:0a:bd:9d:5d:8d:7a:a0:8e:03:
+ 73:86:79:ab:49:f6:4b:ff:96:12:a4:ee:cf:69:b5:33:21:03:
+ 2c:d2:58:31:33:9b:be:c1:a2:17:f9:06:16:f5:c0:07:e3:a5:
+ e5:e5:41:46:40:47:14:58:f9:33:91:08:2f:ae:63:96:47:0f:
+ 0a:82
+-----BEGIN CERTIFICATE-----
+MIIBvTCCASYCAgM5MA0GCSqGSIb3DQEBBAUAMCcxEDAOBgNVBAoTB1RFU1RORVQx
+EzARBgNVBAMTClRFU1RORVQgQ0EwHhcNMDgxMjIzMjI0ODU4WhcNMDkxMjIzMjI0
+ODU4WjAmMRAwDgYDVQQKEwdURVNUTkVUMRIwEAYDVQQDDAk4I3Rlc3RlcjgwgZ8w
+DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANgwgkOXFanOwu3dVY4mXoNXMajop2MN
+Xm3hvAP24cYrxH7QBO6nn51toWgcMcF/cA7jUUhG1QEzvPmxvB67rIXeEGUdVdpR
+udTATQcfZf6Oq3vv1hZnq2JuQvmYpvICoslO1dv8OsvfK3V+rlWNfW6hS5XEdsWm
+h2oSJRrd4b6jAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAbVxEoemY+O3nudk/d2Te
+A3ZONIIic8vwdxu1Uv9yZB23z62xaOtRUMqvG0zb1j7PtkUBHIwU8GgxXjiMWklp
+Cr2dXY16oI4Dc4Z5q0n2S/+WEqTuz2m1MyEDLNJYMTObvsGiF/kGFvXAB+Ol5eVB
+RkBHFFj5M5EIL65jlkcPCoI=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,6D6F87F691984AE7
+
+dejRxz88M3Njbhy8XMIjpQdVFxnfgj8l5B+pMPjsu9rYq+yt3J0Ax2L55ngcZDgN
+XbFM/iGhuxwFhZ5+J0dZek8pfNcbHCcbSPkpofxitw6FfVH/Nx/fTDh7scfHvezG
+/870bZnxYKPfOO2HD53g4k1v3RHcNmoajAzxOiaZhYwY0ODAu8ZqSyziCc1ItSWZ
+VFbNFn1y/duhSeCIVqrYFuJkTJLxLEz+VbZyM6aIJiwUUHZe4K1IPCdE3on0FXUY
+0NU9OCI1T2dzEBznGyVF1fHN37NAT1esrZTQhRDOIJZ80hTgZPcPC/S/1Ay7xLzo
+zm0DaeUPggBCiPn6IuJ625UfryWlk0Da6eejNtjo62xLvn5QbGzenMYYruPMhXiK
+QoNSJ0Ug5gz3AfF5cX44Nl0TPj/pZQKoPXskub3B/Vjmzt9Ainhw0HS9agdILftX
+Rhvd7d3tgEFV/m0TGtfFsYaeoyIUtSz/cl6HmdturGEveH4RFl0NHKgdq+wN3zx3
++3yrK0NsFcTGB2E6unzdKd2+0/2kwqOgXpL88x4+hV6zIIMIl/wiaX8cO5rgKuXz
+ZURmRvbO1u3mNGgXy6ZsJ6rFM7hUZJwvzRQPtveSyADIXYgHlkbBFJXpZD0e6LN+
+rHq6RraPEs77dFxWvM4cQP2Fj828sxQZ8mM/zoQQH13D9Hs3Pr0v/O+HQB792STP
+zCCLcnTut03BXRkmEP3NH8Ux6H6hyWYRhrGKxozj3l5qTeii8Tc3w2TbJhLdlZpc
+flJ7gT60yrOs+2t1G4/K75ufhguAkeXgOG7l77Hva1zEvAK2Ar6kpQ==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tester8req.pem b/tests/tester8req.pem
new file mode 100755
index 0000000..3e29232
--- /dev/null
+++ b/tests/tester8req.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBZTCBzwIBADAmMRAwDgYDVQQKEwdURVNUTkVUMRIwEAYDVQQDDAk4I3Rlc3Rl
+cjgwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANgwgkOXFanOwu3dVY4mXoNX
+Majop2MNXm3hvAP24cYrxH7QBO6nn51toWgcMcF/cA7jUUhG1QEzvPmxvB67rIXe
+EGUdVdpRudTATQcfZf6Oq3vv1hZnq2JuQvmYpvICoslO1dv8OsvfK3V+rlWNfW6h
+S5XEdsWmh2oSJRrd4b6jAgMBAAGgADANBgkqhkiG9w0BAQUFAAOBgQDBJVHaNTzB
+ermwktucZVjXaIneHkSihcqqDBCRGCknAkeX9fSwVfKcLtMMV9Q/jxVyQ92ziIhA
+TXKhIUA1ns4WVYwvwtQ445of7xRTTNIVXBqQM3RzPP4AUTFmkmjNvg7Gg4I3s89A
+fkJBSzP3f1JIBYqdl8194/mwle4YuL/vwQ==
+-----END CERTIFICATE REQUEST-----
diff --git a/tests/tester9_privkey.pem b/tests/tester9_privkey.pem
new file mode 100755
index 0000000..ba0b815
--- /dev/null
+++ b/tests/tester9_privkey.pem
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,0EC286726E7BF725
+
+QHMJ/INR+5bgul3tBeZhNIH+R3ILZsgE84+xA43J+A0GkRTf9KzUfwDeALlWVgXA
+ejxQq9re0I3FadDbAfLCNBGz3xVAwaq2zFN+UbPhzuVbx63SsLV4+eXg9eRy9ACK
+zG6Vl9bW0PWNMhFgjj6en6UnI187X4oR5OMHjgKzVrrmNfB5kxxtpEuo+jBHKhMN
+7X5qW0Qu44CaV9OcWwdniXRMdBN63nWPHpx/sJT0UBBOy2ZBs1Mo4fOnyJ5ucOzn
+d8+NLx8nRDorTwwCp/2ehPlMPScyLaFCct5bvDETQ6bOVntA34k3sOaoukgGWf46
+XZYW9VBLeOtjyxyn+A+ciId8d1sh1s9NFH21fGABSuhTBVOkDBqkugG4OR4Lsokr
+QqzPmXeKtD9x8GOrnLdCh/UcVBCANclO4Opls5cRYeCap1Dx9HPw9RpMczN4kpQz
+u5LZall4XI7ngPebB2F/nn3Ip6tCMdKsReGq9hAG1nT4FVD2pEpj9guiyk7DOjXZ
+ZGPANBMtfH1FPiv1p6a1bhPwtQ3jTF6vH7z5bWgVbHxuCWvqy2Aw/vqE2Qlh+g+V
+h+TKYV7KH67HG4BXIhwx/A8mtosb1I7ctud/et0V4TqM6BAYDrGL5UI2GBAkT1W4
+Vb0szLdmClKoPQ3Wsoy/729xqmxW7+CEaYAVBJnccknJ5grwDUF5Uwmpl74Us4yX
+ToRl9ssj2VvznKW0VGoT4W2eaJp/rQX50lA1oRLraItNtplOPSX15/KztJAO5PAh
+ofMaBG4YT5Y/AalPbezsKZaS9lUOlZYUxs1++Id8dKAOls8tFaauQQ==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tester9cert.pem b/tests/tester9cert.pem
new file mode 100755
index 0000000..835e6f0
--- /dev/null
+++ b/tests/tester9cert.pem
@@ -0,0 +1,63 @@
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 826 (0x33a)
+ Signature Algorithm: md5WithRSAEncryption
+ Issuer: O=TESTNET, CN=TESTNET CA
+ Validity
+ Not Before: Dec 23 22:49:35 2008 GMT
+ Not After : Dec 23 22:49:35 2009 GMT
+ Subject: O=TESTNET, CN=9#tester9
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:b0:d2:ba:8f:cb:8d:f3:5e:a9:05:e9:22:a0:60:
+ a8:3c:6b:c2:71:d9:f4:eb:85:d6:fd:5d:7a:8a:b4:
+ e3:1c:83:57:a7:77:56:c9:0c:20:4c:33:d9:40:92:
+ 64:f6:a2:8c:a9:77:1d:fe:b2:b9:ae:30:c8:b2:47:
+ d4:2e:2f:52:30:d8:14:e3:33:8a:47:27:68:47:4e:
+ 9c:c0:72:08:98:27:c4:d1:40:eb:97:8f:e7:75:2a:
+ f3:26:ea:7f:d4:01:3d:92:2f:e0:45:b3:97:b0:ed:
+ 19:b2:5b:f8:49:52:38:3a:c3:33:cc:c0:15:42:2b:
+ dc:f9:cb:73:94:1b:23:92:cf
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: md5WithRSAEncryption
+ 5a:17:8d:3c:f4:82:2e:1e:de:37:f7:21:49:2f:65:ca:88:c9:
+ 7e:cf:a4:92:6a:25:d7:1b:3d:35:b1:88:ca:41:6a:a7:d2:ea:
+ 45:53:39:b0:eb:99:ed:ab:10:46:71:0a:61:a4:a2:6f:9a:c1:
+ 2c:87:9e:c3:0a:b6:36:10:e9:91:e8:25:6b:da:9a:ec:09:55:
+ ea:51:6c:c9:07:21:b1:e6:83:33:1d:3a:91:4f:26:bd:d6:29:
+ e9:72:0f:72:51:31:c4:2a:29:92:ca:bb:5e:73:5c:f9:4d:19:
+ 57:d0:95:ab:bc:f0:30:6c:9f:bd:5e:a7:4d:16:0f:b3:9e:20:
+ 79:16
+-----BEGIN CERTIFICATE-----
+MIIBvTCCASYCAgM6MA0GCSqGSIb3DQEBBAUAMCcxEDAOBgNVBAoTB1RFU1RORVQx
+EzARBgNVBAMTClRFU1RORVQgQ0EwHhcNMDgxMjIzMjI0OTM1WhcNMDkxMjIzMjI0
+OTM1WjAmMRAwDgYDVQQKEwdURVNUTkVUMRIwEAYDVQQDDAk5I3Rlc3RlcjkwgZ8w
+DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALDSuo/LjfNeqQXpIqBgqDxrwnHZ9OuF
+1v1deoq04xyDV6d3VskMIEwz2UCSZPaijKl3Hf6yua4wyLJH1C4vUjDYFOMzikcn
+aEdOnMByCJgnxNFA65eP53Uq8ybqf9QBPZIv4EWzl7DtGbJb+ElSODrDM8zAFUIr
+3PnLc5QbI5LPAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAWheNPPSCLh7eN/chSS9l
+yojJfs+kkmol1xs9NbGIykFqp9LqRVM5sOuZ7asQRnEKYaSib5rBLIeewwq2NhDp
+kegla9qa7AlV6lFsyQchseaDMx06kU8mvdYp6XIPclExxCopksq7XnNc+U0ZV9CV
+q7zwMGyfvV6nTRYPs54geRY=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,0EC286726E7BF725
+
+QHMJ/INR+5bgul3tBeZhNIH+R3ILZsgE84+xA43J+A0GkRTf9KzUfwDeALlWVgXA
+ejxQq9re0I3FadDbAfLCNBGz3xVAwaq2zFN+UbPhzuVbx63SsLV4+eXg9eRy9ACK
+zG6Vl9bW0PWNMhFgjj6en6UnI187X4oR5OMHjgKzVrrmNfB5kxxtpEuo+jBHKhMN
+7X5qW0Qu44CaV9OcWwdniXRMdBN63nWPHpx/sJT0UBBOy2ZBs1Mo4fOnyJ5ucOzn
+d8+NLx8nRDorTwwCp/2ehPlMPScyLaFCct5bvDETQ6bOVntA34k3sOaoukgGWf46
+XZYW9VBLeOtjyxyn+A+ciId8d1sh1s9NFH21fGABSuhTBVOkDBqkugG4OR4Lsokr
+QqzPmXeKtD9x8GOrnLdCh/UcVBCANclO4Opls5cRYeCap1Dx9HPw9RpMczN4kpQz
+u5LZall4XI7ngPebB2F/nn3Ip6tCMdKsReGq9hAG1nT4FVD2pEpj9guiyk7DOjXZ
+ZGPANBMtfH1FPiv1p6a1bhPwtQ3jTF6vH7z5bWgVbHxuCWvqy2Aw/vqE2Qlh+g+V
+h+TKYV7KH67HG4BXIhwx/A8mtosb1I7ctud/et0V4TqM6BAYDrGL5UI2GBAkT1W4
+Vb0szLdmClKoPQ3Wsoy/729xqmxW7+CEaYAVBJnccknJ5grwDUF5Uwmpl74Us4yX
+ToRl9ssj2VvznKW0VGoT4W2eaJp/rQX50lA1oRLraItNtplOPSX15/KztJAO5PAh
+ofMaBG4YT5Y/AalPbezsKZaS9lUOlZYUxs1++Id8dKAOls8tFaauQQ==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/tester9req.pem b/tests/tester9req.pem
new file mode 100755
index 0000000..f54d67b
--- /dev/null
+++ b/tests/tester9req.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBZTCBzwIBADAmMRAwDgYDVQQKEwdURVNUTkVUMRIwEAYDVQQDDAk5I3Rlc3Rl
+cjkwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALDSuo/LjfNeqQXpIqBgqDxr
+wnHZ9OuF1v1deoq04xyDV6d3VskMIEwz2UCSZPaijKl3Hf6yua4wyLJH1C4vUjDY
+FOMzikcnaEdOnMByCJgnxNFA65eP53Uq8ybqf9QBPZIv4EWzl7DtGbJb+ElSODrD
+M8zAFUIr3PnLc5QbI5LPAgMBAAGgADANBgkqhkiG9w0BAQUFAAOBgQAi2bNSrWTs
+kzwUcJugFIjQcTOl6+jS7H1UNZlCfnHw1znjxlcDhYWesWH1uigiqI9x+dH9mSA/
+UIWEc3UBKRcLUDLl3jg8XtoZddsVnuUP4qBdKAuIkOmuu5Cwl6Buo4UO8aOZlxvV
+ATI3KZTBlm0q4YT7k6qgr9jTeCTtzMCtUg==
+-----END CERTIFICATE REQUEST-----
diff --git a/tests/trustedCA/newtestca.pem b/tests/trustedCA/newtestca.pem
new file mode 100644
index 0000000..9bba2c9
--- /dev/null
+++ b/tests/trustedCA/newtestca.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICiTCCAfKgAwIBAgIJAJACtwPaX6NqMA0GCSqGSIb3DQEBBQUAMDgxHTAbBgNV
+BAoTFFRlc3RpbmcgY2VydGlmaWNhdGVzMRcwFQYDVQQDEw5URVNUIFJPT1QgQ0VS
+VDAeFw0wOTA4MDEyMzI4NThaFw0xMDEyMjYyMzI4NThaMDgxHTAbBgNVBAoTFFRl
+c3RpbmcgY2VydGlmaWNhdGVzMRcwFQYDVQQDEw5URVNUIFJPT1QgQ0VSVDCBnzAN
+BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1YFmjCqUUt6qJkqJgAiZV1p+xdYKB75J
+iLTSwUVD21ebh2dwshYtVhYHLTrNZE3yLvsUrc/EVu4525Xz08XDyJnMtSSroeoY
+MbPMrdJ31JxOr3XOsq0O98WY8ZcZRJ6vZ4LcYZQAgkQXnHnIDFTQocf3VxXp69dq
+XNhY53axjkkCAwEAAaOBmjCBlzAdBgNVHQ4EFgQUz1SvyKy3WjTGqbpKa791Jz20
+GacwaAYDVR0jBGEwX4AUz1SvyKy3WjTGqbpKa791Jz20GaehPKQ6MDgxHTAbBgNV
+BAoTFFRlc3RpbmcgY2VydGlmaWNhdGVzMRcwFQYDVQQDEw5URVNUIFJPT1QgQ0VS
+VIIJAJACtwPaX6NqMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAwjjG
+qoE1mzNumupkMozPkZHeNUp9FL+8iKKyC6RTJ3hA5KXgOayiX9i82BeG+OSox1qs
+t1u6ygx253+m9+TZmaHqeJc1gtHOJKINN3Tu6rWYu6wCjT+f7exu0o9q/1xGB1Nr
+lBPHPKTQAQxbvuoRGNKfjEXlVHXwFd+BobKAqjw=
+-----END CERTIFICATE-----
diff --git a/tests/trustedCA/picapica_test_CA.pem b/tests/trustedCA/picapica_test_CA.pem
new file mode 100644
index 0000000..f11826f
--- /dev/null
+++ b/tests/trustedCA/picapica_test_CA.pem
@@ -0,0 +1,54 @@
+-----BEGIN CERTIFICATE-----
+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
+qStgK39Nj5myPko=
+-----END CERTIFICATE-----
diff --git a/tests/trustedCA/picapica_test_CA_pkey.pem b/tests/trustedCA/picapica_test_CA_pkey.pem
new file mode 100644
index 0000000..d908370
--- /dev/null
+++ b/tests/trustedCA/picapica_test_CA_pkey.pem
@@ -0,0 +1,102 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,6B353E4D75711879
+
+89dzPOEXgckvgWHtlNPzOARJ5qlzf+czSnJR0l4cobCaYCTx16gtkLmwWezyt7Ga
+fiuGOsu4CTuaxZRLnyCvyG4X43RB4M0clPwVxiI17qz8m8K6NsFMQZj+0UqbjjUW
+IZd7JToVGv6j8ak1hSXM/wTuNJg8+pXpElIeIlUUgYFQBKajm0BkKSclnvRxFMOZ
+fM8xPOf8IMhYucWOazhPDJOH8b0lDxZtjLwKHiJZvl7oSk3WkmwGs0FDabOXnXOE
+V/HgYjYYYePvhYz9f9Dd/uqzNuheFyDKDp8wUnKibFrlGGb2UgcVGFiuugEa3srD
+mcjRdAb0DBgANPCAdrdGgpkgkZObf+u8Hbtfn/fAZptau63twlUY8C88QsxCFsuk
+zVk+i7yrk+5TNlKhtoT+kCHkewrW9ZPgfw+iDdg2pzcagD91YmLcp3Jn7aycKgF8
+KMiEdi6+ZyntcnkLtYghTWUZn0myMM3SrgZsa4qnWw13dDvaP+n1ST62JJeueSva
+R+mOYoc3d4f4glTIEyu2422gn4GshdfjqhByNBZkqB1a0uI9uiwFOA0Uc5SWzZaY
+DX+XxLMGPgTY2jJFN7sO5CzRw09qizLY9+MtlVRo7h2L48O3wlsas4ON2Gr+NmG1
+kz6joufF9M57SrTQMnyzIZ9e/SUGPILdGFdY0Ma4sdxN//C0m0NwU8X7H5z28YdZ
+pq15/eR1i9rV02Zl+jdJ52j2wmYSa4tLCZEclgiHIshkWaJv01n2on6oNZaS3AFN
+VBXwW22kgdIVOStyP1gZpZOdEmWrzBbDnlJYr4UTL0KPS4SWvDRD/MAY/3Ox9nqe
+Fy4ZnnPxiF9ln/wBm5PBQeiL8EPzmq46K0K5UeLDN1EjrDuO0suxdNfmBjHzCQld
+6UV50qHR+LDpbtwIk8Cnvr/wYm+1eMw4htj4KxN2qN9EI0PjsSRwt32pD2SNkFTX
+LXF3AbczekgHtXGgMi4Ubd9FlkazixMblBeHT/3G7wOhHNwkBXt/lLK1B3uoLT/L
+vE6gyvIHsOXpMVAMDw6ByW2fNAWUnOiCCw/CLU4LkoKqzx1o8wjZSgKWuOw4rnmM
+uLjlhpnBAy6S2jyM9gqjFwOx+0Fo/zpTaD3DkVidOLU+iRMwBCbiY+E2CanPOrB5
+UmtgxyP/r04lMJS+9pMEZg+xrXS0AGgUjSl1wGwcmbc6NMr5eM8In1LWCw3keRJ7
+fdk7Z/wUoBu2uPkzaVNz446oy/LXlHt2dx6FAgOvvC/EJuisi/FqPtAi1FDJB92H
+Jr3884S584BQyBfQbeJZnEYzxqTsk5LdfYxeTW/2M6pL/m8s+6kPAUq52xSNVK+M
+cuJ41/r1pB7UPqLEJxZZ4Bq68gxo87JqZMdSV3EXiBtKTFlpRoKG+1pb7XUWLo+j
+5JuzmwGM1W7sCU35fpKLWIpZb8cksTWe+INCLGL9MT2sv6d3vdq1IBuHNBt55nYS
+Xltpf2xSMplhqaPWEPh1uw5KLs7N76Jvh3l/a/fMeqrqv2kzmvFBU75rDdmwG0qF
+605vJp82COPcLrVTGROSNwkPpjARaQBmSjCiThkrcRoJuB7Zyifggh1sZs2eX9lX
+4OfG0DCYeYN39p3+5ieKqGsSXYfc9IeRl3jddUA7E+Br2V6hsvPJyMHAfxSKHDdg
+9vnyTWhWHewH5YpmkcNRgTtquqMxwh/vLsMih8QnzLJsPyZPabd3blS/qPdJY2+P
+vuqeVNjheUxN4jIGwMc7Zx0OTOptm085SgbI7QXPAiC2ldklUN6UGdyOT9nmc5B6
+CVS6fO6Luir9k2zhti5USOMuR6b66fmL9hNOH2tnNzZOLk5JjJo47zJ0oAZEN+8k
+La7w6q1TmNZ7sv1/y7+t+gpNxzWbYE2EtozIOxNGe19NfqouXog/GA3WjGA6X6ps
+FjVI+CqhrfTdrubMBIfHfYfL4CSQ8Cp/GdQIqS9TpDab1pyql7iOlOZRdgAsII0t
+fQ2Ubm7hgjUj1rSOIZ3vk3Ba1ERlJiEsUaHrYrxgkG2uu5sKR67rzaCB1NNSALep
+33YcMCjfcecc61ByhKs4gYJsRtQUO4HVI7xCMqOxwRWRqyXb/e8/TRrlCGHv6dKj
+TbmmwLGJaEtWMvP5XA8EUStkhp/WN+gR/Z+cn1DSyAAbXQ4aQs7s9rs1bPOGL3Bd
+kwtFokGhg6InqOvpmvrorMh70OHU0Yv9aX23PCbFwQz138GlivoV0IuhTQfL4v5F
+4Vf7DF1K/EOtWllykdfIuT09FR5G2ZL3UGdVUSGbkHDyEu0rSA07Iogy49kywqLE
+6l3W50RLDJY/EZMS5JG4WwBmF7vtBppagNclfVriDlP9QlybmOYsFrzQihhTV2Ux
+g0GVYX8EAjWFeh34r17f2KFkcy1fs9i6BFr1PuBeNd4AU9hr5+z+I72mCvIwtrfR
+fmb7/Dw4hbOfVBx8QMo+apEN7SvS51HqmPpM4g1/pgiT/oRceLPMGIcuIvEXqfSG
+IYoje76Wcxc5+XEQmxtvktzOlyLvUFG7BNaJh6Xs70EStv+qodB0AnFb2Jwgn9VI
+Vtk6SeY+dTMUJGnH7SfhUxAHxh7S32/T9RsZ4lFfv9E/FCs68nwvVreGcN7+qXQ5
+Y4CPC7DxzOyJoyEyNz9RtzRitaZxR3jsckdNQhSPWBtOVwMS5/3GKC3mzs4BXywh
+0QwO3gXE8/aSBdoCrXNRfH7i5D0yc8VPTlyTm7X3BwbkVeuuOA7IF/sCEMGaRDWL
+uVf21Kd3DV5XjQhlFzvfdWyCO6mVE5rhvB68+uZn+t3CCe8uGZy1SzImKfkVne+I
+cwNZmsoiFCTVGLfZTSG1rwY9QeQ/cwCeODlKtfF9qe4NLE2bXujDW9A8P6Ge0TVl
+8nwYYNyuC9NgDyC+3J1Gpd73suPzsbW1pJJh+G2hfheYXBijqNgZtOkT+A/alrAd
+vCDugkNk3ge55OcbXKM0hTKGMV2/6WprJn5RhWO3KF05t03dL9mR0fF20lJ7rIi6
+sRbSpHHmALA02Da/nengaX1pqFk/lX1Sh5L149sz3Cs/b87jwviH7qeH2G8S8bhj
+2UZP9yLLZkBv14o6bmb3LcPYUxPBUmVvev1iwJTTEEutPMc+oR/FEJFG3gC8FMMQ
+1/B5AhcHc+pfA0ksrmlDpd5Oy+BvfzrImD50XODUh+q01w4F0nd2xgvmuW+kLsnm
+tx3QOzhkOx2D+aWDJbbYDPj0XU/gooChvzBgjfyqqLqOPKrEBfOWIKO4t5sx7AFH
+PEIn3xw8sZ0yXVCV/pqrTVf6ypNpypd5XcaqFu8yYRNmbyLwhuzW5K2lkE5cUtPN
+Y+7Elymrq29A1ZDr/FUf6PxGeTowzKhb3rQxwCRFy8Gc7bbBMXMIzcX2hY+TAhkP
+v2qSJRIWdCXvcKoiEsHo+bHCvoz28e1fqhK8LVJ6JECQZDbtwJAs+BKESmLlidHM
+rPVNNgY4GWZwptQZ4pp1ZtGAWJdBRYEpjt6bMB7TfUFpXFAfjIaB5gNCRBMJSupc
+6ukarRWxul0I2qdctN7THgAD3jDJYjjzeAjDI0wahWzQU+JLHBhWThxEa3LZR6qg
+WKw6TjHTVZbOwcpjCwxhvj2LMlmCG8e+gdiM9u0RSGl1UT/2bkyVmk/aOYAJtsnD
+I8aucuriaVEHKTP83fTeLlL2ifMDShsbs0ImcnOrwLB6pTvziV/4XUvBLHKFCAu7
+XusqVCJ2AW8pwzcm/XkFrUZ7Gl8rrGNNTRpYwfveIw8h+T3UM/a1tvDLwXGU3GpQ
+FD1X7wv8Gi6A1CIwboGEPu7nsDtslpuzdRndYGcdDjcvtXza2cRe6AgYEiCMplli
+AMpvxzgzr/0jwj9OuqnqVdR1CYOL1dEa/Xra7k1rKGoizcdAXBjoh8NR45KQd0ap
+mhRmK2shKjGAO6U9lep9kFeZBWcHAPse3RlEGP7gdQt7Is4BZkI/oIYQUfOKMD05
+QLbryJ8qFo1K9uZ1L/HjjJgXVChQRN+WBzELpIcIqPWPrIEm9dNG1kNUPbW45Oeg
+f+f2OtQs/eWPioFiCwbuU2w1ZP6nWRZY6KAApevUIIx1aF0DBU1KLPWKD5s9ajlj
+GsI9xAnN4QxG1yqFhVbNvZQ+pK+wg8WpTzU+cC/vD6ayHUam1w/hVumFstsyqG1F
+uyUfgFNrlNsww7ElAZopK70iyaZLFC11X0lZbH0N6SfoIE55FInvWrF4CwFDAhsu
+OOj1miAlnDHYRPC9w1+5GAltrcrJ7zjt2K3pUAkIMxWqRCD344X/7WrK1PXE36WB
+wPDCEGbZsZoK9UYiZ0TQCJZCrAb1jp3Vnx7EFvde7yxgppUw/K/Qe8W5v2AURhim
+RRbxRb30+enXKh5wktlwfvET6TzGd4HZmNTaE1sdpLpFn12R/DRdtMcNH83/MvNi
+w1CJwOnRg1Ln24hJz+nM0o7fpOjdIOrq5B/PqYQsg8uzCIzw10mgMydMN5Igjt52
+V8InZnFwDLGaMXpWr0uFdhNJ3kxDC12eOTNMmpPv5iCWzoMn06ySgEqwiNN0ffqn
+zTamgePT0oVH6/N6yBdxDhCkh4yrZkuYhVDyM8QJmhXLkVjRRvM2E1r8zTg4xrDs
+DoX0dQSR8WvcuG2/7bS4HEDsCw7PH6LN96N7edW7hDtevvq++SuvNSegM3scclgh
+tekry+kT9kMg8hD+3Z1vmtx2wy/alRjZuTNH4SIpF6PruqvzDqN9CoxGjaGDsamo
+Jv0WrGFpTZmvpvT76Rt+Ai8Ha5vqbquxu9TunPqyyzX2e3kCmnvx3rtycuzYNApH
+fWdcYK+BsS2zeCNPE5I63VH4Q/Gsr/p4kQlF2rCmIJWKgt4bjAIko1A1gwcYKDvz
+WxdkVHR15FgMoZiza/fIOUUgA15JwHWNG6+WuQ9lMTHo9a9NuQnv+PNWnnJ/ivLm
+8pn8zjcXz5Lr3pki8bILGNgzX5nXyqjgJ3MC3N3MzmkZjOt6a8vBZG7sAWLd0gqa
+DbIqI8mFMMzS1/QFJJOcL40IKw9fENRvKHZo0GWYARXVVTuyaBmycOZzcAttFjDC
+0hMS27PgsUHcF9vxdoqJESK3ZdA6T6rmfLl+KFcOKuyRsbXE4xeYNKaTOQVBnwBn
+/wA37tUZcdK0xyHey3uIkE1bWto+Bjqp4zCLQx7FRCkkN1yVG8NOozBdT1unNnNw
+HaM3ruPb1lV+zj/9kYqwImKE5p35gQiQsD0CVMvgzi8eEA8PEeEld96sX00cEr6m
+g/9qya5sek72PrLAb/JhMbmwQcVQX29YNzUsox9nwvzrq55T6YzqW31xU65Va4a0
+tvEizsMYjYIIlAt0WsDMWU7PZ/NZo/tYPZtUEAYUIh30NBm0kskpLNRayk3DC6b0
+Ll6CWXWSWibLwkLTUtSQDOSWJu3qlV0xPJzV++eDt4d2bhCF7udZx5U9sRsDd2z3
+AdyXzKhy3eB9oHk3VTzo5pALEw88Kvm+4v/cRtXCjL8HkVWLuc+5zGEzm1j+ZD07
+xr/vubQFq7isaY6KcuXK22uhdoUyRU85AiwDzzTeyeBE0wUM4urWHJ1VX0cvtIN0
+Pj+xAQZk+IDUUHX6/ZZI/RE9uLPDvJV+jTlbo7etPj7uTk35UN6aSqZvCycV1UaU
+uPGMPvE/MhE7rCBfMlitBY/9s2kgfS5mwIljD5HG7o7c03OqpLAe7ua4/6y6Iuvp
+/6RrjROHX/wWZ9hpPSovEFUM0KWSysEZEXJ9See3VB/VaYxTjquvRtP25/brHKlL
+tTAlBNJj0uDNWKYDvuGfUO+bldgpcUgOI0+BKK3KCb2D2FOX2gCeoms2+B5M8zii
+HOMNCvipQCJoJKUNFuisyvRkMlUzbrdbHXQFZK6KIMtxnzA+f/0AvfsFiLhMGh5A
+KWe7kjEZUGZnJDOMuutX/v+RSDzTGuuYwdxfPAlPDorcDSdSr8YeCaFS2vDss9ev
+jZBykWmHQgdfZzVqRT1R3R8+ONtzct/41dkgI3ZQV09kIS3+WiUeIrQ2VjqU/hTs
+Lgz/rHVJ2cp5yEfMVkuq0KVzjeFu13SQ4l5ABSy+hCeqgYThXi+oryGAcZ+NxbLc
+reL0oFJoDsQFqysb3zQtU9FlbbavrmfFgO2itJIk/BwNXagyMRUuYHlUdAPNWygd
+awVQf8FCFygkGRhcWV1jR3JqsjCLxsc+YekA/jqqCTtnC4Pgh96EfzjI7OuceNIY
+-----END RSA PRIVATE KEY-----
diff --git a/tests/trustedCA/testnetCA.pem b/tests/trustedCA/testnetCA.pem
new file mode 100755
index 0000000..81a4e87
--- /dev/null
+++ b/tests/trustedCA/testnetCA.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICVjCCAb+gAwIBAgIJAIwFS1tZBlNfMA0GCSqGSIb3DQEBBQUAMCcxEDAOBgNV
+BAoTB1RFU1RORVQxEzARBgNVBAMTClRFU1RORVQgQ0EwHhcNMDgxMTI1MTcxMzQ1
+WhcNMDgxMjI1MTcxMzQ1WjAnMRAwDgYDVQQKEwdURVNUTkVUMRMwEQYDVQQDEwpU
+RVNUTkVUIENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2Jn0QVBOoOtoH
+vr8+HZZZqmPZnlG0Rrb1pNUEzScMIm48hDChMEifTWtAr3NHnL9mzkL08BQKnrH5
+VYPkkh5NLFarxanE9sACuI3K6m2UvNK8X0ZPkLt4bQ6A6L7hHD63LSG5fvouGhAQ
+CVgxastn7/qs9RKIRp5GpZTj6um+8wIDAQABo4GJMIGGMB0GA1UdDgQWBBSiFul4
+NNvxHsJ0hDiv7A+FWnSsXTBXBgNVHSMEUDBOgBSiFul4NNvxHsJ0hDiv7A+FWnSs
+XaErpCkwJzEQMA4GA1UEChMHVEVTVE5FVDETMBEGA1UEAxMKVEVTVE5FVCBDQYIJ
+AIwFS1tZBlNfMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAK9AeX2Hv
+DBUWKAF7RI/an2GOOTECLgeDIgzGLjDWFo4CAhrvYSpiyblbAadSQclSWgQalpUH
+OQbAVqB4A8LmXvF/Niixu5QsmG6bCQlrT89khYOQ2GwEnrDpDCB+5t41gZNHmuMX
+r1urVCbUPDo71Hf/TjsPMh/QpY4pAI1BGTc=
+-----END CERTIFICATE-----
diff --git a/tests/trusted_CA.pem b/tests/trusted_CA.pem
new file mode 100755
index 0000000..fe05eed
--- /dev/null
+++ b/tests/trusted_CA.pem
@@ -0,0 +1,87 @@
+-----BEGIN CERTIFICATE-----
+MIICVjCCAb+gAwIBAgIJAIwFS1tZBlNfMA0GCSqGSIb3DQEBBQUAMCcxEDAOBgNV
+BAoTB1RFU1RORVQxEzARBgNVBAMTClRFU1RORVQgQ0EwHhcNMDgxMTI1MTcxMzQ1
+WhcNMDgxMjI1MTcxMzQ1WjAnMRAwDgYDVQQKEwdURVNUTkVUMRMwEQYDVQQDEwpU
+RVNUTkVUIENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2Jn0QVBOoOtoH
+vr8+HZZZqmPZnlG0Rrb1pNUEzScMIm48hDChMEifTWtAr3NHnL9mzkL08BQKnrH5
+VYPkkh5NLFarxanE9sACuI3K6m2UvNK8X0ZPkLt4bQ6A6L7hHD63LSG5fvouGhAQ
+CVgxastn7/qs9RKIRp5GpZTj6um+8wIDAQABo4GJMIGGMB0GA1UdDgQWBBSiFul4
+NNvxHsJ0hDiv7A+FWnSsXTBXBgNVHSMEUDBOgBSiFul4NNvxHsJ0hDiv7A+FWnSs
+XaErpCkwJzEQMA4GA1UEChMHVEVTVE5FVDETMBEGA1UEAxMKVEVTVE5FVCBDQYIJ
+AIwFS1tZBlNfMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAK9AeX2Hv
+DBUWKAF7RI/an2GOOTECLgeDIgzGLjDWFo4CAhrvYSpiyblbAadSQclSWgQalpUH
+OQbAVqB4A8LmXvF/Niixu5QsmG6bCQlrT89khYOQ2GwEnrDpDCB+5t41gZNHmuMX
+r1urVCbUPDo71Hf/TjsPMh/QpY4pAI1BGTc=
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIICiTCCAfKgAwIBAgIJAJACtwPaX6NqMA0GCSqGSIb3DQEBBQUAMDgxHTAbBgNV
+BAoTFFRlc3RpbmcgY2VydGlmaWNhdGVzMRcwFQYDVQQDEw5URVNUIFJPT1QgQ0VS
+VDAeFw0wOTA4MDEyMzI4NThaFw0xMDEyMjYyMzI4NThaMDgxHTAbBgNVBAoTFFRl
+c3RpbmcgY2VydGlmaWNhdGVzMRcwFQYDVQQDEw5URVNUIFJPT1QgQ0VSVDCBnzAN
+BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1YFmjCqUUt6qJkqJgAiZV1p+xdYKB75J
+iLTSwUVD21ebh2dwshYtVhYHLTrNZE3yLvsUrc/EVu4525Xz08XDyJnMtSSroeoY
+MbPMrdJ31JxOr3XOsq0O98WY8ZcZRJ6vZ4LcYZQAgkQXnHnIDFTQocf3VxXp69dq
+XNhY53axjkkCAwEAAaOBmjCBlzAdBgNVHQ4EFgQUz1SvyKy3WjTGqbpKa791Jz20
+GacwaAYDVR0jBGEwX4AUz1SvyKy3WjTGqbpKa791Jz20GaehPKQ6MDgxHTAbBgNV
+BAoTFFRlc3RpbmcgY2VydGlmaWNhdGVzMRcwFQYDVQQDEw5URVNUIFJPT1QgQ0VS
+VIIJAJACtwPaX6NqMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAwjjG
+qoE1mzNumupkMozPkZHeNUp9FL+8iKKyC6RTJ3hA5KXgOayiX9i82BeG+OSox1qs
+t1u6ygx253+m9+TZmaHqeJc1gtHOJKINN3Tu6rWYu6wCjT+f7exu0o9q/1xGB1Nr
+lBPHPKTQAQxbvuoRGNKfjEXlVHXwFd+BobKAqjw=
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+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
+qStgK39Nj5myPko=
+-----END CERTIFICATE-----
diff --git a/tests/tst_sizeof b/tests/tst_sizeof
new file mode 100755
index 0000000..c09bf42
--- /dev/null
+++ b/tests/tst_sizeof
Binary files differ
diff --git a/tests/tst_sizeof.c b/tests/tst_sizeof.c
new file mode 100755
index 0000000..46ce2cf
--- /dev/null
+++ b/tests/tst_sizeof.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+
+char str[]={'A',};
+int i[]={1,2,3};
+
+struct tsr
+{
+int len;
+char s[256];
+} t = {sizeof(str),"string"};
+
+int main()
+{
+printf("sizeof str: %u i:%u\n",sizeof("string"),sizeof(i));
+return 0;
+}