1 /*
2 * C++ Interface to Rserve
3 * Copyright (C) 2004-8 Simon Urbanek, All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation; version 2.1 of the License
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Leser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Although this code is licensed under LGPL v2.1, we strongly encourage
19 * everyone modifying this software to contribute back any improvements and
20 * bugfixes to the project for the benefit all other users. Thank you.
21 *
22 * $Id$
23 */
24
25 /* external defines:
26 SWAPEND - needs to be defined for platforms with inverse endianess related to Intel
27 see also SOCK_ERROR, MAIN and other defines in sisocks.h
28 */
29
30 /* locally generated status error and return codes:
31 -1 - operation failed (e.g. connect failed)
32 -2 - handhake failed
33 -3 - invalid ID string
34 -4 - protocol not supported
35 -5 - not connected
36 -6 - - unused -
37 -7 - remote connection close
38 -8 - malformed packet
39 -9 - send error
40 -10 - out of memory
41 -11 - operation is unsupported (e.g. unix login while crypt is not linked)
42 -12 - eval didn't return a SEXP (possibly the server is too old/buggy or crashed)
43 */
44 #if defined (__cplusplus) && !defined (_WIN32)
45
46 #include "Rconnection.h"
47
48 #include <stdio.h>
49 #include "sisocks.h"
50 #ifdef unix
51 #include <sys/un.h>
52 #include <unistd.h>
53 #else
54 #define AF_LOCAL -1
55 #endif
56 #if defined HAVE_NETINET_TCP_H && defined HAVE_NETINET_IN_H
57 #define CAN_TCP_NODELAY
58 #include <netinet/tcp.h>
59 #include <netinet/in.h>
60 #endif
61 #ifdef Win32
62 #define CAN_TCP_NODELAY
63 #endif
64
65 #include "Rsrv.h"
66
67 #ifndef AF_LOCAL
68 #define AF_LOCAL AF_UNIX
69 #endif
70
71 // NOTE: 0103 compatibility has not been established! use at your own risk!
72 static const char *myID = "Rsrv0103QAP1"; /* this client supports up to protocol version 0103 */
73
74 #define IS_LIST_TYPE_(TYPE) ((TYPE) == XT_LIST || (TYPE) == XT_LIST_NOTAG || (TYPE) == XT_LIST_TAG)
75 #define IS_SYMBOL_TYPE_(TYPE) ((TYPE) == XT_SYM || (TYPE) == XT_SYMNAME)
76
new_parsed_Rexp(unsigned int * d,Rmessage * msg)77 static Rexp *new_parsed_Rexp(unsigned int *d, Rmessage *msg) {
78 int type=ptoi(*d)&0x3f;
79 #ifdef DEBUG_CXX
80 printf("new_parsed_Rexp(%p, %p) type=%d\n", d, msg, type);
81 #endif
82 if (type==XT_ARRAY_INT || type==XT_INT)
83 return new Rinteger(d,msg);
84 if (type==XT_ARRAY_DOUBLE || type==XT_DOUBLE)
85 return new Rdouble(d,msg);
86 if (IS_LIST_TYPE_(type))
87 return new Rlist(d,msg);
88 if (type==XT_VECTOR)
89 return new Rvector(d,msg);
90 if (type==XT_STR)
91 return new Rstring(d,msg);
92 if (type==XT_SYM || type==XT_SYMNAME)
93 return new Rsymbol(d,msg);
94 if (type==XT_ARRAY_STR)
95 return new Rstrings(d,msg);
96 return new Rexp(d,msg);
97 }
98
new_parsed_Rexp_from_Msg(Rmessage * msg)99 static Rexp *new_parsed_Rexp_from_Msg(Rmessage *msg) {
100 int hl=1;
101 unsigned int *hp=msg->par[0];
102 Rsize_t plen=hp[0]>>8;
103 if ((hp[0]&DT_LARGE)>0) {
104 hl++;
105 plen|=((Rsize_t)hp[1])<<24;
106 }
107 return new_parsed_Rexp(hp+hl,msg);
108 }
109
Rmessage()110 Rmessage::Rmessage() {
111 complete=0;
112 data=0;
113 len=0;
114 }
115
Rmessage(int cmd)116 Rmessage::Rmessage(int cmd) {
117 memset(&head,0,sizeof(head));
118 head.cmd = cmd;
119 data=0;
120 len=0;
121 complete=1;
122 }
123
Rmessage(int cmd,const char * txt)124 Rmessage::Rmessage(int cmd, const char *txt) {
125 memset(&head,0,sizeof(head));
126 int tl=strlen(txt)+1;
127 if ((tl&3)>0)
128 tl=(tl+4)&0xffffc; // allign the text
129 len=tl+4; // message length is tl + 4 (short format only)
130 head.cmd=cmd;
131 head.len=len;
132 data=(char*)malloc(tl+16);
133 memset(data,0,tl+16);
134 *((int*)data)=itop(SET_PAR(DT_STRING,tl));
135 strcpy(data+4,txt);
136 complete=1;
137 }
138
Rmessage(int cmd,const void * buf,int dlen,int raw_data)139 Rmessage::Rmessage(int cmd, const void *buf, int dlen, int raw_data) {
140 memset(&head,0,sizeof(head));
141 len=(raw_data)?dlen:(dlen+4);
142 head.cmd=cmd;
143 head.len=len;
144 data=(char*)malloc(len);
145 memcpy(data, (raw_data)?buf:((char*)buf+4), dlen);
146 if (!raw_data)
147 *((int*)data)=itop(SET_PAR(DT_BYTESTREAM,dlen));
148 complete=1;
149 }
150
Rmessage(int cmd,int i)151 Rmessage::Rmessage(int cmd, int i) {
152 memset(&head,0,sizeof(head));
153 len=8; // DT_INT+len (4) + payload-1xINT (4)
154 head.cmd=cmd;
155 head.len=len;
156 data=(char*)malloc(8);
157 *((int*)data)=itop(SET_PAR(DT_INT,4));
158 ((int*)data)[1]=itop(i);
159 complete=1;
160 }
161
~Rmessage()162 Rmessage::~Rmessage() {
163 if(data) free(data);
164 complete=0;
165 }
166
read(int s)167 int Rmessage::read(int s) {
168 complete=0;
169 int n=recv(s,(char*)&head,sizeof(head),0);
170 if (n!=sizeof(head)) {
171 closesocket(s); s=-1;
172 return (n==0)?-7:-8;
173 }
174 Rsize_t i=len=head.len=ptoi(head.len);
175 head.cmd=ptoi(head.cmd);
176 head.msg_id=ptoi(head.msg_id);
177 head.res=ptoi(head.res);
178 if (i>0) {
179 data=(char*) malloc(i);
180 if (!data) {
181 closesocket(s); s=-1;
182 return -10; // out of memory
183 }
184 char *dp=data;
185 while(i>0 && (n=recv(s,(char*)dp,i,0))>0) {
186 dp+=n;
187 i-=n;
188 }
189 if (i>0) {
190 closesocket(s); s=-1;
191 return -8;
192 }
193 }
194 parse();
195 complete=1;
196 return 0;
197 }
198
parse()199 void Rmessage::parse() {
200 pars=0;
201 if (len<4) return;
202 char *c=data, *eop=c+len;
203 while (c<eop) {
204 int hs=4;
205 unsigned int *pp=(unsigned int*)c;
206 unsigned int p1=ptoi(pp[0]);
207
208 Rsize_t len=p1>>8;
209 if ((p1&DT_LARGE)>0) {
210 hs+=4;
211 unsigned int p2=ptoi(pp[1]);
212 len|=((Rsize_t)p2)<<24;
213 }
214 #ifdef DEBUG_CXX
215 printf(" par %d: %d length %d\n", pars, p1&0x3f, len);
216 #endif
217 par[pars++]=(unsigned int*)c;
218 c+=hs;
219 c+=len;
220 if (pars>15) break; // max 16 pars
221 }
222 }
223
send(int s)224 int Rmessage::send(int s) {
225 int failed=0;
226 head.cmd=itop(head.cmd);
227 head.len=itop(head.len);
228 head.msg_id=itop(head.msg_id);
229 head.res=itop(head.res);
230 if (::send(s,(char*)&head,sizeof(head),0)!=sizeof(head))
231 failed=-1;
232 if (!failed && len>0 && (Rsize_t)::send(s,data,len,0)!=len)
233 failed=-1;
234 head.cmd=ptoi(head.cmd);
235 head.len=ptoi(head.len);
236 head.msg_id=ptoi(head.msg_id);
237 head.res=ptoi(head.res);
238 return failed;
239 }
240
Rexp(Rmessage * msg)241 Rexp::Rexp(Rmessage *msg) {
242 #ifdef DEBUG_CXX
243 printf("new Rexp@%x\n", this);
244 #endif
245 master=0; rcount=0; attr=0; attribs=0;
246 this->msg=msg;
247 int hl=1;
248 unsigned int *hp=msg->par[0];
249 Rsize_t plen=hp[0]>>8;
250 if ((hp[0]&DT_LARGE)>0) {
251 hl++;
252 plen|=((Rsize_t)hp[1])<<24;
253 }
254 next=parse(hp+hl);
255 }
256
Rexp(unsigned int * pos,Rmessage * msg)257 Rexp::Rexp(unsigned int *pos, Rmessage *msg) {
258 #ifdef DEBUG_CXX
259 printf("new Rexp@%x\n", this);
260 #endif
261 attr=0; master=0; this->msg=msg; rcount=0; attribs=0;
262 next=parse(pos);
263 }
264
Rexp(int type,const char * data,int len,Rexp * attr)265 Rexp::Rexp(int type, const char *data, int len, Rexp *attr) {
266 this->attr=attr; master=this; rcount=0; attribs=0;
267 this->type=type;
268 this->msg=0;
269 if (len>0) {
270 #ifdef DEBUG_CXX
271 fprintf(stderr, "Rexp::Rexp %p: allocating %d bytes\n", this, len);
272 #endif
273 this->data=(char*) malloc(len);
274 memcpy(this->data, data, len);
275 this->len=len;
276 } else
277 this->len=0;
278 next=(char*)data+this->len;
279 }
280
~Rexp()281 Rexp::~Rexp() {
282 #ifdef DEBUG_CXX
283 printf("releasing Rexp@%p\n", this);
284 #endif
285 if (attr)
286 delete(attr);
287 attr=0;
288 if (master) {
289 if (master==this) {
290 free(data); len=0;
291 } else
292 master->rcount--;
293 master=0;
294 }
295 if (msg) {
296 if (rcount>0)
297 fprintf(stderr, "WARNING! Rexp master %lx delete requested, but %d object(s) are using our memory - refusing to free, leaking...\n", (long)this, rcount);
298 else
299 delete(msg);
300 }
301 msg=0;
302 }
303
set_master(Rexp * m)304 void Rexp::set_master(Rexp *m) {
305 if (master) master->rcount--;
306 master=m;
307 if (m) m->rcount++;
308 }
309
parse(unsigned int * pos)310 char *Rexp::parse(unsigned int *pos) { // plen is not used
311 this->pos=pos;
312 int hl=1;
313 unsigned int p1=ptoi(pos[0]);
314 len=p1>>8;
315 if ((p1&XT_LARGE)>0) {
316 hl++;
317 len|=((Rsize_t)(ptoi(pos[1])))<<24;
318 }
319 data=(char*)(pos+hl);
320 if (p1&XT_HAS_ATTR) {
321 attr=new_parsed_Rexp((unsigned int*)data, 0);
322 len-=attr->next-data;
323 data=attr->next;
324 if (master || msg)
325 attr->set_master(master?master:this);
326 }
327 type=p1&0x3f;
328 #ifdef DEBUG_CXX
329 printf("Rexp(type=%d, len=%d, attr=%p)\n", type, len, attr);
330 #endif
331 return data+len;
332 }
333
store(char * buf)334 void Rexp::store(char *buf) {
335 int hl=4;
336 unsigned int *i = (unsigned int*)buf;
337 i[0]=SET_PAR(type, len);
338 i[0]=itop(i[0]);
339 if (len>0x7fffff) {
340 buf[0]|=XT_LARGE;
341 i[1]=itop(len>>24);
342 hl+=4;
343 }
344 memcpy(buf+hl, data, len);
345 }
346
attribute(const char * name)347 Rexp *Rexp::attribute(const char *name) {
348 return (attr && IS_LIST_TYPE_(attr->type)) ? ((Rlist*)attr)->entryByTagName(name) : 0;
349 }
350
attributeNames()351 const char **Rexp::attributeNames() {
352 if (!attr || !IS_LIST_TYPE_(attr->type))
353 return 0;
354 if (attribs == 0) {
355 // let us cache attribute names
356 Rlist *l = (Rlist*) attr;
357 while (l && (IS_LIST_TYPE_(l->type))) {
358 if (l->tag && IS_SYMBOL_TYPE_(l->tag->type)) attribs++;
359 l = l->tail;
360 }
361 attrnames = (const char**) malloc(sizeof(char*)*(attribs+1));
362 l = (Rlist*) attr;
363 while (l && IS_LIST_TYPE_(l->type)) {
364 if (l->tag && IS_SYMBOL_TYPE_(l->tag->type))
365 attrnames[attribs++] = ((Rsymbol*)l->tag)->symbolName();
366 l = l->tail;
367 }
368 attrnames[attribs] = 0;
369 }
370 return attrnames;
371 }
372
fix_content()373 void Rinteger::fix_content() {
374 if (!data) return;
375 #ifdef SWAPEND
376 int *i = (int*) data;
377 int *j = (int*) (data+len);
378 while (i<j) { *i=ptoi(*i); i++; }
379 #endif
380 }
381
fix_content()382 void Rdouble::fix_content() {
383 if (!data) return;
384 #ifdef SWAPEND
385 double *i = (double*) data;
386 double *j = (double*) (data+len);
387 while (i<j) { *i=ptod(*i); i++; }
388 #endif
389 }
390
fix_content()391 void Rsymbol::fix_content() {
392 if (type == XT_SYM && *data==3) name=data+4; // normally the symbol should consist of a string SEXP specifying its name - no further content is defined as of now
393 if (type == XT_SYMNAME) name=data; // symname consists solely of the name
394 #ifdef DEBUG_CXX
395 printf("SYM %p \"%s\"\n", this, name);
396 #endif
397 }
398
~Rlist()399 Rlist::~Rlist() {
400 if (head) delete(head);
401 if (tail) delete(tail);
402 if (tag) delete(tag);
403 }
404
fix_content()405 void Rlist::fix_content() {
406 char *ptr = data;
407 char *eod = data+len;
408 #ifdef DEBUG_CXX
409 printf("Rlist::fix_content data=%p, type=%d\n", ptr, type);
410 #endif
411 if (type == XT_LIST) { /* old-style lists */
412 head=new_parsed_Rexp((unsigned int*)ptr,0);
413 if (head) {
414 ptr=head->next;
415 if (ptr<eod) {
416 tail=(Rlist*)new_parsed_Rexp((unsigned int*)ptr,0);
417 if (tail) {
418 ptr=tail->next;
419 if (ptr<eod)
420 tag=new_parsed_Rexp((unsigned int*)ptr,0);
421 if (tail->type!=XT_LIST) {
422 // if tail is not a list, then something is wrong - just delete it
423 delete(tail); tail=0;
424 }
425 }
426 }
427 }
428 } else if (type == XT_LIST_NOTAG) { /* new style list w/o tags */
429 Rlist *lt = this;
430 int n = 0;
431 while (ptr < eod) {
432 Rexp *h = new_parsed_Rexp((unsigned int*) ptr, 0);
433 if (!h) break;
434 if (n)
435 lt = lt->tail = new Rlist(type, h, 0, h->next, msg);
436 else
437 lt->head = h;
438 n++;
439 ptr = h->next;
440 }
441 } else if (type == XT_LIST_TAG) { /* new style list with tags */
442 Rlist *lt = this;
443 int n = 0;
444 while (ptr < eod) {
445 Rexp *h = new_parsed_Rexp((unsigned int*) ptr, 0);
446 #ifdef DEBUG_CXX
447 printf(" LIST_TAG: n=%d, ptr=%p, h=%p\n", n, ptr, h);
448 #endif
449 if (!h) break;
450 ptr = h->next;
451 Rexp *t = new_parsed_Rexp((unsigned int*) ptr, 0);
452 #ifdef DEBUG_CXX
453 printf(" tag=%p (ptr=%p)\n", t, ptr);
454 #endif
455 if (!t) break;
456 if (n)
457 lt = lt->tail = new Rlist(type, h, t, t->next, msg);
458 else {
459 lt->head = h;
460 lt->tag = t;
461 }
462 ptr = t->next;
463 n++;
464 }
465 next = ptr;
466 }
467 #ifdef DEBUG_CXX
468 printf(" end of list %p, ptr=%p\n", this, ptr);
469 #endif
470 }
471
~Rvector()472 Rvector::~Rvector() {
473 int i=0;
474 while(i<count) {
475 if (cont[i]) delete(cont[i]);
476 i++;
477 }
478 if (strs) free(strs);
479 free(cont);
480 }
481
strings()482 char **Rvector::strings() {
483 if (strs) return strs;
484 int i=0, sc=0;
485 while (i<count) {
486 if (cont[i] && cont[i]->type==XT_STR) sc++;
487 i++;
488 }
489 if (sc==0) return 0;
490 strs=(char**)malloc(sizeof(char*)*(sc+1));
491 i=0; sc=0;
492 while (i<count) {
493 if (cont[i] && cont[i]->type==XT_STR) strs[sc++]=((Rstring*)cont[i])->string();
494 i++;
495 }
496 strs[sc]=0;
497 return strs;
498 }
499
indexOf(Rexp * exp)500 int Rvector::indexOf(Rexp *exp) {
501 int i=0;
502 while (i<count) {
503 if (cont[i]==exp) return i;
504 i++;
505 }
506 return -1;
507 }
508
indexOfString(const char * str)509 int Rvector::indexOfString(const char *str) {
510 int i=0;
511 while (i<count) {
512 if (cont[i] && cont[i]->type==XT_STR && !strcmp(((Rstring*)cont[i])->string(),str)) return i;
513 i++;
514 }
515 return -1;
516 }
517
indexOfString(const char * str)518 int Rstrings::indexOfString(const char *str) {
519 unsigned int i = 0;
520 while (i < nel) {
521 if (cont[i] && !strcmp(cont[i], str)) return i;
522 i++;
523 }
524 return -1;
525 }
526
byName(const char * name)527 Rexp* Rvector::byName(const char *name) {
528 /* here we are not using IS_LIST_TYPE_() because XT_LIST_NOTAG is guaranteed to not match */
529 if (count < 1 || !attr || (attr->type!=XT_LIST && attr->type != XT_LIST_TAG)) return 0;
530 Rexp *e = ((Rlist*) attr)->head;
531 if (((Rlist*) attr)->tag)
532 e = ((Rlist*) attr)->entryByTagName("names");
533 if (!e || (e->type!=XT_VECTOR && e->type!=XT_ARRAY_STR && e->type!=XT_STR))
534 return 0;
535 if (e->type == XT_VECTOR) {
536 int pos = ((Rvector*)e)->indexOfString(name);
537 if (pos>-1 && pos<count) return cont[pos];
538 } else if (e->type == XT_ARRAY_STR) {
539 int pos = ((Rstrings*)e)->indexOfString(name);
540 if (pos>-1 && pos<count) return cont[pos];
541 } else {
542 if (!strcmp(((Rstring*)e)->string(),name))
543 return cont[0];
544 }
545 return 0;
546 }
547
fix_content()548 void Rvector::fix_content() {
549 char *ptr = data;
550 char *eod = data+len;
551 capacity=16;
552 cont=(Rexp**) malloc(sizeof(Rexp*)*capacity);
553 while (ptr<eod) {
554 if (count==capacity) {
555 capacity*=2;
556 cont=(Rexp**) realloc(cont, sizeof(Rexp*)*capacity);
557 }
558 cont[count]=new_parsed_Rexp((unsigned int*)ptr,0);
559 if (cont[count])
560 ptr=cont[count]->next;
561 else break;
562 count++;
563 }
564 }
565
Rconnection(const char * host,int port)566 Rconnection::Rconnection(const char *host, int port) {
567 if (!host) host = "127.0.0.1";
568 this->host = strdup(host);
569 this->port = port;
570 family = (port==-1) ? AF_LOCAL : AF_INET;
571 s = -1;
572 auth = 0;
573 salt[0] = '.'; salt[1] = '.';
574 session_key = 0;
575 }
576
Rconnection(Rsession * session)577 Rconnection::Rconnection(Rsession *session) {
578 const char *sHost = session->host();
579 if (!sHost) sHost="127.0.0.1";
580 this->host = strdup(sHost);
581 this->port = session->port();
582 family = AF_INET;
583 s = -1;
584 auth = 0;
585 salt[0]='.'; salt[1]='.';
586 session_key = (char*) malloc(32);
587 memcpy(session_key, session->key(), 32);
588 }
589
~Rconnection()590 Rconnection::~Rconnection() {
591 if (host) free(host);
592 host = 0;
593 if (s != -1) closesocket(s);
594 s = -1;
595 }
596
connect()597 int Rconnection::connect() {
598 #ifdef unix
599 struct sockaddr_un sau;
600 #endif
601 SAIN sai;
602 char IDstring[33];
603
604 if (family==AF_INET) {
605 memset(&sai,0,sizeof(sai));
606 build_sin(&sai,host,port);
607 } else {
608 #ifdef unix
609 memset(&sau,0,sizeof(sau));
610 sau.sun_family=AF_LOCAL;
611 strcpy(sau.sun_path,host); // FIXME: possible overflow!
612 #else
613 return -11; // unsupported
614 #endif
615 }
616
617 IDstring[32]=0;
618 int i;
619
620 s=socket(family,SOCK_STREAM,0);
621 if (family==AF_INET) {
622 #ifdef CAN_TCP_NODELAY
623 int opt=1;
624 setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const char*) &opt, sizeof(opt));
625 #endif
626 i=::connect(s,(SA*)&sai,sizeof(sai));
627 }
628 #ifdef unix
629 else
630 i=::connect(s,(SA*)&sau,sizeof(sau));
631 #endif
632 if (i==-1) {
633 closesocket(s); s=-1;
634 return -1; // connect failed
635 }
636
637 if (session_key) { // resume a session
638 int n = send(s, session_key, 32, 0);
639 if (n != 32) {
640 closesocket(s); s = -1;
641 return -2; // handshake failed (session key send error)
642 }
643 Rmessage *msg = new Rmessage();
644 int q = msg->read(s);
645 delete msg;
646 return q;
647 }
648
649 int n=recv(s,IDstring,32,0);
650 if (n!=32) {
651 closesocket(s); s=-1;
652 return -2; // handshake failed (no IDstring)
653 }
654 if (strncmp(IDstring,myID,4)) {
655 closesocket(s); s=-1;
656 return -3; // invalid IDstring
657 }
658 if (strncmp(IDstring+8,myID+8,4) || strncmp(IDstring+4,myID+4,4)>0) {
659 closesocket(s); s=-1;
660 return -4; // protocol not supported
661 }
662 {
663 int i=12;
664 while (i<32) {
665 if (!strncmp(IDstring+i, "ARuc", 4)) auth|=A_required|A_crypt;
666 if (!strncmp(IDstring+i, "ARpt", 4)) auth|=A_required|A_plain;
667 if (IDstring[i]=='K') {
668 salt[0]=IDstring[i+1];
669 salt[1]=IDstring[i+2];
670 }
671 i+=4;
672 }
673 }
674 return 0;
675 }
676
disconnect()677 int Rconnection::disconnect() {
678 if (s>-1) {
679 closesocket(s);
680 s=-1;
681 }
682 return 0;
683 }
684
685 /**--- low-level functions --*/
686
request(Rmessage * msg,int cmd,int len,void * par)687 int Rconnection::request(Rmessage *msg, int cmd, int len, void *par) {
688 struct phdr ph;
689
690 if (s==-1) return -5; // not connected
691 memset(&ph,0,sizeof(ph));
692 ph.len=itop(len);
693 ph.cmd=itop(cmd);
694 if (send(s,(char*)&ph,sizeof(ph),0)!=sizeof(ph)) {
695 closesocket(s); s=-1;
696 return -9;
697 }
698 if (len>0 && send(s,(char*)par,len,0)!=len) {
699 closesocket(s); s=-1;
700 return -9;
701 }
702 return msg->read(s);
703 }
704
request(Rmessage * targetMsg,Rmessage * contents)705 int Rconnection::request(Rmessage *targetMsg, Rmessage *contents) {
706 if (s==-1) return -5; // not connected
707 if (contents->send(s)) {
708 closesocket(s); s=-1;
709 return -9; // send error
710 }
711 return targetMsg->read(s);
712 }
713
714 /** --- high-level functions -- */
715
shutdown(const char * key)716 int Rconnection::shutdown(const char *key) {
717 Rmessage *msg = new Rmessage();
718 Rmessage *cm = key?new Rmessage(CMD_shutdown, key):new Rmessage(CMD_shutdown);
719 int res = request(msg, cm);
720 delete cm;
721 delete msg;
722 return res;
723 }
724
assign(const char * symbol,Rexp * exp)725 int Rconnection::assign(const char *symbol, Rexp *exp) {
726 Rmessage *msg=new Rmessage();
727 Rmessage *cm=new Rmessage(CMD_setSEXP);
728
729 int tl=strlen(symbol)+1;
730 if (tl&3) tl=(tl+4)&0xfffc;
731 Rsize_t xl=exp->storageSize();
732 Rsize_t hl=4+tl+4;
733 if (xl>0x7fffff) hl+=4;
734 cm->data=(char*) malloc(hl+xl);
735 cm->head.len=cm->len=hl+xl;
736 ((unsigned int*)cm->data)[0]=SET_PAR(DT_STRING, tl);
737 ((unsigned int*)cm->data)[0]=itop(((unsigned int*)cm->data)[0]);
738 strcpy(cm->data+4, symbol);
739 ((unsigned int*)(cm->data+4+tl))[0]=SET_PAR((Rsize_t) ((xl>0x7fffff)?(DT_SEXP|DT_LARGE):DT_SEXP), (Rsize_t) xl);
740 ((unsigned int*)(cm->data+4+tl))[0]=itop(((unsigned int*)(cm->data+4+tl))[0]);
741 if (xl>0x7fffff)
742 ((unsigned int*)(cm->data+4+tl))[1]=itop(xl>>24);
743 exp->store(cm->data+hl);
744
745 int res=request(msg,cm);
746 delete (cm);
747 if (res) {
748 delete(msg);
749 return res;
750 }
751 if (!res) res = CMD_STAT(msg->command());
752 delete(msg);
753 return res;
754 }
755
voidEval(const char * cmd)756 int Rconnection::voidEval(const char *cmd) {
757 int status=0;
758 eval(cmd, &status, 1);
759 return status;
760 }
761
eval(const char * cmd,int * status,int opt)762 Rexp *Rconnection::eval(const char *cmd, int *status, int opt) { /* opt = 1 -> void eval */
763 Rmessage *msg=new Rmessage();
764 Rmessage *cmdMessage=new Rmessage((opt&1)?CMD_voidEval:CMD_eval, cmd);
765 int res=request(msg,cmdMessage);
766 delete (cmdMessage);
767 if (opt&1 && !res) {
768 if (status) *status=0; // we should put response code here
769 delete(msg);
770 return 0;
771 }
772 if ((opt & 1) == 0 && !res && (msg->pars!=1 || (ptoi(msg->par[0][0])&0x3f)!=DT_SEXP)) {
773 delete(msg);
774 if (status) *status=-12; // returned object is not SEXP
775 return 0;
776 }
777 if (res) {
778 delete(msg);
779 if (status) *status=res;
780 return 0;
781 }
782 if (status) *status=0;
783 return new_parsed_Rexp_from_Msg(msg);
784 }
785
786 /** detached eval (aka detached void eval) initiates eval and detaches the session.
787 * @param cmd command to evaluate. If NULL equivalent to simple detach()
788 * @param status optional status to be reported (zero on success)
789 * @return object describintg he session.
790 * Note that the caller is responsible for freeing the object if not needed. */
detachedEval(const char * cmd,int * status)791 Rsession *Rconnection::detachedEval(const char *cmd, int *status) {
792 Rmessage *msg = new Rmessage();
793 Rmessage *cmdMessage = cmd ? new Rmessage(CMD_detachedVoidEval, cmd) : new Rmessage(CMD_detachSession);
794 int res = request(msg, cmdMessage);
795 delete cmdMessage;
796 if (res) {
797 if (status) *status = res;
798 delete msg;
799 return 0;
800 }
801 if (msg->pars != 2 ||
802 PAR_TYPE(ptoi(msg->par[0][0])) != DT_INT || PAR_LEN(ptoi(msg->par[0][0])) != sizeof(int) ||
803 PAR_TYPE(ptoi(msg->par[1][0])) != DT_BYTESTREAM || PAR_LEN(ptoi(msg->par[1][0])) != 32) { // invalid contents
804 if (status) *status = -12;
805 delete msg;
806 return 0;
807 }
808 Rsession *session = new Rsession(host, ptoi(msg->par[0][1]), (const char*) (msg->par[1] + 1));
809 delete msg;
810 if (status) *status=0;
811 return session;
812 }
813
detach(int * status)814 Rsession *Rconnection::detach(int *status) {
815 return detachedEval(0, status);
816 }
817
openFile(const char * fn)818 int Rconnection::openFile(const char *fn) {
819 Rmessage *msg=new Rmessage();
820 Rmessage *cmdMessage=new Rmessage(CMD_openFile, fn);
821 int res=request(msg,cmdMessage);
822 delete (cmdMessage);
823 if (!res) res=CMD_STAT(msg->command());
824 delete (msg);
825 return res;
826 }
827
createFile(const char * fn)828 int Rconnection::createFile(const char *fn) {
829 Rmessage *msg=new Rmessage();
830 Rmessage *cmdMessage=new Rmessage(CMD_createFile, fn);
831 int res=request(msg,cmdMessage);
832 delete (cmdMessage);
833 if (!res) res=CMD_STAT(msg->command());
834 delete (msg);
835 return res;
836 }
837
readFile(char * buf,unsigned int len)838 int Rconnection::readFile(char *buf, unsigned int len) {
839 Rmessage *msg=new Rmessage();
840 Rmessage *cmdMessage=new Rmessage(CMD_readFile, len);
841 int res=request(msg,cmdMessage);
842 delete(cmdMessage);
843 if (!res) {
844 // FIXME: Rserve up to 0.4-0 actually sends buggy response - it ommits DT_BYTESTREAM header!
845 if (msg->len > len) {
846 // we're in trouble here - techincally we should not get this
847 delete(msg);
848 return CERR_malformed_packet;
849 }
850 if (msg->len > 0) memcpy(buf, msg->data, msg->len);
851 int rl = msg->len;
852 delete(msg);
853 return rl;
854 }
855 delete(msg);
856 return CERR_io_error;
857 }
858
writeFile(const char * buf,unsigned int len)859 int Rconnection::writeFile(const char *buf, unsigned int len) {
860 Rmessage *msg=new Rmessage();
861 Rmessage *cmdMessage=new Rmessage(CMD_writeFile, buf, len);
862 int res=request(msg,cmdMessage);
863 delete(cmdMessage);
864 if (!res && msg->command()==RESP_OK) {
865 delete(msg);
866 return 0;
867 }
868 delete(msg);
869 // FIXME: this is not really true ...
870 return (res==0)?CERR_io_error:res;
871 }
872
closeFile()873 int Rconnection::closeFile() {
874 Rmessage *msg=new Rmessage();
875 Rmessage *cmdMessage=new Rmessage(CMD_closeFile);
876 int res=request(msg,cmdMessage);
877 delete(cmdMessage);
878 if (!res && msg->command()==RESP_OK) {
879 delete(msg);
880 return 0;
881 }
882 delete(msg);
883 // FIXME: this is not really true ...
884 return (res==0)?CERR_io_error:res;
885 }
886
removeFile(const char * fn)887 int Rconnection::removeFile(const char *fn) {
888 Rmessage *msg=new Rmessage();
889 Rmessage *cmdMessage=new Rmessage(CMD_removeFile, fn);
890 int res=request(msg,cmdMessage);
891 delete (cmdMessage);
892 if (!res) res=CMD_STAT(msg->command());
893 delete (msg);
894 return res;
895 }
896
login(const char * user,const char * pwd)897 int Rconnection::login(const char *user, const char *pwd) {
898 char *authbuf, *c;
899 if (!(auth&A_required)) return 0;
900 authbuf=(char*) malloc(strlen(user)+strlen(pwd)+22);
901 strcpy(authbuf, user); c=authbuf+strlen(user);
902 *c='\n'; c++;
903 strcpy(c,pwd);
904 // disabled for now, since NSS can't be statically linked
905 // #ifdef unix
906 // if (auth&A_crypt)
907 // strcpy(c,crypt(pwd,salt));
908 // #else
909 if (!(auth&A_plain)) {
910 free(authbuf);
911 return CERR_auth_unsupported;
912 }
913 // #endif
914
915 Rmessage *msg=new Rmessage();
916 Rmessage *cmdMessage=new Rmessage(CMD_login, authbuf);
917 int res=request(msg,cmdMessage);
918 delete (cmdMessage);
919 if (!res) res=CMD_STAT(msg->command());
920 delete (msg);
921 free(authbuf);
922 return res;
923 }
924
925 #ifdef CMD_ctrl
926
927 /* server control methods */
928 int serverEval(const char *cmd);
929 int serverSource(const char *fn);
930 int serverShutdown();
931
serverEval(const char * cmd)932 int Rconnection::serverEval(const char *cmd) {
933 Rmessage *msg = new Rmessage(); /* result message */
934 Rmessage *cmdMessage = new Rmessage(CMD_ctrlEval, cmd); /* request message */
935 int res = request(msg, cmdMessage);
936 delete (cmdMessage);
937 if (!res) res = CMD_STAT(msg->command());
938 delete (msg);
939 return res;
940 }
941
serverSource(const char * fn)942 int Rconnection::serverSource(const char *fn) {
943 Rmessage *msg = new Rmessage(); /* result message */
944 Rmessage *cmdMessage = new Rmessage(CMD_ctrlSource, fn); /* request message */
945 int res = request(msg, cmdMessage);
946 delete (cmdMessage);
947 if (!res) res = CMD_STAT(msg->command());
948 delete (msg);
949 return res;
950 }
951
serverShutdown()952 int Rconnection::serverShutdown() {
953 Rmessage *msg = new Rmessage(); /* result message */
954 Rmessage *cmdMessage = new Rmessage(CMD_ctrlShutdown); /* request message */
955 int res = request(msg, cmdMessage);
956 delete (cmdMessage);
957 if (!res) res = CMD_STAT(msg->command());
958 delete (msg);
959 return res;
960 }
961
962 #endif
963
964 #endif // __cplusplus, !_WIN32
965