blob: d82499450baa6a6e21e0c3f8503f9800970ce68b [file] [log] [blame]
/*
* messages.c -- the "messages on streams" module for websh3
* nca-073-9
*
* Copyright (c) 1996-2000 by Netcetera AG.
* Copyright (c) 2001 by Apache Software Foundation.
* All rights reserved.
*
* See the file "license.terms" for information on usage and
* redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* @(#) $Id$
*
*/
#include <tcl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef WIN32
# include <unistd.h>
# include <signal.h>
# include <sys/types.h>
# include <sys/uio.h>
# include <netinet/in.h>
# include <sys/time.h>
#endif
#include <errno.h>
#include "messages.h"
#include "log.h"
#include "macros.h"
/* ----------------------------------------------------------------------------
* Web_Send --
* ------------------------------------------------------------------------- */
int Web_Send(ClientData clientData,
Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[])
{
int flags = 0;
int mode = 0;
int cmdcode = 0;
int len = 0;
Tcl_Channel tc;
char *tmpStr = NULL;
/* ------------------------------------------------------------------------
* arg check
* --------------------------------------------------------------------- */
WebAssertObjc((objc < 4)
|| (objc > 5), 1, "channel cmdnr string ??#?flags?");
/* ------------------------------------------------------------------------
* flags there ?
* --------------------------------------------------------------------- */
flags = 0;
if (objc == 5) {
tmpStr = Tcl_GetString(objv[4]);
if (tmpStr[0] == '#') {
if (Tcl_GetInt(interp, &tmpStr[1], &flags) == TCL_ERROR)
return TCL_ERROR;
}
else {
if (parseFlags(interp, tmpStr, &flags) == TCL_ERROR)
return TCL_ERROR;
}
}
/* ------------------------------------------------------------------------
* see if we can get this channel
* --------------------------------------------------------------------- */
tc = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), &mode);
if (tc == NULL) {
LOG_MSG(interp, WRITE_LOG | SET_RESULT,
__FILE__, __LINE__, "web::send", WEBLOG_ERROR,
"unknown channel \"", Tcl_GetString(objv[1]), "\"", NULL);
return TCL_ERROR;
}
if ((mode & TCL_WRITABLE) == 0) {
LOG_MSG(interp, WRITE_LOG | SET_RESULT, __FILE__, __LINE__,
"web::send", WEBLOG_ERROR,
"channel \"", Tcl_GetString(objv[1]),
"\" not open for writing", NULL);
return TCL_ERROR;
}
/* ------------------------------------------------------------------------
* get commandcode
* --------------------------------------------------------------------- */
if (Tcl_GetIntFromObj(interp, objv[2], &cmdcode) == TCL_ERROR)
return TCL_ERROR;
tmpStr = Tcl_GetStringFromObj(objv[3],&len);
if ( send_msg(tc, cmdcode, flags, len, (void *)tmpStr) == -1 ) {
Tcl_PosixError(interp);
return TCL_ERROR;
}
return TCL_OK;
}
/* ----------------------------------------------------------------------------
* Web_Recv --
* ------------------------------------------------------------------------- */
int Web_Recv(ClientData clientData,
Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[])
{
int mode = 0;
void *data = NULL;
TCLCONST char *res = NULL;
int cmdcode = 0;
int flags = 0;
int size = 0;
Tcl_Channel tc;
Tcl_Obj *to = NULL;
/* ------------------------------------------------------------------------
* arg check
* --------------------------------------------------------------------- */
WebAssertObjc(objc != 5, 1, "channel cmdvarname resvarname flagsvarname");
/* ------------------------------------------------------------------------
* see if we can get this channel
* --------------------------------------------------------------------- */
tc = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), &mode);
if (tc == NULL) {
LOG_MSG(interp, WRITE_LOG | SET_RESULT,
__FILE__, __LINE__, "web::recv", WEBLOG_ERROR,
"unknown channel \"", Tcl_GetString(objv[1]), "\"", NULL);
return TCL_ERROR;
}
if ((mode & TCL_READABLE) == 0) {
LOG_MSG(interp, WRITE_LOG | SET_RESULT, __FILE__, __LINE__,
"web::recv", WEBLOG_ERROR,
"channel \"", Tcl_GetString(objv[1]),
"\" not open for reading", NULL);
return TCL_ERROR;
}
if (receive_msg(tc, &cmdcode, &flags, &size, &data) == -1) {
if (data)
Tcl_Free((char *) data);
Tcl_PosixError(interp);
return TCL_ERROR;
}
res = Tcl_SetVar(interp, Tcl_GetString(objv[3]), (char *) data, TCL_LEAVE_ERR_MSG);
Tcl_Free((char *) data);
if (res == NULL)
return TCL_ERROR;
to = Tcl_NewIntObj(cmdcode);
Tcl_IncrRefCount(to);
if (Tcl_ObjSetVar2(interp, objv[2], NULL, to, TCL_LEAVE_ERR_MSG) == NULL) {
Tcl_DecrRefCount(to);
return TCL_ERROR;
}
Tcl_DecrRefCount(to);
to = Tcl_NewIntObj(flags);
Tcl_IncrRefCount(to);
if (Tcl_ObjSetVar2(interp, objv[4], NULL, to, TCL_LEAVE_ERR_MSG) == NULL) {
Tcl_DecrRefCount(to);
return TCL_ERROR;
}
Tcl_DecrRefCount(to);
Tcl_SetObjResult(interp, Tcl_NewIntObj(size));
return TCL_OK;
}
/* ----------------------------------------------------------------------------
* Web_MsgFlag --
* ------------------------------------------------------------------------- */
int Web_MsgFlag(ClientData clientData,
Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[])
{
int flags;
int test;
char buf[30];
/* ------------------------------------------------------------------------
* arg check
* --------------------------------------------------------------------- */
WebAssertObjc((objc < 1) || (objc > 3), 1, "?flags? ?testflags?");
if (objc == 1) {
Tcl_SetResult(interp, "multiple", NULL);
return TCL_OK;
}
if (objc == 2) {
if (parseFlags(interp, Tcl_GetString(objv[1]), &flags) == TCL_ERROR)
return TCL_ERROR;
sprintf(buf, "%d", flags);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
return TCL_OK;
}
if (Tcl_GetIntFromObj(interp, objv[1], &flags) == TCL_ERROR)
return TCL_ERROR;
if (parseFlags(interp, Tcl_GetString(objv[2]), &test) == TCL_ERROR)
return TCL_ERROR;
if ((test & flags) == test && test != 0)
Tcl_SetResult(interp, "1", NULL);
else
Tcl_SetResult(interp, "0", NULL);
return TCL_OK;
}
/* ----------------------------------------------------------------------------
* parseFlags (from websh2)
* ------------------------------------------------------------------------- */
int parseFlags(Tcl_Interp * interp, char *flaglist, int *flags)
{
TCLCONST char **argv;
int argc;
int count;
*flags = 0;
Tcl_SplitList(NULL, flaglist, &argc, &argv);
for (count = 0; count < argc; count++) {
if (argv[count][0] == 'm')
*flags |= WMSG_FLAG_MULT;
else {
LOG_MSG(interp, WRITE_LOG | SET_RESULT, __FILE__, __LINE__,
"web::recv", WEBLOG_ERROR,
"unknown flag \"", argv[count], "\"", NULL);
Tcl_Free((char *) argv);
return TCL_ERROR;
}
}
Tcl_Free((char *) argv);
return TCL_OK;
}
/* ----------------------------------------------------------------------------
* send_msg
* ------------------------------------------------------------------------- */
int send_msg(Tcl_Channel f, int command, int flags, int size, void *data)
{
/* void *origsig; */
MsgHeader mh;
int ret;
/* remember SIGPIPE handler and ignore from now on: */
/* origsig=signal(SIGPIPE,SIG_IGN); */
/* fill in message header */
mh.magic = htonl(WMSG_MAGIC);
mh.version = htonl(WMSG_VERSION);
mh.command = htonl((u_long) ((command & 0xffff) | (flags & 0xffff0000)));
mh.size = htonl((u_long) size);
/* send header */
ret = Tcl_Write(f, (char *) &mh, sizeof(MsgHeader));
if (ret == -1) {
/*signal(SIGPIPE,origsig); */
#ifdef MSGDEBUG
printf("Error writing to socket\n");
#endif
return (-1);
}
if (ret != sizeof(MsgHeader)) {
/*signal(SIGPIPE,origsig); */
#ifdef MSGDEBUG
printf("Could only write %d instead of %d bytes\n", ret,
sizeof(MsgHeader));
#endif
errno = EIO;
return (-1);
}
/* send data */
ret = 0;
if (size != 0) {
ret = Tcl_Write(f, (char *) data, size * sizeof(char));
if (ret == -1) {
/* signal(SIGPIPE,origsig); */
#ifdef MSGDEBUG
printf("Error writing to socket\n");
#endif
return (-1);
}
if (ret != (int) (size * sizeof(char))) {
/*signal(SIGPIPE,origsig); */
#ifdef MSGDEBUG
printf("Could only write %d instead of %d bytes\n", ret,
sizeof(MsgHeader));
#endif
errno = EIO;
return (-1);
}
}
/*signal(SIGPIPE,origsig); */
if (!(flags & WMSG_FLAG_MULT)) {
Tcl_Flush(f);
}
return 0;
}
/* ----------------------------------------------------------------------------
* receive_msg
* ------------------------------------------------------------------------- */
int receive_msg(Tcl_Channel f, int *command, int *flags, int *size,
void **data)
{
/* fd_set rfs;
struct timeval timeout; */
int ret, maxsize;
u_long magic;
u_long version;
MsgHeader mh;
/* timeout.tv_usec=0;
timeout.tv_sec=WMSG_TIMEOUT; */
magic = 0;
if (*data == NULL) {
maxsize = 0;
}
else {
maxsize = *size;
}
/* read from socket until timeout or magic header word appears */
while (magic != WMSG_MAGIC) {
ret = Tcl_Read(f, (char *) &magic, sizeof(u_long));
magic = ntohl(magic);
if (ret == -1) {
#ifdef MSGDEBUG
printf("Error reading socket\n");
#endif
return (-1); /* some error */
}
if (ret != sizeof(u_long)) {
#ifdef MSGDEBUG
printf("incomplete read or client disconnected\n");
#endif
errno = EIO;
return (-1); /* incomplete read */
}
}
/* get rest of message header */
ret =
Tcl_Read(f, (char *) &mh.version, sizeof(MsgHeader) - sizeof(u_long));
if (ret == -1) {
#ifdef MSGDEBUG
printf("Error reading socket\n");
#endif
return (-1); /* some error */
}
if (ret != sizeof(MsgHeader) - sizeof(u_long)) {
#ifdef MSGDEBUG
printf("Incomplete header read: %d but expected %d\n", ret,
sizeof(MsgHeader) - sizeof(u_long));
#endif
errno = EIO;
return (-1); /* incomplete read */
}
version = (int) ntohl(mh.version);
if (version > WMSG_VERSION) {
#ifdef MSGDEBUG
printf("Got unknown version %d\n", version);
#endif
#ifdef EPROTONOSUPPORT
errno = EPROTONOSUPPORT;
#else
errno = EIO;
#endif
return (-1); /* unknown version */
}
*command = (int) ntohl(mh.command);
*flags = (*command & 0xffff0000);
*command &= 0xffff;
*size = (int) ntohl(mh.size); /* +1 = zero terminate block */
if (*data == NULL) { /*alloc a buffer */
*data = Tcl_Alloc(*size + 1);
if (*data == NULL) {
errno = ENOMEM;
return (-1); /* no more memory */
}
maxsize = *size + 1;
}
if ((*size + 1) > maxsize) { /* problem: incoming msg larger than buffer */
#ifdef MSGDEBUG
printf("Message larger than receive buffer, reallocating memory\n");
#endif
*data = Tcl_Realloc(*data, *size + 1);
if (*data == NULL) {
errno = ENOMEM;
return (-1); /* no more memory */
}
}
if ((*size) != 0) {
ret = Tcl_Read(f, (char *) *data, (*size));
if (ret == -1) {
#ifdef MSGDEBUG
printf("Error reading data section of message\n");
#endif
return (-1);
}
if (ret != *size) {
#ifdef MSGDEBUG
printf("Incomplete data read: expected %d got %d (%d)\n", *size, ret, Tcl_Eof(f));
#endif
errno = EIO;
return (-1); /* incomplete read */
}
}
/* zero terminate */
((char *) *data)[*size] = '\0';
return (0);
}