* Update to version 2.19.1
[alpine.git] / imap / src / osdep / os2 / tcp_os2.c
blobd5787be6f49eebc511f577fd24cf3fac768e9ea5
1 /* ========================================================================
2 * Copyright 2008-2010 Mark Crispin
3 * ========================================================================
4 */
6 /*
7 * Program: MS-DOS TCP/IP routines
9 * Author: Mark Crispin
11 * Date: 11 April 1989
12 * Last Edited: 3 April 2010
14 * Previous versions of this file were:
16 * Copyright 1988-2008 University of Washington
18 * Licensed under the Apache License, Version 2.0 (the "License");
19 * you may not use this file except in compliance with the License.
20 * You may obtain a copy of the License at
22 * http://www.apache.org/licenses/LICENSE-2.0
25 static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
26 static long ttmo_read = 0; /* TCP timeouts, in seconds */
27 static long ttmo_write = 0;
29 static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
30 long *contd);
32 /* TCP/IP manipulate parameters
33 * Accepts: function code
34 * function-dependent value
35 * Returns: function-dependent return value
38 void *tcp_parameters (long function,void *value)
40 void *ret = NIL;
41 switch ((int) function) {
42 case SET_TIMEOUT:
43 tmoh = (tcptimeout_t) value;
44 case GET_TIMEOUT:
45 ret = (void *) tmoh;
46 break;
47 case SET_READTIMEOUT:
48 ttmo_read = (long) value;
49 case GET_READTIMEOUT:
50 ret = (void *) ttmo_read;
51 break;
52 case SET_WRITETIMEOUT:
53 ttmo_write = (long) value;
54 case GET_WRITETIMEOUT:
55 ret = (void *) ttmo_write;
56 break;
58 return ret;
61 /* TCP/IP open
62 * Accepts: host name
63 * contact service name
64 * contact port number
65 * Returns: TCP/IP stream if success else NIL
68 TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
70 TCPSTREAM *stream = NIL;
71 struct sockaddr_in sin;
72 int sock;
73 char *s,tmp[MAILTMPLEN];
74 port &= 0xffff; /* erase flags */
75 /* The domain literal form is used (rather than simply the dotted decimal
76 as with other Unix programs) because it has to be a valid "host name"
77 in mailsystem terminology. */
78 sin.sin_family = AF_INET; /* family is always Internet */
79 /* look like domain literal? */
80 if (host[0] == '[' && host[(strlen (host))-1] == ']') {
81 strcpy (tmp,host+1); /* yes, copy number part */
82 tmp[strlen (tmp)-1] = '\0';
83 if ((sin.sin_addr.s_addr = inet_addr (tmp)) == -1) {
84 sprintf (tmp,"Bad format domain-literal: %.80s",host);
85 mm_log (tmp,ERROR);
86 return NIL;
89 /* look up host name */
90 else if (!lookuphost (&host,&sin)) {
91 sprintf (tmp,"Host not found: %s",host);
92 mm_log (tmp,ERROR);
93 return NIL;
96 /* copy port number in network format */
97 if (!(sin.sin_port = htons (port))) fatal ("Bad port argument to tcp_open");
98 /* get a TCP stream */
99 if ((sock = socket (sin.sin_family,SOCK_STREAM,0)) < 0) {
100 sprintf (tmp,"Unable to create TCP socket (%d)",errno);
101 mm_log (tmp,ERROR);
102 fs_give ((void **) &host);
103 return NIL;
105 #if 0
106 /* needed? */
107 else if (sock >= FD_SETSIZE) {/* unselectable sockets are useless */
108 sprintf (tmp,"Unable to create selectable TCP socket (%d >= %d)",
109 sock,FD_SETSIZE);
110 close (sock);
111 errno = ENOBUFS; /* just in case */
112 return NIL;
114 #endif
115 /* open connection */
116 if (connect (sock,(struct sockaddr *) &sin,sizeof (sin)) < 0) {
117 switch (errno) { /* analyze error */
118 case ECONNREFUSED:
119 s = "Refused";
120 break;
121 case ENOBUFS:
122 s = "Insufficient system resources";
123 break;
124 case ETIMEDOUT:
125 s = "Timed out";
126 break;
127 default:
128 s = "Unknown error";
129 break;
131 sprintf (tmp,"Can't connect to %.80s,%ld: %s (%d)",host,port,s,errno);
132 mm_log (tmp,ERROR);
133 close (sock);
134 fs_give ((void **) &host);
135 return NIL;
137 /* create TCP/IP stream */
138 stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
139 stream->host = host; /* official host name */
140 stream->localhost = cpystr (mylocalhost ());
141 stream->port = port; /* port number */
142 stream->tcps = sock; /* init socket */
143 stream->ictr = 0; /* init input counter */
144 return stream; /* return success */
147 /* TCP/IP authenticated open
148 * Accepts: NETMBX specifier
149 * service name
150 * returned user name buffer
151 * Returns: TCP/IP stream if success else NIL
154 TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
156 return NIL; /* always NIL on DOS */
159 /* TCP receive line
160 * Accepts: TCP stream
161 * Returns: text line string or NIL if failure
164 char *tcp_getline (TCPSTREAM *stream)
166 unsigned long n,contd;
167 char *ret = tcp_getline_work (stream,&n,&contd);
168 if (ret && contd) { /* got a line needing continuation? */
169 STRINGLIST *stl = mail_newstringlist ();
170 STRINGLIST *stc = stl;
171 do { /* collect additional lines */
172 stc->text.data = (unsigned char *) ret;
173 stc->text.size = n;
174 stc = stc->next = mail_newstringlist ();
175 ret = tcp_getline_work (stream,&n,&contd);
176 } while (ret && contd);
177 if (ret) { /* stash final part of line on list */
178 stc->text.data = (unsigned char *) ret;
179 stc->text.size = n;
180 /* determine how large a buffer we need */
181 for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
182 ret = fs_get (n + 1); /* copy parts into buffer */
183 for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
184 memcpy (ret + n,stc->text.data,stc->text.size);
185 ret[n] = '\0';
187 mail_free_stringlist (&stl);/* either way, done with list */
189 return ret;
192 /* TCP receive line or partial line
193 * Accepts: TCP stream
194 * pointer to return size
195 * pointer to return continuation flag
196 * Returns: text line string, size and continuation flag, or NIL if failure
199 static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
200 long *contd)
202 unsigned long n;
203 char *s,*ret,c,d;
204 *contd = NIL; /* assume no continuation */
205 /* make sure have data */
206 if (!tcp_getdata (stream)) return NIL;
207 for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
208 d = *stream->iptr++; /* slurp another character */
209 if ((c == '\015') && (d == '\012')) {
210 ret = (char *) fs_get (n--);
211 memcpy (ret,s,*size = n); /* copy into a free storage string */
212 ret[n] = '\0'; /* tie off string with null */
213 return ret;
216 /* copy partial string from buffer */
217 memcpy ((ret = (char *) fs_get (n)),s,*size = n);
218 /* get more data from the net */
219 if (!tcp_getdata (stream)) fs_give ((void **) &ret);
220 /* special case of newline broken by buffer */
221 else if ((c == '\015') && (*stream->iptr == '\012')) {
222 stream->iptr++; /* eat the line feed */
223 stream->ictr--;
224 ret[*size = --n] = '\0'; /* tie off string with null */
226 else *contd = LONGT; /* continuation needed */
227 return ret;
230 /* TCP/IP receive buffer
231 * Accepts: TCP/IP stream
232 * size in bytes
233 * buffer to read into
234 * Returns: T if success, NIL otherwise
237 long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
239 unsigned long n;
240 char *bufptr = buffer;
241 while (size > 0) { /* until request satisfied */
242 if (!tcp_getdata (stream)) return NIL;
243 n = min (size,stream->ictr);/* number of bytes to transfer */
244 /* do the copy */
245 memcpy (bufptr,stream->iptr,n);
246 bufptr += n; /* update pointer */
247 stream->iptr +=n;
248 size -= n; /* update # of bytes to do */
249 stream->ictr -=n;
251 bufptr[0] = '\0'; /* tie off string */
252 return T;
255 /* TCP/IP receive data
256 * Accepts: TCP/IP stream
257 * Returns: T if success, NIL otherwise
260 long tcp_getdata (TCPSTREAM *stream)
262 int i;
263 fd_set fds,efds;
264 struct timeval tmo;
265 time_t t = time (0);
266 if (stream->tcps < 0) return NIL;
267 while (stream->ictr < 1) { /* if nothing in the buffer */
268 time_t tl = time (0); /* start of request */
269 tmo.tv_sec = ttmo_read; /* read timeout */
270 tmo.tv_usec = 0;
271 FD_ZERO (&fds); /* initialize selection vector */
272 FD_ZERO (&efds); /* handle errors too */
273 FD_SET (stream->tcps,&fds);/* set bit in selection vector */
274 FD_SET(stream->tcps,&efds);/* set bit in error selection vector */
275 errno = NIL; /* block and read */
276 while (((i = select (stream->tcps+1,&fds,0,&efds,ttmo_read ? &tmo : 0))<0)
277 && (errno == EINTR));
278 if (!i) { /* timeout? */
279 time_t tc = time (0);
280 if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue;
281 else return tcp_abort (stream);
283 else if (i < 0) return tcp_abort (stream);
284 while (((i = read (stream->tcps,stream->ibuf,BUFLEN)) < 0) &&
285 (errno == EINTR));
286 if (i < 1) return tcp_abort (stream);
287 stream->iptr = stream->ibuf;/* point at TCP buffer */
288 stream->ictr = i; /* set new byte count */
290 return T;
293 /* TCP/IP send string as record
294 * Accepts: TCP/IP stream
295 * string pointer
296 * Returns: T if success else NIL
299 long tcp_soutr (TCPSTREAM *stream,char *string)
301 return tcp_sout (stream,string,(unsigned long) strlen (string));
305 /* TCP/IP send string
306 * Accepts: TCP/IP stream
307 * string pointer
308 * byte count
309 * Returns: T if success else NIL
312 long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
314 int i;
315 fd_set fds;
316 struct timeval tmo;
317 time_t t = time (0);
318 if (stream->tcps < 0) return NIL;
319 while (size > 0) { /* until request satisfied */
320 time_t tl = time (0); /* start of request */
321 tmo.tv_sec = ttmo_write; /* write timeout */
322 tmo.tv_usec = 0;
323 FD_ZERO (&fds); /* initialize selection vector */
324 FD_SET (stream->tcps,&fds);/* set bit in selection vector */
325 errno = NIL; /* block and write */
326 while (((i = select (stream->tcps+1,0,&fds,0,ttmo_write ? &tmo : 0)) < 0)
327 && (errno == EINTR));
328 if (!i) { /* timeout? */
329 time_t tc = time (0);
330 if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue;
331 else return tcp_abort (stream);
333 else if (i < 0) return tcp_abort (stream);
334 while (((i = write (stream->tcps,string,size)) < 0) && (errno == EINTR));
335 if (i < 0) return tcp_abort (stream);
336 size -= i; /* how much we sent */
337 string += i;
339 return T; /* all done */
342 /* TCP/IP close
343 * Accepts: TCP/IP stream
346 void tcp_close (TCPSTREAM *stream)
348 tcp_abort (stream); /* nuke the socket */
349 /* flush host names */
350 fs_give ((void **) &stream->host);
351 fs_give ((void **) &stream->localhost);
352 fs_give ((void **) &stream); /* flush the stream */
356 /* TCP/IP abort stream
357 * Accepts: TCP/IP stream
358 * Returns: NIL always
361 long tcp_abort (TCPSTREAM *stream)
363 if (stream->tcps >= 0) close (stream->tcps);
364 stream->tcps = -1;
365 return NIL;
368 /* TCP/IP get host name
369 * Accepts: TCP/IP stream
370 * Returns: host name for this stream
373 char *tcp_host (TCPSTREAM *stream)
375 return stream->host; /* return host name */
379 /* TCP/IP get remote host name
380 * Accepts: TCP/IP stream
381 * Returns: host name for this stream
384 char *tcp_remotehost (TCPSTREAM *stream)
386 return stream->host; /* all we can do for now */
390 /* TCP/IP return port for this stream
391 * Accepts: TCP/IP stream
392 * Returns: port number for this stream
395 unsigned long tcp_port (TCPSTREAM *stream)
397 return stream->port; /* return port number */
401 /* TCP/IP get local host name
402 * Accepts: TCP/IP stream
403 * Returns: local host name
406 char *tcp_localhost (TCPSTREAM *stream)
408 return stream->localhost; /* return local host name */
412 /* TCP/IP return canonical form of host name
413 * Accepts: host name
414 * Returns: canonical form of host name
417 char *tcp_canonical (char *name)
419 return cpystr (name);
423 /* TCP/IP get client host name (server calls only)
424 * Returns: client host name
427 char *tcp_clienthost ()
429 return "UNKNOWN";