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